diff options
Diffstat (limited to 'tests/auto/corelib/tools')
108 files changed, 10369 insertions, 3042 deletions
diff --git a/tests/auto/corelib/tools/CMakeLists.txt b/tests/auto/corelib/tools/CMakeLists.txt index 8fb36a956c..5cca2e2df6 100644 --- a/tests/auto/corelib/tools/CMakeLists.txt +++ b/tests/auto/corelib/tools/CMakeLists.txt @@ -1,6 +1,10 @@ -# Generated from tools.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause -add_subdirectory(collections) +add_subdirectory(qatomicscopedvaluerollback) +if(NOT INTEGRITY) + add_subdirectory(collections) +endif() add_subdirectory(containerapisymmetry) add_subdirectory(qalgorithms) add_subdirectory(qarraydata) @@ -13,16 +17,21 @@ add_subdirectory(qduplicatetracker) add_subdirectory(qeasingcurve) add_subdirectory(qexplicitlyshareddatapointer) add_subdirectory(qflatmap) -add_subdirectory(qfreelist) +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) -add_subdirectory(qoffsetstringarray) +if(NOT INTEGRITY) + add_subdirectory(qoffsetstringarray) +endif() add_subdirectory(qpair) add_subdirectory(qpoint) add_subdirectory(qpointf) @@ -33,17 +42,17 @@ add_subdirectory(qscopedpointer) add_subdirectory(qscopedvaluerollback) add_subdirectory(qscopeguard) add_subdirectory(qtaggedpointer) +add_subdirectory(qtyperevision) add_subdirectory(qset) -# add_subdirectory(qsharedpointer) # special case not ported +add_subdirectory(qsharedpointer) add_subdirectory(qsize) add_subdirectory(qsizef) +add_subdirectory(qspan) add_subdirectory(qstl) +add_subdirectory(quniquehandle) add_subdirectory(qvarlengtharray) add_subdirectory(qversionnumber) -# QTBUG-88137 # special case -if(NOT ANDROID) - add_subdirectory(qtimeline) -endif() +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 index 32ef8d7506..687d88b2e4 100644 --- a/tests/auto/corelib/tools/collections/CMakeLists.txt +++ b/tests/auto/corelib/tools/collections/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from collections.pro. +# 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 - DEFINES - # -QT_NO_JAVA_STYLE_ITERATORS # special case remove ) + +qt_internal_undefine_global_definition(tst_collections 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 34a352c614..2fab6cae3a 100644 --- a/tests/auto/corelib/tools/collections/tst_collections.cpp +++ b/tests/auto/corelib/tools/collections/tst_collections.cpp @@ -1,31 +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 +#undef QT_NO_FOREACH // this file tests Q_FOREACH over containers (centralize in a tst_qforeach?) // test the container forwards #include <QtContainerFwd> @@ -63,6 +39,9 @@ void foo() #include <QTest> #include <QVector> +#include <QScopedPointer> +#include <QThread> +#include <QSemaphore> #include <algorithm> @@ -123,6 +102,15 @@ 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 { @@ -160,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); } @@ -397,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")); @@ -552,7 +685,7 @@ QT_WARNING_POP 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")); } @@ -978,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); @@ -1710,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); @@ -1724,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')); @@ -1852,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())); @@ -1905,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(); @@ -2527,7 +2694,7 @@ void tst_Collections::vector_stl() QFETCH(QStringList, elements); QList<QString> vector; - for (int i = 0; i < elements.count(); ++i) + for (int i = 0; i < elements.size(); ++i) vector << elements.at(i); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) @@ -2562,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) @@ -2655,7 +2822,7 @@ void instantiateContainer() container.clear(); container.contains(value); - container.count(); + container.size(); container.empty(); container.isEmpty(); container.size(); @@ -2903,9 +3070,8 @@ class T2; void tst_Collections::forwardDeclared() { -#define COMMA , -#define TEST(type) do { \ - using C = type; \ +#define TEST(...) do { \ + using C = __VA_ARGS__; \ C *x = nullptr; \ C::iterator i; \ C::const_iterator j; \ @@ -2914,16 +3080,15 @@ void tst_Collections::forwardDeclared() Q_UNUSED(j); \ } while (false) - TEST(QHash<Key1 COMMA T1>); - TEST(QMap<Key1 COMMA T1>); - TEST(QMultiMap<Key1 COMMA T1>); + 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 -#undef COMMA { using C = QPair<T1, T2>; @@ -3080,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>(); } @@ -3105,7 +3270,7 @@ 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>(); } @@ -3172,7 +3337,7 @@ void tst_Collections::QTBUG13079_collectionInsideCollection() QSet<QTBUG13079_Node<QSet> > nodeSet; nodeSet << QTBUG13079_Node<QSet>(); nodeSet = nodeSet.begin()->children; - QCOMPARE(nodeSet.count(), 0); + QCOMPARE(nodeSet.size(), 0); } QTBUG13079_collectionInsideCollectionAssocImpl<QMap>(); @@ -3194,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; @@ -3231,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; @@ -3243,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 @@ -3266,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]))); } @@ -3287,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]))); } @@ -3300,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]))); } @@ -3326,8 +3494,8 @@ 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]))); } @@ -3337,8 +3505,8 @@ template<class Container> void insert_remove_loop_impl() 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.count()), sizeof(expect6)/sizeof(int)); - for (int i = 0; i < t.count(); i++) { + QCOMPARE(size_t(t.size()), sizeof(expect6)/sizeof(int)); + for (int i = 0; i < t.size(); i++) { QCOMPARE(t[i], T(IntOrString(expect6[i]))); } @@ -3371,7 +3539,63 @@ void tst_Collections::insert_remove_loop() 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 index 68d6f4bc0e..0ae1092043 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/CMakeLists.txt +++ b/tests/auto/corelib/tools/containerapisymmetry/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from containerapisymmetry.pro. +# 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 diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index 7eb4125553..5eb9dbfa36 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -1,53 +1,43 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 "qdebug.h" #include "qhash.h" +#include "qmap.h" +#include "qset.h" #include "qlist.h" #include "qstring.h" #include "qvarlengtharray.h" #include <algorithm> #include <functional> -#include <vector> // for reference +#include <iostream> #include <list> #include <set> +#include <sstream> #include <map> #include <forward_list> #include <unordered_set> #include <unordered_map> +#include <q20vector.h> // For reference -#if __cplusplus >= 202002L && defined(__cpp_lib_erase_if) -# define STDLIB_HAS_UNIFORM_ERASURE -#endif +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 { @@ -70,6 +60,11 @@ 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; @@ -106,9 +101,16 @@ 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; @@ -316,6 +318,49 @@ 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: @@ -337,31 +382,53 @@ private: 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() { -#ifdef STDLIB_HAS_UNIFORM_ERASURE - erase_impl<std::vector<int>>(); -#endif - } + 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() { -#ifdef STDLIB_HAS_UNIFORM_ERASURE - erase_if_impl<std::vector<int>>(); -#endif - } + 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() @@ -420,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); } @@ -650,7 +730,8 @@ Container make(int size) Container c; c.reserve(size); using V = typename Container::value_type; - std::generate_n(std::inserter(c, c.end()), size, [i = 1]() mutable { return V(i++); }); + int i = 0; + std::generate_n(std::inserter(c, c.end()), size, [&i] { return V(++i); }); return c; } @@ -676,20 +757,209 @@ 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 { @@ -708,6 +978,7 @@ void tst_ContainerApiSymmetry::erase_impl() const 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)); @@ -729,21 +1000,38 @@ void tst_ContainerApiSymmetry::erase_if_impl() const auto c = make<Container>(7); // {1, 2, 3, 4, 5, 6, 7} QCOMPARE(c.size(), S(7)); - auto result = erase_if(c, [](V i) { return Conv::toInt(i) % 2 == 0; }); + 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); - result = erase_if(c, [](V i) { return Conv::toInt(i) % 123 == 0; }); + 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); - result = erase_if(c, [](V i) { return Conv::toInt(i) % 3 == 0; }); + 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); - result = erase_if(c, [](V i) { return Conv::toInt(i) % 2 == 1; }); + 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> @@ -795,5 +1083,185 @@ void tst_ContainerApiSymmetry::erase_if_associative_impl() const 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) #include "tst_containerapisymmetry.moc" diff --git a/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt b/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt index d793c81ce6..9e87144a4c 100644 --- a/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt +++ b/tests/auto/corelib/tools/qalgorithms/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qalgorithms.pro. +# 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/tst_qalgorithms.cpp b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp index 0b921ffdbe..8d68a7a270 100644 --- a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp +++ b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 <QTest> @@ -95,6 +70,18 @@ private: void countLeading_impl(); }; +template <typename T> struct PrintIfFailed +{ + T value; + PrintIfFailed(T v) : value(v) {} + ~PrintIfFailed() + { + if (!QTest::currentTestFailed()) + return; + qWarning() << "Original value was" << Qt::hex << Qt::showbase << T(value); + } +}; + void tst_QAlgorithms::swap() { { @@ -274,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> @@ -300,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; @@ -326,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; } } } @@ -343,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); } @@ -353,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); } } } @@ -380,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 index 4a2f9d0d6a..1d84630de2 100644 --- a/tests/auto/corelib/tools/qarraydata/CMakeLists.txt +++ b/tests/auto/corelib/tools/qarraydata/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qarraydata.pro. +# 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 diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 747af3d422..b92cd4a887 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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> @@ -52,7 +28,7 @@ public: } explicit SimpleVector(size_t n, bool capacityReserved = false) - : d(Data::allocate(n)) + : d(n) { if (n) d->appendInitialize(n); @@ -61,7 +37,7 @@ public: } SimpleVector(size_t n, const T &t, bool capacityReserved = false) - : d(Data::allocate(n)) + : d(n) { if (n) d->copyAppend(n, t); @@ -70,7 +46,7 @@ public: } SimpleVector(const T *begin, const T *end, bool capacityReserved = false) - : d(Data::allocate(end - begin)) + : d(end - begin) { if (end - begin) d->copyAppend(begin, end); @@ -83,11 +59,6 @@ public: { } - explicit SimpleVector(QPair<Data*, T*> ptr, size_t len = 0) - : d(ptr, len) - { - } - SimpleVector(const QArrayDataPointer<T> &other) : d(other) { @@ -159,7 +130,7 @@ public: } } - SimpleVector detached(Data::allocate(qMax(n, size()))); + SimpleVector detached(DataPointer(qMax(n, size()))); if (size()) { detached.d->copyAppend(constBegin(), constEnd()); detached.d->setFlag(QArrayData::CapacityReserved); @@ -173,7 +144,7 @@ public: return; if (d->needsDetach() || newSize > capacity()) { - SimpleVector detached(Data::allocate(d->detachCapacity(newSize))); + SimpleVector detached(DataPointer(d->detachCapacity(newSize))); if (newSize) { if (newSize < size()) { const T *const begin = constBegin(); @@ -209,22 +180,7 @@ public: d->insert(0, first, last - first); } - void append(const_iterator first, const_iterator last) - { - if (first == last) - return; - - auto requiredSize = qsizetype(last - first); - if (d->needsDetach() || d.freeSpaceAtEnd() < requiredSize) { - DataPointer oldData; - d.reallocateAndGrow(QArrayData::GrowsAtEnd, requiredSize, &oldData); - - d->copyAppend(first, last); - 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) { @@ -262,7 +218,7 @@ public: const T *const end = begin + d->size; if (d->needsDetach()) { - SimpleVector detached(Data::allocate(d->detachCapacity(size() - (last - first)))); + SimpleVector detached(DataPointer(d->detachCapacity(size() - (last - first)))); if (first != begin) detached.d->copyAppend(begin, first); detached.d->copyAppend(last, end); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index e29527cb00..e7a84d57ee 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -1,31 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 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> @@ -37,6 +13,7 @@ #include <tuple> #include <algorithm> #include <vector> +#include <set> #include <stdexcept> #include <functional> #include <memory> @@ -84,6 +61,10 @@ private slots: 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; } @@ -92,7 +73,7 @@ void tst_QArrayData::referenceCounting() { { // Reference counting initialized to 1 (owned) - QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 }; + QArrayData array = { Q_BASIC_ATOMIC_INITIALIZER(1), {}, 0 }; QCOMPARE(array.ref_.loadRelaxed(), 1); @@ -126,8 +107,8 @@ void tst_QArrayData::simpleVector() SimpleVector<int> v4(nullptr, data, 0); SimpleVector<int> v5(nullptr, data, 1); SimpleVector<int> v6(nullptr, data, 7); - SimpleVector<int> v7(10, 5); - SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); + const SimpleVector<int> v7(10, 5); + const SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); v3 = v1; v1.swap(v3); @@ -255,7 +236,7 @@ void tst_QArrayData::simpleVector() { int count = 0; - Q_FOREACH (int value, v7) { + for (int value : v7) { QCOMPARE(value, 5); ++count; } @@ -265,7 +246,7 @@ void tst_QArrayData::simpleVector() { int count = 0; - Q_FOREACH (int value, v8) { + for (int value : v8) { QCOMPARE(value, count); ++count; } @@ -503,7 +484,7 @@ void tst_QArrayData::allocate() keeper.headers.append(data); if (grow) - QVERIFY(data->allocatedCapacity() > capacity); + QCOMPARE_GE(data->allocatedCapacity(), capacity); else QCOMPARE(data->allocatedCapacity(), capacity); @@ -1135,8 +1116,7 @@ void tst_QArrayData::arrayOpsExtra() const auto cloneArrayDataPointer = [] (auto &dataPointer, size_t capacity) { using ArrayPointer = std::decay_t<decltype(dataPointer)>; - using Type = std::decay_t<typename ArrayPointer::parameter_type>; - ArrayPointer copy(QTypedArrayData<Type>::allocate(qsizetype(capacity))); + ArrayPointer copy{qsizetype(capacity)}; copy->copyAppend(dataPointer.begin(), dataPointer.end()); return copy; }; @@ -1792,7 +1772,7 @@ 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)); } @@ -1815,7 +1795,7 @@ void tst_QArrayData::literals() { // wchar_t is not necessarily 2-bytes QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); - QCOMPARE(d.size, 10u + 1u); + QCOMPARE(d.size, 10 + 1); for (int i = 0; i < 10; ++i) QCOMPARE(d.data()[i], wchar_t('A' + i)); } @@ -1856,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); } @@ -1864,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)); } @@ -1872,7 +1852,7 @@ 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'); @@ -2056,7 +2036,7 @@ void tst_QArrayData::dataPointerAllocate() const auto createDataPointer = [] (qsizetype capacity, auto initValue) { using Type = std::decay_t<decltype(initValue)>; Q_UNUSED(initValue); - return QArrayDataPointer<Type>(QTypedArrayData<Type>::allocate(capacity)); + return QArrayDataPointer<Type>(capacity); }; const auto testRealloc = [&] (qsizetype capacity, qsizetype newSize, auto initValue) { @@ -2276,5 +2256,281 @@ void tst_QArrayData::selfEmplaceForward() 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 index 37e7a873e8..ac3bd24bd5 100644 --- a/tests/auto/corelib/tools/qbitarray/CMakeLists.txt +++ b/tests/auto/corelib/tools/qbitarray/CMakeLists.txt @@ -1,10 +1,19 @@ -# Generated from qbitarray.pro. +# 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/tst_qbitarray.cpp b/tests/auto/corelib/tools/qbitarray/tst_qbitarray.cpp index 263083972c..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$ -** -****************************************************************************/ +// 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(); @@ -91,6 +87,59 @@ private slots: 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 @@ -150,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; @@ -168,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); @@ -223,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() @@ -280,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() @@ -325,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() @@ -376,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() @@ -425,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() { @@ -477,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() @@ -497,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; @@ -519,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); @@ -534,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() @@ -587,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() @@ -596,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); } @@ -665,9 +922,9 @@ 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() diff --git a/tests/auto/corelib/tools/qcache/CMakeLists.txt b/tests/auto/corelib/tools/qcache/CMakeLists.txt index 5ed12a7973..8ffe942d70 100644 --- a/tests/auto/corelib/tools/qcache/CMakeLists.txt +++ b/tests/auto/corelib/tools/qcache/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qcache.pro. +# 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/tst_qcache.cpp b/tests/auto/corelib/tools/qcache/tst_qcache.cpp index f8b0aba2dc..5fccb8f1d0 100644 --- a/tests/auto/corelib/tools/qcache/tst_qcache.cpp +++ b/tests/auto/corelib/tools/qcache/tst_qcache.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 <QTest> @@ -37,6 +12,7 @@ public slots: void initTestCase(); void cleanupTestCase(); private slots: + void empty(); void maxCost(); void setMaxCost(); void totalCost(); @@ -50,6 +26,7 @@ private slots: void largeCache(); void internalChainOrderAfterEntryUpdate(); void emplaceLowerCost(); + void trimWithMovingAcrossSpans(); }; @@ -75,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); @@ -359,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 &); @@ -445,5 +438,71 @@ void tst_QCache::emplaceLowerCost() 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 index 128b8cfd73..5aa8bd2500 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/CMakeLists.txt +++ b/tests/auto/corelib/tools/qcommandlineparser/CMakeLists.txt @@ -1,11 +1,21 @@ -# Generated from qcommandlineparser.pro. +# 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/testhelper/CMakeLists.txt b/tests/auto/corelib/tools/qcommandlineparser/testhelper/CMakeLists.txt index 2068f988dd..20cec30a9c 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/testhelper/CMakeLists.txt +++ b/tests/auto/corelib/tools/qcommandlineparser/testhelper/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from qcommandlineparser_test_helper.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## qcommandlineparser_test_helper Binary: 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 dd4235ca40..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> diff --git a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp index aea3550452..812cf2d1b3 100644 --- a/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp +++ b/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp @@ -1,33 +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$ -** -****************************************************************************/ +// 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**) @@ -148,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 @@ -185,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); } @@ -383,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()); } @@ -470,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() @@ -509,6 +493,7 @@ void tst_QCommandLineParser::testSingleDashWordOptionModes() QFETCH(QStringList, commandLine); QFETCH(QStringList, expectedOptionNames); QFETCH(QStringList, expectedOptionValues); + QFETCH(QStringList, invalidOptionValues); commandLine.prepend("tst_QCommandLineParser"); @@ -525,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; @@ -546,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; @@ -576,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" @@ -623,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); @@ -671,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); @@ -697,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); @@ -748,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 @@ -772,10 +757,9 @@ void tst_QCommandLineParser::testVeryLongOptionNames() { #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; @@ -787,7 +771,7 @@ void tst_QCommandLineParser::testVeryLongOptionNames() output.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); #endif const QStringList lines = output.split('\n'); - const int last = lines.count() - 1; + 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"); diff --git a/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt b/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt index dc9722ccc7..5c32c34023 100644 --- a/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt +++ b/tests/auto/corelib/tools/qcontiguouscache/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qcontiguouscache.pro. +# 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/tst_qcontiguouscache.cpp b/tests/auto/corelib/tools/qcontiguouscache/tst_qcontiguouscache.cpp index b25ed55648..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,9 +78,9 @@ 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() @@ -137,7 +116,7 @@ void tst_QContiguousCache::append() 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 index a4b3106856..8a0c08fcad 100644 --- a/tests/auto/corelib/tools/qcryptographichash/CMakeLists.txt +++ b/tests/auto/corelib/tools/qcryptographichash/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qcryptographichash.pro. +# 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} @@ -16,20 +23,6 @@ qt_internal_add_test(tst_qcryptographichash TESTDATA ${test_data} ) -## Scopes: -##################################################################### - -if(ANDROID AND NOT ANDROID_EMBEDDED) - # Resources: - set(testdata_resource_files - "data/2c1517dad3678f03917f15849b052fd5.md5" - "data/d41d8cd98f00b204e9800998ecf8427e.md5" - ) - - qt_internal_add_resource(tst_qcryptographichash "testdata" - PREFIX - "/" - FILES - ${testdata_resource_files} - ) +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/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 ca4a6f7354..c08afd67c4 100644 --- a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp +++ b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp @@ -1,37 +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 <QTest> +#include <QScopeGuard> #include <QCryptographicHash> #include <QtCore/QMetaEnum> +#include <thread> + Q_DECLARE_METATYPE(QCryptographicHash::Algorithm) class tst_QCryptographicHash : public QObject @@ -45,11 +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() @@ -63,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() @@ -156,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() @@ -168,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(); } @@ -198,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")); } @@ -267,6 +279,68 @@ 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"); @@ -400,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 index a38255f3e9..13645c50b8 100644 --- a/tests/auto/corelib/tools/qduplicatetracker/CMakeLists.txt +++ b/tests/auto/corelib/tools/qduplicatetracker/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qduplicatetracker.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp b/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp index f4f038ca94..ad0b6abbc7 100644 --- a/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp +++ b/tests/auto/corelib/tools/qduplicatetracker/tst_qduplicatetracker.cpp @@ -1,36 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 <QtTest/QtTest> #include <QtCore/private/qduplicatetracker_p.h> #include <QObject> + +#include <string> #include <utility> class tst_QDuplicateTracker : public QObject @@ -38,6 +15,7 @@ class tst_QDuplicateTracker : public QObject Q_OBJECT private slots: void hasSeen(); + void clear(); void appendTo(); void appendTo_special(); }; @@ -74,6 +52,42 @@ void tst_QDuplicateTracker::hasSeen() 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() @@ -94,9 +108,15 @@ void tst_QDuplicateTracker::appendTo() QVERIFY(!tracker.hasSeen(2)); QList<int> c; - tracker.appendTo(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 @@ -168,17 +188,40 @@ size_t qHash(const ConstructionCounted &c, std::size_t seed = 0) void tst_QDuplicateTracker::appendTo_special() { - QDuplicateTracker<ConstructionCounted> tracker; - tracker.reserve(3); + QDuplicateTracker<ConstructionCounted> tracker(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); + 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)); + } } } diff --git a/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt b/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt index ce12cd9bfc..3f76f8a38f 100644 --- a/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt +++ b/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qeasingcurve.pro. +# 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/tst_qeasingcurve.cpp b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp index 09ab680b49..fc8c1a3e5c 100644 --- a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp +++ b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.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 <QTest> @@ -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); @@ -599,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(' ')); QList<QPointF> points; - foreach (const QString &str, pointStr) { + 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); @@ -628,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); @@ -667,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(); @@ -690,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 index 92850a8c4e..280918e302 100644 --- a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/CMakeLists.txt +++ b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qexplicitlyshareddatapointer.pro. +# 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/tst_qexplicitlyshareddatapointer.cpp b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp index 8ff8a7309f..5e105a090a 100644 --- a/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.cpp +++ b/tests/auto/corelib/tools/qexplicitlyshareddatapointer/tst_qexplicitlyshareddatapointer.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 <QTest> @@ -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 index 5d79b16776..bc98c669fc 100644 --- a/tests/auto/corelib/tools/qflatmap/CMakeLists.txt +++ b/tests/auto/corelib/tools/qflatmap/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qflatmap.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp index 674d9fa6c0..986cf2407b 100644 --- a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp +++ b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp @@ -1,30 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 + +#define QT_USE_QSTRINGBUILDER +#define QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT #include <QTest> @@ -38,6 +16,20 @@ #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 @@ -45,13 +37,25 @@ 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(); + 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() @@ -138,7 +142,7 @@ void tst_QFlatMap::insertion() QCOMPARE(m.value("foo").data(), "FOO"); QCOMPARE(m.value("bar").data(), "BAR"); QCOMPARE(m.value("baz").data(), "BAZ"); - QCOMPARE(m.value("oof").data(), "OOF"); + QCOMPARE(m.value("oof").data(), "eek"); QCOMPARE(m.value("bla").data(), "BLA"); QCOMPARE(m.value("blubb").data(), "BLUBB"); @@ -152,16 +156,52 @@ void tst_QFlatMap::insertion() 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(), "NARFFFFFF"); + 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(expectedKeys, expectedValues); + Map m(Qt::OrderedUniqueRange, expectedKeys, expectedValues); auto keys = m.keys(); auto values = m.values(); QCOMPARE(keys, expectedKeys); @@ -174,7 +214,7 @@ void tst_QFlatMap::extraction() void tst_QFlatMap::iterators() { using Map = QFlatMap<int, QByteArray>; - auto m = Map{ { 1, "foo" }, { 2, "bar" }, { 3, "baz" } }; + auto m = Map{ Qt::OrderedUniqueRange, { { 1, "foo" }, { 2, "bar" }, { 3, "baz" } } }; { // forward / backward Map::iterator a = m.begin(); @@ -318,6 +358,74 @@ void tst_QFlatMap::iterators() } } +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>; @@ -365,17 +473,35 @@ void tst_QFlatMap::statefulComparator() QVERIFY(m2.key_comp().count > m1.key_comp().count); } -void tst_QFlatMap::transparency() +void tst_QFlatMap::transparency_using() { struct StringViewCompare { - using is_transparent = void; - bool operator()(const QStringView &lhs, const QStringView &rhs) const + 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" } }; @@ -384,8 +510,163 @@ void tst_QFlatMap::transparency() 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() @@ -401,7 +682,7 @@ void tst_QFlatMap::viewIterators() }); auto it = keys.begin(); QCOMPARE(*it, "kaksi"); - QCOMPARE(it->length(), 5); + QCOMPARE(it->size(), 5); ++it; QCOMPARE(*it, "kolme"); it++; @@ -422,7 +703,7 @@ void tst_QFlatMap::viewIterators() }); auto it = values.begin(); QCOMPARE(*it, "twee"); - QCOMPARE(it->length(), 4); + QCOMPARE(it->size(), 4); ++it; QCOMPARE(*it, "dree"); it++; @@ -438,10 +719,9 @@ void tst_QFlatMap::viewIterators() void tst_QFlatMap::varLengthArray() { - using Map = QFlatMap<int, QByteArray, std::less<int>, - QVarLengthArray<int, 1024>, QVarLengthArray<QByteArray, 1024>>; - Map m{ { 2, "twee" } }; - m.insert(1, "een"); + 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); diff --git a/tests/auto/corelib/tools/qfreelist/CMakeLists.txt b/tests/auto/corelib/tools/qfreelist/CMakeLists.txt index cdac17e612..a37d3131f5 100644 --- a/tests/auto/corelib/tools/qfreelist/CMakeLists.txt +++ b/tests/auto/corelib/tools/qfreelist/CMakeLists.txt @@ -1,20 +1,19 @@ -# Generated from qfreelist.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) - -## Scopes: -##################################################################### - -qt_internal_extend_target(tst_qfreelist CONDITION NOT QT_FEATURE_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 5537c70c48..a45fa6d400 100644 --- a/tests/auto/corelib/tools/qfreelist/tst_qfreelist.cpp +++ b/tests/auto/corelib/tools/qfreelist/tst_qfreelist.cpp @@ -1,31 +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 <QtCore/QCoreApplication> #include <QtCore/QElapsedTimer> @@ -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 index b01782aed5..8702b8bf23 100644 --- a/tests/auto/corelib/tools/qhash/CMakeLists.txt +++ b/tests/auto/corelib/tools/qhash/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qhash.pro. +# 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 - DEFINES - #-QT_NO_JAVA_STYLE_ITERATORS # special case remove ) + +qt_internal_undefine_global_definition(tst_qhash 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 014617f8d4..b3dbdfa40c 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -1,48 +1,32 @@ -/**************************************************************************** -** -** 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 <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 { Q_OBJECT private slots: void insert1(); void erase(); + void erase_edge_case(); void key(); + void keys(); void swap(); void count(); // copied from tst_QMap @@ -54,17 +38,35 @@ 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 const_shared_null(); @@ -73,6 +75,7 @@ private slots: void eraseValidIteratorOnSharedHash(); void equal_range(); void insert_hash(); + void multiHashStoresInReverseInsertionOrder(); void emplace(); @@ -80,6 +83,25 @@ private slots: 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 { @@ -95,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]; }; @@ -161,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 @@ -177,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") ); @@ -189,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") ); @@ -197,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 @@ -272,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 ); { @@ -482,6 +506,7 @@ QT_WARNING_POP { 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); @@ -526,6 +551,66 @@ void tst_QHash::erase() 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() @@ -536,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")); @@ -566,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); @@ -597,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() @@ -626,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() @@ -633,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"); @@ -659,6 +828,16 @@ void tst_QHash::find() QCOMPARE(map1.find(1).value(), QLatin1String("Mayer")); QCOMPARE(map1.find(2).value(), QLatin1String("Hej")); + 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); @@ -685,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"); @@ -693,6 +873,10 @@ void tst_QHash::constFind() QCOMPARE(map1.constFind(1).value(), QLatin1String("Mayer")); QCOMPARE(map1.constFind(2).value(), QLatin1String("Hej")); + 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); @@ -716,6 +900,9 @@ 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)); @@ -734,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(); } }; @@ -794,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"); + + QCOMPARE(map.take(3), QLatin1String("drei")); + QVERIFY(!map.contains(3)); + } + { + QMultiHash<int, QString> hash; + QCOMPARE(hash.take(1), QString()); + QVERIFY(!hash.isDetached()); - map.insert(2, "zwei"); - map.insert(3, "drei"); + hash.insert(1, "value1"); + hash.insert(2, "value2"); + hash.insert(1, "value3"); - QCOMPARE(map.take(3), QLatin1String("drei")); - QVERIFY(!map.contains(3)); + // 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 @@ -947,6 +1169,222 @@ 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; @@ -1099,14 +1537,144 @@ void tst_QHash::iterators() } } +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()); @@ -1121,20 +1689,54 @@ void tst_QHash::keyIterator() QCOMPARE(*key_it, it.key()); QCOMPARE(*(key_it++), (it++).key()); if (key_it != hash.keyEnd()) { - QVERIFY(it != hash.end()); + QVERIFY(it != hash.cend()); ++key_it; ++it; if (key_it != hash.keyEnd()) QCOMPARE(*key_it, it.key()); else - QVERIFY(it == hash.end()); + QVERIFY(it == hash.cend()); } QCOMPARE(std::count(hash.keyBegin(), hash.keyEnd(), 99), 1); // DefaultConstructible test - typedef QHash<int, int>::key_iterator keyIterator; - 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() @@ -1179,7 +1781,7 @@ void tst_QHash::keyValueIterator() ++it; ++key_value_it; - if (it != hash.end()) + if (it != hash.cend()) QCOMPARE(*key_value_it, entry_type(it.key(), it.value())); else QVERIFY(key_value_it == hash.constKeyValueEnd()); @@ -1189,13 +1791,95 @@ void tst_QHash::keyValueIterator() 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) { - QMultiHash<int, int> testHash; + QHash<int, int> testHash; for (int i = 0; i < 500000; ++i) - testHash.insert(1, 1); + testHash.insert(i, 1); } } @@ -1227,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)); @@ -1244,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); @@ -1308,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); @@ -1322,15 +2020,16 @@ 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.count(), 6); + QCOMPARE(map2.size(), 6); QCOMPARE(map2[-1], -1); } } @@ -1446,12 +2145,238 @@ void tst_QHash::qmultihash_qhash_rvalue_ref_unite() } } -template <typename T> -QList<T> sorted(const QList<T> &list) +void tst_QHash::qmultihashUnite() { - QList<T> res = list; - std::sort(res.begin(), res.end()); - return res; + // 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() @@ -1460,6 +2385,7 @@ void tst_QHash::keys_values_uniqueKeys() QVERIFY(hash.uniqueKeys().isEmpty()); QVERIFY(hash.keys().isEmpty()); QVERIFY(hash.values().isEmpty()); + QVERIFY(!hash.isDetached()); hash.insert("alpha", 1); QVERIFY(sorted(hash.keys()) == (QList<QString>() << "alpha")); @@ -1587,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")); @@ -1597,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()); @@ -1771,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); } @@ -1783,7 +2709,7 @@ void tst_QHash::insert_hash() hash.insert(hash2); - QCOMPARE(hash.count(), 1); + QCOMPARE(hash.size(), 1); QCOMPARE(hash[0], 5); } { @@ -1793,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); } @@ -1806,13 +2732,31 @@ 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() { { @@ -1976,5 +2920,322 @@ void tst_QHash::stdHash() QVERIFY(!strings.contains("z")); } +void tst_QHash::countInEmptyHash() +{ + { + QHash<int, int> hash; + QCOMPARE(hash.size(), 0); + QCOMPARE(hash.count(42), 0); + } + + { + 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(other.size(), size); + for (int i = 0; i < (size / 2); ++i) + QCOMPARE(other.values(i), QList<int>({ i * 10 + 1, i * 10 })); + } +} + QTEST_APPLESS_MAIN(tst_QHash) #include "tst_qhash.moc" diff --git a/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt b/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt index 86d4207d6e..6cbba503dc 100644 --- a/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt +++ b/tests/auto/corelib/tools/qhashfunctions/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qhashfunctions.pro. +# 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/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index d313fbb0b0..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$ -** -****************************************************************************/ +// 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,18 +18,28 @@ 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(); @@ -63,29 +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); - QCOMPARE(qHash(s), qHash(QStringView(s))); + 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::initTestCase() +void tst_QHashFunctions::init() { - static_assert(int(RandomSeed) > 0); + QFETCH_GLOBAL(quint64, seedValue); + seed = size_t(seedValue); +} - QTest::addColumn<uint>("seedValue"); - QTest::newRow("zero-seed") << 0U; - QTest::newRow("non-zero-seed") << uint(RandomSeed); +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::init() +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::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() +{ + QTest::addColumn<double>("value"); + QTest::addRow("zero") << 0.0; + + 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::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() @@ -188,9 +436,7 @@ void tst_QHashFunctions::qhash_of_zero_floating_points() { QCOMPARE(qHash(-0.0f, seed), qHash(0.0f, seed)); QCOMPARE(qHash(-0.0 , seed), qHash(0.0 , seed)); -#ifndef Q_OS_DARWIN QCOMPARE(qHash(-0.0L, seed), qHash(0.0L, seed)); -#endif } void tst_QHashFunctions::qthash_data() @@ -216,8 +462,14 @@ namespace SomeNamespace { struct Hashable { int i; }; 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}; @@ -239,10 +491,16 @@ void tst_QHashFunctions::range() 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() @@ -265,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")); @@ -310,8 +600,41 @@ void tst_QHashFunctions::stdHash() } +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); @@ -324,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 index 49253ff06c..17a3a1bcef 100644 --- a/tests/auto/corelib/tools/qline/CMakeLists.txt +++ b/tests/auto/corelib/tools/qline/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qline.pro. +# 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 @@ -13,6 +20,6 @@ qt_internal_add_test(tst_qline ##################################################################### qt_internal_extend_target(tst_qline CONDITION UNIX AND NOT APPLE AND NOT HAIKU AND NOT INTEGRITY AND NOT VXWORKS - PUBLIC_LIBRARIES + LIBRARIES m ) diff --git a/tests/auto/corelib/tools/qline/tst_qline.cpp b/tests/auto/corelib/tools/qline/tst_qline.cpp index ad8438dfe9..51f1f8ac79 100644 --- a/tests/auto/corelib/tools/qline/tst_qline.cpp +++ b/tests/auto/corelib/tools/qline/tst_qline.cpp @@ -1,35 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <qline.h> #include <qmath.h> +#include <array> + class tst_QLine : public QObject { Q_OBJECT @@ -58,6 +35,9 @@ private slots: void testAngleTo_data(); void testSet(); + + void toLineF_data(); + void toLineF(); }; const qreal epsilon = sizeof(qreal) == sizeof(double) ? 1e-8 : 1e-4; @@ -269,6 +249,13 @@ void tst_QLine::testLength() QCOMPARE(l.length(), qreal(length)); l.setLength(lengthToSet); + + 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); @@ -495,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/qlist/CMakeLists.txt b/tests/auto/corelib/tools/qlist/CMakeLists.txt index 89b92ab305..fdcfcd7424 100644 --- a/tests/auto/corelib/tools/qlist/CMakeLists.txt +++ b/tests/auto/corelib/tools/qlist/CMakeLists.txt @@ -1,12 +1,21 @@ -# Generated from qlist.pro. +# 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 index 80adb0f6a1..35d69e8433 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -1,38 +1,33 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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) 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 <QScopedValueRollback> +#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) @@ -139,6 +134,7 @@ struct Custom { i = 0; counter.fetchAndAddRelaxed(-1); state = Destructed; + QVERIFY(heapData.use_count() > 0); // otherwise it's double free } bool operator ==(const Custom &other) const @@ -167,6 +163,9 @@ struct Custom { 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; @@ -192,81 +191,105 @@ 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; - 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 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; - void capacityMovable() const; - void capacityCustom() const; - void clearInt() const; - void clearMovable() const; - void clearCustom() 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; - void countMovable() const; - void countCustom() 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; - void emptyMovable() const; - void emptyCustom() const; + void emptyInt() const { empty<int>(); } + void emptyMovable() const { empty<Movable>(); } + void emptyCustom() const { empty<Custom>(); } 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 fillDetachInt() const; - void fillDetachMovable() const; - void fillDetachCustom() 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; - void fromListMovable() const; - void fromListCustom() const; + void fromListInt() const { fromList<int>(); } + void fromListMovable() const { fromList<Movable>(); } + void fromListCustom() const { fromList<Custom>(); } void indexOf() const; - void insertInt() const; - void insertMovable() const; - void insertCustom() 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; @@ -274,19 +297,20 @@ private slots: void lastIndexOf() const; void mid() const; void sliced() const; - void moveInt() const; - void moveMovable() const; - void moveCustom() const; - void prependInt() const; - void prependMovable() const; - void prependCustom() 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; - void removeMovable() const; - void removeCustom() 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; @@ -298,63 +322,94 @@ private slots: void resizeToZero() const; void resizeToTheSameSize_data(); void resizeToTheSameSize() const; + void resizeForOverwrite() const; + void iterators() const; + void constIterators() const; void reverseIterators() const; - void sizeInt() const; - void sizeMovable() const; - void sizeCustom() const; + void sizeInt() const { size<int>(); } + void sizeMovable() const { size<Movable>(); } + void sizeCustom() const { size<Custom>(); } void startsWith() const; - void swapInt() const; - void swapMovable() const; - void swapCustom() 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(); - void initializeListMovable(); - void initializeListCustom(); - + void initializeListInt() { initializeList<int>(); } + void initializeListMovable() { initializeList<Movable>(); } + void initializeListCustom() { initializeList<Custom>(); } void const_shared_null(); - - void detachInt() const; - void detachMovable() const; - void detachCustom() const; + 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(); - void emplaceCustom(); - void emplaceMovable(); - void emplaceConsistentWithStdVectorInt(); - void emplaceConsistentWithStdVectorCustom(); - void emplaceConsistentWithStdVectorMovable(); - void emplaceConsistentWithStdVectorQString(); + 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 qtbug_90359() 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; @@ -379,6 +434,56 @@ private: 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); }); + } }; @@ -416,6 +521,18 @@ const Custom SimpleValue<Custom>::Values[] = { 110, 105, 101, 114, 111, 98 }; #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; @@ -445,30 +562,29 @@ void tst_QList::constructors_reserveAndInitialize() const { // default-initialise items - QList<int> myInt(5, 42); + const QList<int> myInt(5, 42); QVERIFY(myInt.capacity() == 5); - foreach (int meaningoflife, myInt) { + for (int meaningoflife : myInt) QCOMPARE(meaningoflife, 42); - } - QList<QString> myString(5, QString::fromLatin1("c++")); + const QList<QString> myString(5, QString::fromLatin1("c++")); QVERIFY(myString.capacity() == 5); // make sure all items are initialised ok - foreach (QString meaningoflife, myString) { + for (const QString &meaningoflife : myString) QCOMPARE(meaningoflife, QString::fromLatin1("c++")); - } - QList<Custom> myCustom(5, Custom('n')); + const QList<Custom> myCustom(5, Custom('n')); QVERIFY(myCustom.capacity() == 5); // make sure all items are initialised ok - foreach (Custom meaningoflife, myCustom) { + 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)); @@ -486,28 +602,11 @@ void tst_QList::copyConstructor() const } } -void tst_QList::copyConstructorInt() const -{ - copyConstructor<int>(); -} - -void tst_QList::copyConstructorMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - copyConstructor<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::copyConstructorCustom() const +template<typename T> +void tst_QList::testAssignment() const { - const int instancesCount = Custom::counter.loadAcquire(); - copyConstructor<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} + TST_QLIST_CHECK_LEAKS(T) -template <class T> -static inline void testAssignment() -{ QList<T> v1(5); QCOMPARE(v1.size(), 5); QVERIFY(v1.isDetached()); @@ -536,24 +635,11 @@ static inline void testAssignment() QCOMPARE((void *)v2.constData(), data2); } -void tst_QList::assignmentInt() const -{ - testAssignment<int>(); -} - -void tst_QList::assignmentMovable() const -{ - testAssignment<Movable>(); -} - -void tst_QList::assignmentCustom() const -{ - testAssignment<Custom>(); -} - 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)); @@ -566,28 +652,11 @@ void tst_QList::assignFromInitializerList() const QCOMPARE(v1.size(), 0); } -void tst_QList::assignFromInitializerListInt() const -{ - assignFromInitializerList<int>(); -} - -void tst_QList::assignFromInitializerListMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - assignFromInitializerList<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::assignFromInitializerListCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - assignFromInitializerList<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - template<typename T> void tst_QList::add() const { + TST_QLIST_CHECK_LEAKS(T) + { QList<T> empty1; QList<T> empty2; @@ -616,28 +685,11 @@ void tst_QList::add() const } } -void tst_QList::addInt() const -{ - add<int>(); -} - -void tst_QList::addMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - add<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::addCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - add<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - template<typename T> void tst_QList::append() const { + TST_QLIST_CHECK_LEAKS(T) + { QList<T> myvec; myvec.append(SimpleValue<T>::at(0)); @@ -676,25 +728,193 @@ void tst_QList::append() const 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::appendInt() const +void tst_QList::assignEmpty() const { - append<int>(); + // 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 } -void tst_QList::appendMovable() const +template <typename T> +void tst_QList::assign() const { - const int instancesCount = Movable::counter.loadAcquire(); - append<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); + 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); + } + } } -void tst_QList::appendCustom() const +template <typename T> +void tst_QList::assignUsesPrependBuffer() const { - const int instancesCount = Custom::counter.loadAcquire(); - append<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); + 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 @@ -703,9 +923,15 @@ void tst_QList::appendRvalue() const v.append("hello"); QString world = "world"; v.append(std::move(world)); - QVERIFY(world.isEmpty()); 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 @@ -882,6 +1108,7 @@ void tst_QList::appendList() const // Using operators // << QList<ConstructionCounted> v6; + v6.reserve(4); v6 << (QList<ConstructionCounted>() << 1 << 2); v6 << (QList<ConstructionCounted>() << 3 << 4); QCOMPARE(v6, expectedFour); @@ -931,11 +1158,14 @@ void tst_QList::at() const 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); @@ -958,29 +1188,15 @@ void tst_QList::capacity() const QVERIFY(myvec.capacity() == 0); } -void tst_QList::capacityInt() const -{ - capacity<int>(); -} - -void tst_QList::capacityMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - capacity<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::capacityCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - capacity<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - 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(); @@ -990,37 +1206,25 @@ void tst_QList::clear() const QCOMPARE(myvec.capacity(), oldCapacity); } -void tst_QList::clearInt() const -{ - clear<int>(); -} - -void tst_QList::clearMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - clear<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::clearCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - clear<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - void tst_QList::constData() const { int arr[] = { 42, 43, 44 }; QList<int> myvec; + QCOMPARE(myvec.constData(), nullptr); + QVERIFY(!myvec.isDetached()); + myvec << 42 << 43 << 44; - QVERIFY(memcmp(myvec.constData(), reinterpret_cast<const int *>(&arr), sizeof(int) * 3) == 0); + 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"))); @@ -1036,28 +1240,34 @@ void tst_QList::contains() const template<typename T> void tst_QList::count() const { + TST_QLIST_CHECK_LEAKS(T) + // total size { // zero size QList<T> myvec; - QVERIFY(myvec.count() == 0); + QVERIFY(myvec.size() == 0); + QVERIFY(!myvec.isDetached()); // grow myvec.append(SimpleValue<T>::at(0)); - QVERIFY(myvec.count() == 1); + QVERIFY(myvec.size() == 1); myvec.append(SimpleValue<T>::at(1)); - QVERIFY(myvec.count() == 2); + QVERIFY(myvec.size() == 2); // shrink myvec.remove(0); - QVERIFY(myvec.count() == 1); + QVERIFY(myvec.size() == 1); myvec.remove(0); - QVERIFY(myvec.count() == 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 @@ -1074,28 +1284,8 @@ void tst_QList::count() const } } -void tst_QList::countInt() const -{ - count<int>(); -} - -void tst_QList::countMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - count<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::countCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - count<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - void tst_QList::cpp17ctad() const { -#ifdef __cpp_deduction_guides #define QVERIFY_IS_VECTOR_OF(obj, Type) \ QVERIFY2((std::is_same<decltype(obj), QList<Type>>::value), \ QMetaType::fromType<decltype(obj)::value_type>().name()) @@ -1115,14 +1305,13 @@ void tst_QList::cpp17ctad() const 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_QList::data() const { QList<int> myvec; + QCOMPARE(myvec.data(), nullptr); + myvec << 42 << 43 << 44; // make sure it starts off ok @@ -1135,16 +1324,23 @@ void tst_QList::data() const QCOMPARE(*(myvec.data() + 1), 69); int arr[] = { 42, 69, 44 }; - QVERIFY(memcmp(myvec.data(), reinterpret_cast<int *>(&arr), sizeof(int) * 3) == 0); + 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)); @@ -1155,25 +1351,6 @@ void tst_QList::empty() const QVERIFY(myvec.empty()); } -void tst_QList::emptyInt() const -{ - empty<int>(); -} - -void tst_QList::emptyMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - empty<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::emptyCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - empty<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - void tst_QList::endsWith() const { QList<int> myvec; @@ -1197,58 +1374,24 @@ void tst_QList::endsWith() const 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); } -void tst_QList::eraseEmptyInt() const -{ - eraseEmpty<int>(); -} - -void tst_QList::eraseEmptyMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - eraseEmpty<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::eraseEmptyCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - eraseEmpty<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - 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); } -void tst_QList::eraseEmptyReservedInt() const -{ - eraseEmptyReserved<int>(); -} - -void tst_QList::eraseEmptyReservedMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - eraseEmptyReserved<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::eraseEmptyReservedCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - eraseEmptyReserved<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - template<typename T> struct SharedVectorChecker { @@ -1279,10 +1422,12 @@ struct SharedVectorChecker template<typename T> void tst_QList::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. + 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); @@ -1336,46 +1481,11 @@ void tst_QList::erase(bool shared) const } } -void tst_QList::eraseInt() const -{ - erase<int>(false); -} - -void tst_QList::eraseIntShared() const -{ - erase<int>(true); -} - -void tst_QList::eraseMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - erase<Movable>(false); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::eraseMovableShared() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - erase<Movable>(true); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::eraseCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - erase<Custom>(false); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - -void tst_QList::eraseCustomShared() const +template<typename T> +void tst_QList::eraseReserved() const { - const int instancesCount = Custom::counter.loadAcquire(); - erase<Custom>(true); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} + TST_QLIST_CHECK_LEAKS(T) -template<typename T> void tst_QList::eraseReserved() const -{ { QList<T> v(12); v.reserve(16); @@ -1408,30 +1518,17 @@ template<typename T> void tst_QList::eraseReserved() const } } -void tst_QList::eraseReservedInt() const -{ - eraseReserved<int>(); -} - -void tst_QList::eraseReservedMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - eraseReserved<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::eraseReservedCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - eraseReserved<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - 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)); @@ -1452,28 +1549,11 @@ void tst_QList::fill() const QCOMPARE(myvec, QList<T>() << SimpleValue<T>::at(3) << SimpleValue<T>::at(3)); } -void tst_QList::fillInt() const -{ - fill<int>(); -} - -void tst_QList::fillMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - fill<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::fillCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - fill<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - 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) }; @@ -1511,25 +1591,6 @@ void tst_QList::fillDetach() const } } -void tst_QList::fillDetachInt() const -{ - fillDetach<int>(); -} - -void tst_QList::fillDetachMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - fillDetach<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::fillDetachCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - fillDetach<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - void tst_QList::first() const { QList<int> myvec; @@ -1627,6 +1688,8 @@ void tst_QList::constFirst() const 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); @@ -1638,25 +1701,6 @@ void tst_QList::fromList() const QCOMPARE(list, QList<T>() << SimpleValue<T>::at(0) << SimpleValue<T>::at(1) << SimpleValue<T>::at(2) << SimpleValue<T>::at(3)); } -void tst_QList::fromListInt() const -{ - fromList<int>(); -} - -void tst_QList::fromListMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - fromList<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::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_QList::fromStdVector() const { @@ -1676,6 +1720,11 @@ void tst_QList::fromStdVector() const 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); @@ -1700,6 +1749,8 @@ void tst_QList::indexOf() const template <typename T> void tst_QList::insert() const { + TST_QLIST_CHECK_LEAKS(T) + QList<T> myvec; const T tA = SimpleValue<T>::at(0), @@ -1763,21 +1814,52 @@ void tst_QList::insert() const QCOMPARE(myvec, QList<T>() << tB << tB << tX << tZ << ti << ti << tA << tB << tC << tT); QCOMPARE(myvec2, myvec); -} -void tst_QList::insertInt() const -{ - insert<int>(); -} - -void tst_QList::insertMovable() const -{ - insert<Movable>(); -} - -void tst_QList::insertCustom() const -{ - insert<Custom>(); + // 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() @@ -1806,6 +1888,7 @@ void tst_QList::isEmpty() const // starts ok QVERIFY(myvec.isEmpty()); + QVERIFY(!myvec.isDetached()); // not empty now myvec.append(QLatin1String("hello there")); @@ -1911,6 +1994,11 @@ void tst_QList::constLast() const 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); @@ -1934,6 +2022,12 @@ void tst_QList::lastIndexOf() const 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"); @@ -1961,6 +2055,8 @@ void tst_QList::sliced() const 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); @@ -1971,15 +2067,17 @@ void tst_QList::qhash() const 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.count() - 1); + list.move(0, list.size() - 1); QCOMPARE(list, QList<T>() << T_BAR << T_BAZ << T_FOO); // move it back - list.move(list.count() - 1, 0); + list.move(list.size() - 1, 0); QCOMPARE(list, QList<T>() << T_FOO << T_BAR << T_BAZ); // move an item in the middle @@ -1987,34 +2085,25 @@ void tst_QList::move() const QCOMPARE(list, QList<T>() << T_BAR << T_FOO << T_BAZ); } -void tst_QList::moveInt() const -{ - move<int>(); -} - -void tst_QList::moveMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - move<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::moveCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - move<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - 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 @@ -2042,23 +2131,19 @@ void tst_QList::prepend() const QCOMPARE(myvec.at(0), val5); } -void tst_QList::prependInt() const +void tst_QList::prependRvalue() const { - prepend<int>(); -} + QList<QString> myvec; -void tst_QList::prependMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - prepend<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} + QString hello = "hello"; + QString world = "world"; -void tst_QList::prependCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - prepend<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); + 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 @@ -2071,61 +2156,61 @@ void tst_QList::removeAllWithAlias() const 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); - myvec << val1 << val2 << val3; - myvec << val1 << val2 << val3; - myvec << val1 << val2 << val3; - // remove middle + 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 << val1 << val2 << val3 << val1 << val2 << val3); + 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(val4)); + QVERIFY(!myvec.removeOne(val5)); QVERIFY(myvec.removeOne(val2)); - QCOMPARE(myvec, QList<T>() << val1 << val3 << val1 << val3 << val1 << val2 << val3); + 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(val4), 0); + QCOMPARE(myvec.removeAll(val5), 0); QVERIFY(myvecCopy.isSharedWith(myvec)); QCOMPARE(myvec.removeAll(val1), 3); QVERIFY(!myvecCopy.isSharedWith(myvec)); - QCOMPARE(myvec, QList<T>() << val3 << val3 << val2 << val3); + 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 << val3 << val3); + 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>()); } -void tst_QList::removeInt() const -{ - remove<int>(); -} - -void tst_QList::removeMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - remove<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::removeCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - remove<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - struct RemoveLastTestClass { RemoveLastTestClass() { other = 0; deleted = false; } @@ -2245,7 +2330,9 @@ void tst_QList::resizePOD_data() const 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; @@ -2261,6 +2348,9 @@ void tst_QList::resizePOD() const 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 @@ -2293,7 +2383,9 @@ void tst_QList::resizeComplexMovable_data() const 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; @@ -2311,6 +2403,8 @@ void tst_QList::resizeComplexMovable() const 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 @@ -2345,7 +2439,9 @@ void tst_QList::resizeComplex_data() const 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; @@ -2362,6 +2458,8 @@ void tst_QList::resizeComplex() const 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 @@ -2434,6 +2532,335 @@ void tst_QList::resizeToTheSameSize() const 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; @@ -2452,40 +2879,31 @@ void tst_QList::reverseIterators() const 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); -} - -void tst_QList::sizeInt() const -{ - size<int>(); -} - -void tst_QList::sizeMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - size<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::sizeCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - size<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); + QCOMPARE(myvec.size(), myvec.size()); } // ::squeeze() is tested in ::capacity(). @@ -2513,6 +2931,8 @@ void tst_QList::startsWith() const 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); @@ -2528,25 +2948,6 @@ void tst_QList::swap() const QCOMPARE(v2,QList<T>() << val1 << val2 << val3); } -void tst_QList::swapInt() const -{ - swap<int>(); -} - -void tst_QList::swapMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - swap<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::swapCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - swap<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - void tst_QList::toList() const { QList<QString> myvec; @@ -2575,6 +2976,11 @@ void tst_QList::toStdVector() const 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 @@ -2691,11 +3097,21 @@ void tst_QList::reserveZero() 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)); @@ -2714,25 +3130,6 @@ void tst_QList::initializeList() QCOMPARE(v4.size(), 0); } -void tst_QList::initializeListInt() -{ - initializeList<int>(); -} - -void tst_QList::initializeListMovable() -{ - const int instancesCount = Movable::counter.loadAcquire(); - initializeList<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::initializeListCustom() -{ - const int instancesCount = Custom::counter.loadAcquire(); - initializeList<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - void tst_QList::const_shared_null() { QList<int> v2; @@ -2742,6 +3139,8 @@ void tst_QList::const_shared_null() template<typename T> void tst_QList::detach() const { + TST_QLIST_CHECK_LEAKS(T) + { // detach an empty vector QList<T> v; @@ -2836,25 +3235,6 @@ void tst_QList::detach() const } } -void tst_QList::detachInt() const -{ - detach<int>(); -} - -void tst_QList::detachMovable() const -{ - const int instancesCount = Movable::counter.loadAcquire(); - detach<Movable>(); - QCOMPARE(instancesCount, Movable::counter.loadAcquire()); -} - -void tst_QList::detachCustom() const -{ - const int instancesCount = Custom::counter.loadAcquire(); - detach<Custom>(); - QCOMPARE(instancesCount, Custom::counter.loadAcquire()); -} - static QAtomicPointer<QList<int> > detachThreadSafetyDataInt; static QAtomicPointer<QList<Movable> > detachThreadSafetyDataMovable; static QAtomicPointer<QList<Custom> > detachThreadSafetyDataCustom; @@ -3003,53 +3383,43 @@ void tst_QList::swapItemsAt() const QCOMPARE(copy.at(2), 2); } -void tst_QList::emplaceInt() +void tst_QList::emplaceReturnsIterator() { - emplaceImpl<int>(); -} + QList<Movable> vec; -void tst_QList::emplaceCustom() -{ - emplaceImpl<Custom>(); -} + vec.emplace(0, 'k')->i = 'p'; -void tst_QList::emplaceMovable() -{ - emplaceImpl<Movable>(); + QCOMPARE(vec[0].i, 'p'); } -void tst_QList::emplaceConsistentWithStdVectorInt() +void tst_QList::emplaceFront() const { - emplaceConsistentWithStdVectorImpl<int>(); -} + QAtomicScopedValueRollback rollback(Movable::counter, 0); -void tst_QList::emplaceConsistentWithStdVectorCustom() -{ - emplaceConsistentWithStdVectorImpl<Custom>(); -} + QList<Movable> vec; + vec.emplaceFront('b'); + QCOMPARE(Movable::counter, 1); -void tst_QList::emplaceConsistentWithStdVectorMovable() -{ - emplaceConsistentWithStdVectorImpl<Movable>(); -} + vec.emplaceFront('a'); + QCOMPARE(Movable::counter, 2); -void tst_QList::emplaceConsistentWithStdVectorQString() -{ - emplaceConsistentWithStdVectorImpl<QString>(); + QCOMPARE(vec, QList<Movable>({ 'a', 'b' })); } -void tst_QList::emplaceReturnsIterator() +void tst_QList::emplaceFrontReturnsRef() const { QList<Movable> vec; - vec.emplace(0, 'k')->i = 'p'; + QCOMPARE(vec.emplaceFront('c').i, 'c'); - QCOMPARE(vec[0].i, 'p'); + vec.emplaceFront('b').i = 'a'; + + QCOMPARE(vec.front().i, 'a'); } void tst_QList::emplaceBack() { - QScopedValueRollback<QAtomicInt> rollback(Movable::counter, 0); + QAtomicScopedValueRollback rollback(Movable::counter, 0); QList<Movable> vec; @@ -3106,11 +3476,48 @@ void tst_QList::emplaceWithElementFromTheSameContainer_data() 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> @@ -3130,6 +3537,8 @@ static void squeezeVec(QList<T> &qVec, std::vector<T> &stdVec) 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>) { @@ -3218,7 +3627,7 @@ void tst_QList::fromReadOnlyData() const QCOMPARE(v.size(), qsizetype(11)); // v.capacity() is unspecified, for now - QCOMPARE((void*)(const char*)(v.constBegin() + v.size()), (void*)(const char*)v.constEnd()); + QCOMPARE((void*)(v.constBegin() + v.size()).operator->(), (void*)v.constEnd().operator->()); for (int i = 0; i < 10; ++i) QCOMPARE(v[i], char('A' + i)); @@ -3247,7 +3656,7 @@ struct alignas(8) CustomAligned friend bool operator==(const CustomAligned &x, const CustomAligned &y) { return x.v == y.v; } }; -void tst_QList::qtbug_90359() const +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); @@ -3265,5 +3674,296 @@ void tst_QList::qtbug_90359() const 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 index e2f1ed45ea..b968945ac6 100644 --- a/tests/auto/corelib/tools/qmacautoreleasepool/CMakeLists.txt +++ b/tests/auto/corelib/tools/qmacautoreleasepool/CMakeLists.txt @@ -1,12 +1,20 @@ -# Generated from qmacautoreleasepool.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES + Qt::CorePrivate ${FWFoundation} ) diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm b/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm index 56e9a2748d..e7923b47f3 100644 --- a/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm +++ b/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm @@ -1,33 +1,10 @@ -/**************************************************************************** -** -** 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 <QTest> +#include <QtCore/private/qcore_mac_p.h> + #include <Foundation/Foundation.h> class tst_QMacAutoreleasePool : public QObject @@ -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 index fc5be609e1..cec589628f 100644 --- a/tests/auto/corelib/tools/qmakearray/CMakeLists.txt +++ b/tests/auto/corelib/tools/qmakearray/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qmakearray.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp b/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp index 2e27272364..1d796452b0 100644 --- a/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp +++ b/tests/auto/corelib/tools/qmakearray/tst_qmakearray.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** 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 <QTest> diff --git a/tests/auto/corelib/tools/qmap/CMakeLists.txt b/tests/auto/corelib/tools/qmap/CMakeLists.txt index c0a2cb79ab..bddf9267f8 100644 --- a/tests/auto/corelib/tools/qmap/CMakeLists.txt +++ b/tests/auto/corelib/tools/qmap/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qmap.pro. +# 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 - DEFINES - #-QT_NO_JAVA_STYLE_ITERATORS # special case remove ) + +qt_internal_undefine_global_definition(tst_qmap 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 d8be138c0e..6950dcf705 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -1,34 +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 #include <qmap.h> #include <QTest> + #include <QDebug> +#include <QScopeGuard> + +using namespace Qt::StringLiterals; QT_WARNING_DISABLE_DEPRECATED @@ -47,6 +26,7 @@ private slots: void beginEnd(); void firstLast(); void key(); + void value(); void swap(); @@ -61,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(); @@ -78,6 +63,16 @@ private slots: 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 { @@ -114,6 +109,7 @@ 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; @@ -176,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 @@ -192,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") ); @@ -204,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") ); @@ -212,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 @@ -288,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 ); { @@ -316,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() @@ -323,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() @@ -370,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() @@ -409,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")); @@ -439,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); @@ -470,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() @@ -492,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: @@ -524,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"); @@ -532,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"); @@ -548,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)); } @@ -562,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; + + QVERIFY(!map1.contains(1)); + QVERIFY(!map1.isDetached()); - map1.insert(1, "one"); - QVERIFY(map1.contains(1)); + map1.insert(1, "one"); + QVERIFY(map1.contains(1)); - for(i=2; i < 100; ++i) - map1.insert(i, "teststring"); - for(i=99; i > 1; --i) - QVERIFY(map1.contains(i)); + 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)); + 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() { - QMultiMap<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); } - QMultiMap<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; } @@ -628,32 +853,53 @@ void tst_QMap::find() void tst_QMap::constFind() { - QMultiMap<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); } - QMultiMap<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; } @@ -662,58 +908,128 @@ void tst_QMap::constFind() void tst_QMap::lowerUpperBound() { - QMultiMap<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()); + } - map1.insert(1, "one"); - map1.insert(5, "five"); - map1.insert(10, "ten"); + const QMultiMap<int, QString> emptyConstMap; + QCOMPARE(emptyConstMap.lowerBound(1), emptyConstMap.constEnd()); + QCOMPARE(emptyConstMap.upperBound(1), emptyConstMap.constEnd()); + QVERIFY(!emptyConstMap.isDetached()); + 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() { - QMultiMap<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"); @@ -743,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() @@ -834,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()); @@ -862,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; - 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() @@ -925,27 +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.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.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.values() == (QList<int>() << 1 << -2)); + QVERIFY(map.uniqueKeys() == QList<QString>({ "alpha", "beta" })); - map.insertMulti("alpha", 2); + 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); + 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() @@ -968,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); @@ -1046,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); @@ -1073,6 +1706,37 @@ void tst_QMap::const_shared_null() void tst_QMap::equal_range() { + { + 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()); + QMultiMap<int, QString> map; const QMultiMap<int, QString> &cmap = map; @@ -1137,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)); @@ -1150,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) { @@ -1233,12 +1891,15 @@ void testDetachWhenInsert() dest.insert(3, 3); Map<int, int> destCopy = dest; - dest.insert(source); + 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.count(), 1); // unchanged + QCOMPARE(destCopy.size(), 1); // unchanged } // copy insertion of shared map @@ -1253,13 +1914,16 @@ void testDetachWhenInsert() dest.insert(3, 3); Map<int, int> destCopy = dest; - dest.insert(source); + 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.count(), 1); // unchanged + QCOMPARE(destCopy.size(), 1); // unchanged } // move insertion of non-shared map @@ -1273,10 +1937,13 @@ void testDetachWhenInsert() dest.insert(3, 3); Map<int, int> destCopy = dest; - dest.insert(source); + 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.count(), 1); // unchanged + QCOMPARE(destCopy.size(), 1); // unchanged } // move insertion of shared map @@ -1291,18 +1958,33 @@ void testDetachWhenInsert() dest.insert(3, 3); Map<int, int> destCopy = dest; - dest.insert(std::move(source)); + 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.count(), 1); // unchanged + 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); @@ -1315,7 +1997,7 @@ void tst_QMap::insertMap() map.insert(map2); - QCOMPARE(map.count(), 5); + QCOMPARE(map.size(), 5); for (int i = 0; i < 5; ++i) QCOMPARE(map[i], i); } @@ -1330,7 +2012,7 @@ void tst_QMap::insertMap() map.insert(map2); - QCOMPARE(map.count(), 17); + 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); @@ -1352,7 +2034,7 @@ void tst_QMap::insertMap() QMap<int, int> map2; map.insert(map2); - QCOMPARE(map.count(), 1); + QCOMPARE(map.size(), 1); QCOMPARE(map[1], 1); } { @@ -1361,8 +2043,12 @@ void tst_QMap::insertMap() map2.insert(1, 1); map.insert(map2); - QCOMPARE(map.count(), 1); + QCOMPARE(map.size(), 1); QCOMPARE(map[1], 1); + + QMap<int, int> map3; + map3.insert(std::move(map2)); + QCOMPARE(map3, map); } { QMap<int, int> map; @@ -1373,7 +2059,7 @@ void tst_QMap::insertMap() // Test inserting into self, nothing should happen map.insert(map); - QCOMPARE(map.count(), 3); + QCOMPARE(map.size(), 3); for (int i = 0; i < 3; ++i) QCOMPARE(map[i], i); } @@ -1391,7 +2077,7 @@ void tst_QMap::insertMap() map.insert(map2); - QCOMPARE(map.count(), 1); + QCOMPARE(map.size(), 1); } testDetachWhenInsert<QMap>(); @@ -1453,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")); @@ -1463,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()); @@ -1547,48 +2233,48 @@ void tst_QMap::testInsertMultiWithHint() { QMultiMap<int, int> map; - map.insertMulti(map.end(), 64, 65); + 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); - QMultiMap<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); - QMultiMap<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); } @@ -1597,9 +2283,9 @@ void tst_QMap::eraseValidIteratorOnSharedMap() { 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); QMultiMap<int, int>::iterator i = a.begin(); @@ -1624,8 +2310,8 @@ void tst_QMap::eraseValidIteratorOnSharedMap() // Border cases QMultiMap <QString, QString> ms1, ms2, ms3; ms1.insert("foo", "bar"); - ms1.insertMulti("foo", "quux"); - ms1.insertMulti("foo", "bar"); + ms1.insert("foo", "quux"); + ms1.insert("foo", "bar"); QMultiMap <QString, QString>::iterator si = ms1.begin(); ms2 = ms1; @@ -1661,6 +2347,14 @@ void tst_QMap::removeElementsInMap() }; { + 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 }, @@ -1718,9 +2412,21 @@ void tst_QMap::removeElementsInMap() 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 }, @@ -1817,8 +2523,127 @@ void tst_QMap::removeElementsInMap() 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 index aa58ce03ab..2e0ea797ff 100644 --- a/tests/auto/corelib/tools/qmargins/CMakeLists.txt +++ b/tests/auto/corelib/tools/qmargins/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qmargins.pro. +# 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/tst_qmargins.cpp b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp index 8eaa4edd3b..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$ -** -****************************************************************************/ +// 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 @@ -54,6 +60,9 @@ private slots: #endif void structuredBinding(); + + void toMarginsF_data(); + void toMarginsF(); }; // Testing get/set functions @@ -339,5 +348,34 @@ void tst_QMargins::structuredBinding() } } +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 index dcc86fe555..a21481b7ba 100644 --- a/tests/auto/corelib/tools/qmessageauthenticationcode/CMakeLists.txt +++ b/tests/auto/corelib/tools/qmessageauthenticationcode/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qmessageauthenticationcode.pro. +# 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/tst_qmessageauthenticationcode.cpp b/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp index 3c8f8e13d1..9e94ad77e9 100644 --- a/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp +++ b/tests/auto/corelib/tools/qmessageauthenticationcode/tst_qmessageauthenticationcode.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** 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> @@ -37,16 +12,64 @@ 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"); @@ -124,14 +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(result, code); + QCOMPARE(resultView, code); - result = QMessageAuthenticationCode::hash(message, key, algo); + const auto result = QMessageAuthenticationCode::hash(message, key, algo); QCOMPARE(result, code); } @@ -147,17 +169,16 @@ 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); } @@ -179,7 +200,7 @@ void tst_QMessageAuthenticationCode::addData_overloads() QMessageAuthenticationCode mac(algo); mac.setKey(key); mac.addData(message.constData(), message.size()); - QByteArray result = mac.result(); + QByteArrayView result = mac.resultView(); QCOMPARE(result, code); } @@ -191,12 +212,55 @@ void tst_QMessageAuthenticationCode::addData_overloads() QMessageAuthenticationCode mac(algo); mac.setKey(key); QVERIFY(mac.addData(&buffer)); - QByteArray result = mac.result(); + 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 index 7584d580ec..d0205cfa15 100644 --- a/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt +++ b/tests/auto/corelib/tools/qoffsetstringarray/CMakeLists.txt @@ -1,12 +1,26 @@ -# Generated from qoffsetstringarray.pro. +# 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 - PUBLIC_LIBRARIES + 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/tst_qoffsetstringarray.cpp b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp index 9445366efc..dbb24e7af4 100644 --- a/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp +++ b/tests/auto/corelib/tools/qoffsetstringarray/tst_qoffsetstringarray.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** 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 <QTest> @@ -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 index 3d0ba82e44..2dd048e015 100644 --- a/tests/auto/corelib/tools/qpair/CMakeLists.txt +++ b/tests/auto/corelib/tools/qpair/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qpair.pro. +# 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 diff --git a/tests/auto/corelib/tools/qpair/tst_qpair.cpp b/tests/auto/corelib/tools/qpair/tst_qpair.cpp index 3bdc7f8895..0c9d87bb01 100644 --- a/tests/auto/corelib/tools/qpair/tst_qpair.cpp +++ b/tests/auto/corelib/tools/qpair/tst_qpair.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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> @@ -90,7 +65,7 @@ static_assert( QTypeInfo<QPairPM>::isRelocatable ); static_assert(!QTypeInfo<QPairPP>::isComplex); static_assert( QTypeInfo<QPairPP>::isRelocatable ); -static_assert(!QTypeInfo<QPairPP>::isPointer); +static_assert(!std::is_pointer_v<QPairPP>); void tst_QPair::pairOfReferences() diff --git a/tests/auto/corelib/tools/qpoint/CMakeLists.txt b/tests/auto/corelib/tools/qpoint/CMakeLists.txt index ddc0733231..f1402d8815 100644 --- a/tests/auto/corelib/tools/qpoint/CMakeLists.txt +++ b/tests/auto/corelib/tools/qpoint/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qpoint.pro. +# 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/tst_qpoint.cpp b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp index 3ce8c3942d..7fea787131 100644 --- a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp +++ b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp @@ -1,36 +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$ -** -****************************************************************************/ +// 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 @@ -45,6 +44,9 @@ private slots: void transposed(); + void toPointF_data(); + void toPointF(); + void rx(); void ry(); @@ -131,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)); diff --git a/tests/auto/corelib/tools/qpointf/CMakeLists.txt b/tests/auto/corelib/tools/qpointf/CMakeLists.txt index 09f725f8e6..16e5a9036a 100644 --- a/tests/auto/corelib/tools/qpointf/CMakeLists.txt +++ b/tests/auto/corelib/tools/qpointf/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qpointf.pro. +# 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/tst_qpointf.cpp b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp index 645c1ba210..392c22c70a 100644 --- a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp +++ b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp @@ -1,30 +1,27 @@ -/**************************************************************************** -** -** 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 <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> diff --git a/tests/auto/corelib/tools/qqueue/CMakeLists.txt b/tests/auto/corelib/tools/qqueue/CMakeLists.txt index c3528163eb..bf229eee6a 100644 --- a/tests/auto/corelib/tools/qqueue/CMakeLists.txt +++ b/tests/auto/corelib/tools/qqueue/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qqueue.pro. +# 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/tst_qqueue.cpp b/tests/auto/corelib/tools/qqueue/tst_qqueue.cpp index 959927cab8..44d4c34768 100644 --- a/tests/auto/corelib/tools/qqueue/tst_qqueue.cpp +++ b/tests/auto/corelib/tools/qqueue/tst_qqueue.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 <QTest> diff --git a/tests/auto/corelib/tools/qrect/CMakeLists.txt b/tests/auto/corelib/tools/qrect/CMakeLists.txt index d48bec0c7d..a02e1c33a5 100644 --- a/tests/auto/corelib/tools/qrect/CMakeLists.txt +++ b/tests/auto/corelib/tools/qrect/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qrect.pro. +# 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/tst_qrect.cpp b/tests/auto/corelib/tools/qrect/tst_qrect.cpp index 4057eb67fb..0f3dd1a0ef 100644 --- a/tests/auto/corelib/tools/qrect/tst_qrect.cpp +++ b/tests/auto/corelib/tools/qrect/tst_qrect.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) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <qrect.h> @@ -32,6 +7,7 @@ #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(); @@ -2526,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 } { @@ -2695,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 } { @@ -3536,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"); @@ -4313,8 +4315,6 @@ void tst_QRect::containsPointF_data() 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; diff --git a/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt b/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt index d5d633c0bd..cfb7c6f461 100644 --- a/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt +++ b/tests/auto/corelib/tools/qringbuffer/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qringbuffer.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp b/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp index 3b922de0ca..c7b79cfae1 100644 --- a/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp +++ b/tests/auto/corelib/tools/qringbuffer/tst_qringbuffer.cpp @@ -1,33 +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$ -** -****************************************************************************/ +// 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 <qlist.h> @@ -38,6 +14,7 @@ class tst_QRingBuffer : public QObject private slots: void constructing(); void usingInVector(); + void usingInVarLengthArray(); void readPointerAtPositionWriteRead(); void readPointerAtPositionEmptyRead(); void readPointerAtPositionWithHead(); @@ -83,10 +60,20 @@ void tst_QRingBuffer::constructing() void tst_QRingBuffer::usingInVector() { QRingBuffer ringBuffer; - QList<QRingBuffer> buffers; + std::vector<QRingBuffer> buffers; ringBuffer.reserve(5); - buffers.append(ringBuffer); + 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.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 index df2d2aa509..7bfcfdebbf 100644 --- a/tests/auto/corelib/tools/qscopedpointer/CMakeLists.txt +++ b/tests/auto/corelib/tools/qscopedpointer/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qscopedpointer.pro. +# 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/tst_qscopedpointer.cpp b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp index 967a3ccf55..3468c97f42 100644 --- a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp +++ b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.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 <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() @@ -367,51 +347,57 @@ 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); -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - pa2.take(); -QT_WARNING_POP - 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); -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - pa2.take(); -QT_WARNING_POP - QCOMPARE( RefCounted::instanceCount.loadRelaxed(), 85 ); } @@ -459,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 index fa3e3e3024..359a910a0a 100644 --- a/tests/auto/corelib/tools/qscopedvaluerollback/CMakeLists.txt +++ b/tests/auto/corelib/tools/qscopedvaluerollback/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qscopedvaluerollback.pro. +# 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/tst_qscopedvaluerollback.cpp b/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.cpp index a05cf7ef33..3b493b4e75 100644 --- a/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.cpp +++ b/tests/auto/corelib/tools/qscopedvaluerollback/tst_qscopedvaluerollback.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 <QTest> #include <QtCore/QScopedValueRollback> diff --git a/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt b/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt index d21df799ea..6f6d664554 100644 --- a/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt +++ b/tests/auto/corelib/tools/qscopeguard/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qscopeguard.pro. +# 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 diff --git a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp index 21567137fd..b7c2b952e2 100644 --- a/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp +++ b/tests/auto/corelib/tools/qscopeguard/tst_qscopeguard.cpp @@ -1,35 +1,12 @@ -/**************************************************************************** -** -** 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. -** 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 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_QScopeGuard \internal @@ -45,6 +22,7 @@ private Q_SLOTS: void construction(); void constructionFromLvalue(); void constructionFromRvalue(); + void optionalGuard(); void leavingScope(); void exceptions(); }; @@ -94,7 +72,6 @@ static int s_globalState = 0; void tst_QScopeGuard::construction() { -#ifdef __cpp_deduction_guides QScopeGuard fromLambda([] { }); QScopeGuard fromFunction(func); QScopeGuard fromFunctionPointer(&func); @@ -105,14 +82,10 @@ void tst_QScopeGuard::construction() std::function<void()> stdFunction(func); QScopeGuard fromNamedStdFunction(stdFunction); #endif -#else - QSKIP("This test requires C++17 Class Template Argument Deduction support enabled in the compiler."); -#endif } void tst_QScopeGuard::constructionFromLvalue() { -#ifdef __cpp_deduction_guides Callable::resetCounts(); { Callable callable; @@ -127,14 +100,10 @@ void tst_QScopeGuard::constructionFromLvalue() } QCOMPARE(Callable::copied, 1); QCOMPARE(Callable::moved, 0); -#else - QSKIP("This test requires C++17 Class Template Argument Deduction support enabled in the compiler."); -#endif } void tst_QScopeGuard::constructionFromRvalue() { -#ifdef __cpp_deduction_guides Callable::resetCounts(); { Callable callable; @@ -149,9 +118,24 @@ void tst_QScopeGuard::constructionFromRvalue() } QCOMPARE(Callable::copied, 0); QCOMPARE(Callable::moved, 1); -#else - QSKIP("This test requires C++17 Class Template Argument Deduction support enabled in the compiler."); -#endif +} + +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() diff --git a/tests/auto/corelib/tools/qset/CMakeLists.txt b/tests/auto/corelib/tools/qset/CMakeLists.txt index ed92c1e036..9e3e33ee7c 100644 --- a/tests/auto/corelib/tools/qset/CMakeLists.txt +++ b/tests/auto/corelib/tools/qset/CMakeLists.txt @@ -1,15 +1,19 @@ -# Generated from qset.pro. +# 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 - #DEFINES # special case remove - #-QT_NO_JAVA_STYLE_ITERATORS # special case remove ) -## Scopes: -##################################################################### +qt_internal_undefine_global_definition(tst_qset 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 6e69b034ec..116d38112b 100644 --- a/tests/auto/corelib/tools/qset/tst_qset.cpp +++ b/tests/auto/corelib/tools/qset/tst_qset.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 <QTest> #include <qset.h> @@ -33,7 +8,7 @@ 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; } @@ -61,6 +36,7 @@ private slots: void insert(); void insertConstructionCounted(); void setOperations(); + void setOperationsOnEmptySet(); void stlIterator(); void stlMutableIterator(); void javaIterator(); @@ -69,6 +45,8 @@ private slots: void initializerList(); void qhash(); void intersects(); + void find(); + void values(); }; struct IdentityTracker { @@ -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); @@ -248,27 +232,39 @@ void tst_QSet::squeeze() set.squeeze(); QVERIFY(set.capacity() < 100); - for (int i = 0; i < 512; ++i) + for (int i = 0; i < 500; ++i) set.insert(i); - QVERIFY(set.capacity() == 512); + 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() == 512); + QCOMPARE(set.capacity(), capacity); + // removing elements does not shed capacity set.remove(499); - QVERIFY(set.capacity() == 512); + QCOMPARE(set.capacity(), capacity); set.insert(499); - QVERIFY(set.capacity() == 512); + QCOMPARE(set.capacity(), capacity); - set.insert(1000); - QVERIFY(set.capacity() == 1024); + // 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); } @@ -312,6 +308,7 @@ void tst_QSet::clear() set1.clear(); QVERIFY(set1.size() == 0); + QVERIFY(!set1.isDetached()); set1.insert("foo"); QVERIFY(set1.size() != 0); @@ -329,7 +326,6 @@ 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::fromType<decltype(obj)::value_type>().name()) @@ -349,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))); @@ -396,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)); @@ -417,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; @@ -438,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); @@ -467,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() @@ -487,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); @@ -527,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() @@ -756,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; @@ -835,13 +939,11 @@ void tst_QSet::javaIterator() 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); @@ -919,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)); @@ -928,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); @@ -947,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."); } // @@ -982,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)); @@ -993,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)); @@ -1001,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 37a24b6b9b..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,7 +55,7 @@ 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(); } @@ -362,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" 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/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 fa52c4f6c5..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 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/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index 985564731d..f42637a3fe 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Copyright (C) 2020 Intel Corporation. -** Copyright (C) 2019 Klarälvdalens Datakonsult AB. -** 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" @@ -37,8 +12,8 @@ #include <QtCore/QList> #include <QtCore/QMap> #include <QtCore/QThread> +#include <QtCore/private/qvolatile_p.h> -#include "externaltests.h" #include "forwarddeclared.h" #include "nontracked.h" #include "wrapper.h" @@ -48,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 @@ -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. @@ -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; @@ -1231,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 @@ -1925,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 @@ -1936,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(); } @@ -1949,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(); @@ -2014,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); @@ -2113,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); @@ -2125,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() @@ -2334,6 +2399,7 @@ void tst_QSharedPointer::invalidConstructs() QFAIL("Fail"); } } +#endif // #if 0 void tst_QSharedPointer::qvariantCast() { @@ -2663,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 @@ -2705,7 +2771,7 @@ namespace ReentrancyWhileDestructing { { QSharedPointer<IB> b; - virtual QSharedPointer<IB> getB() + virtual QSharedPointer<IB> getB() override { return b; } @@ -2776,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 index 5ecd154cd3..91de696ddd 100644 --- a/tests/auto/corelib/tools/qsize/CMakeLists.txt +++ b/tests/auto/corelib/tools/qsize/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qsize.pro. +# 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/tst_qsize.cpp b/tests/auto/corelib/tools/qsize/tst_qsize.cpp index 83b4f1bd34..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$ -** -****************************************************************************/ +// 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,6 +46,9 @@ private slots: void grownOrShrunkBy_data(); void grownOrShrunkBy(); + void toSizeF_data(); + void toSizeF(); + void transpose_data(); void transpose(); @@ -232,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"); diff --git a/tests/auto/corelib/tools/qsizef/CMakeLists.txt b/tests/auto/corelib/tools/qsizef/CMakeLists.txt index eb59fc6d8e..9adaafe2ea 100644 --- a/tests/auto/corelib/tools/qsizef/CMakeLists.txt +++ b/tests/auto/corelib/tools/qsizef/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qsizef.pro. +# 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/tst_qsizef.cpp b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp index 3a65506dee..ee33fa13b6 100644 --- a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp +++ b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp @@ -1,30 +1,27 @@ -/**************************************************************************** -** -** 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 <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> 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 index 49b209cffa..b2f053e6ce 100644 --- a/tests/auto/corelib/tools/qstl/CMakeLists.txt +++ b/tests/auto/corelib/tools/qstl/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qstl.pro. +# 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/tst_qstl.cpp b/tests/auto/corelib/tools/qstl/tst_qstl.cpp index 1cd74ad305..43d40bc128 100644 --- a/tests/auto/corelib/tools/qstl/tst_qstl.cpp +++ b/tests/auto/corelib/tools/qstl/tst_qstl.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 <QTest> diff --git a/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt b/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt index 7f3ae75028..fb2e5dc922 100644 --- a/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt +++ b/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qtaggedpointer.pro. +# 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 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp b/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp index dcc966fc2f..a1e61fc3a1 100644 --- a/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp +++ b/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 <QTest> #include <QtCore/qtaggedpointer.h> @@ -36,6 +11,7 @@ class tst_QTaggedPointer : public QObject private Q_SLOTS: void constExpr(); void construction(); + void assignment(); void dereferenceOperator(); void pointerOperator(); void negationOperator(); @@ -105,6 +81,47 @@ void tst_QTaggedPointer::construction() } } +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: diff --git a/tests/auto/corelib/tools/qtimeline/CMakeLists.txt b/tests/auto/corelib/tools/qtimeline/CMakeLists.txt index de61529fd2..a43e93990a 100644 --- a/tests/auto/corelib/tools/qtimeline/CMakeLists.txt +++ b/tests/auto/corelib/tools/qtimeline/CMakeLists.txt @@ -1,10 +1,19 @@ -# Generated from qtimeline.pro. +# 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/tst_qtimeline.cpp b/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp index 0869af31f7..3593a65c4e 100644 --- a/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp +++ b/tests/auto/corelib/tools/qtimeline/tst_qtimeline.cpp @@ -1,32 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 <QTest> +#include <QtTest/private/qpropertytesthelper_p.h> #include <QSignalSpy> #include <qtimeline.h> @@ -37,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(); @@ -58,6 +39,7 @@ private slots: void resume(); void restart(); void setPaused(); + void automatedBindableTests(); protected slots: void finishedSlot(); @@ -98,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); } @@ -120,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(); @@ -144,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); @@ -161,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; @@ -176,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); @@ -185,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() @@ -200,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); @@ -249,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(); @@ -264,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); @@ -279,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); + QProperty<QEasingCurve> easingCurveObserver([&]() { return timeLine.easingCurve(); }); + QCOMPARE(timeLine.easingCurve(), QEasingCurve::InOutSine); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::InOutSine); + timeLine.setFrameRange(100, 200); - timeLine.setEasingCurve(QEasingCurve::Linear); + QProperty<QEasingCurve> referenceEasingCurve(QEasingCurve::Linear); + timeLine.bindableEasingCurve().setBinding([&]() { return referenceEasingCurve.value(); }); QCOMPARE(timeLine.easingCurve(), QEasingCurve::Linear); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::Linear); // smooth - timeLine.setEasingCurve(QEasingCurve::InOutSine); + referenceEasingCurve = QEasingCurve::InOutSine; + QCOMPARE(timeLine.easingCurve(), QEasingCurve::InOutSine); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::InOutSine); + timeLine.start(); QTest::qWait(100); QCOMPARE(timeLine.state(), QTimeLine::Running); @@ -324,7 +502,11 @@ void tst_QTimeLine::interpolation() timeLine.setCurrentTime(0); // linear - timeLine.setEasingCurve(QEasingCurve::Linear); + referenceEasingCurve = QEasingCurve::Linear; + + QCOMPARE(timeLine.easingCurve(), QEasingCurve::Linear); + QCOMPARE(easingCurveObserver.value(), QEasingCurve::Linear); + timeLine.start(); QTest::qWait(100); QCOMPARE(timeLine.state(), QTimeLine::Running); @@ -407,14 +589,38 @@ 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() @@ -430,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); @@ -446,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() @@ -459,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); } @@ -475,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() @@ -514,7 +720,7 @@ 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() @@ -660,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 index bdc927d5b6..eccb2634cc 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/CMakeLists.txt +++ b/tests/auto/corelib/tools/qvarlengtharray/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qvarlengtharray.pro. +# 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 diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index ab3b46fc90..6a92663bc4 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -1,36 +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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QTest> -#include <qvarlengtharray.h> +#include <QVarLengthArray> #include <qvariant.h> #include <qscopeguard.h> +#include <qscopedvaluerollback.h> +#include <algorithm> +#include <q20iterator.h> #include <memory> struct Tracker @@ -64,11 +42,35 @@ public: { 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>(); } @@ -81,8 +83,10 @@ private slots: void removeLast(); void oldTests(); void appendCausingRealloc(); + void appendIsStronglyExceptionSafe(); void resize(); void realloc(); + void iterators(); void reverseIterators(); void count(); void cpp17ctad(); @@ -100,8 +104,21 @@ 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> @@ -114,6 +131,48 @@ private: void initializeList(); }; +template <typename T> +void tst_QVarLengthArray::defaultConstructor() +{ + { + 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 + } +} + +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() { QVarLengthArray<QString, 2> v; @@ -136,6 +195,84 @@ 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() { @@ -305,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); @@ -408,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) {} @@ -482,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; @@ -497,6 +747,15 @@ 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; }; @@ -704,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; @@ -724,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()); } { @@ -751,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 @@ -762,27 +1070,30 @@ 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::fromType<decltype(obj)::value_type>().name()) @@ -802,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() @@ -818,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() @@ -840,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); @@ -889,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); @@ -921,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); @@ -945,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); @@ -968,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"))); @@ -983,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"; @@ -1062,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; @@ -1087,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)); @@ -1159,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()); @@ -1193,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/qversionnumber/CMakeLists.txt b/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt index 2ab3703121..8f6ed66841 100644 --- a/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt +++ b/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qversionnumber.pro. +# 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 diff --git a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp index b0dbcb042d..da9dcc9366 100644 --- a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp +++ b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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> @@ -73,18 +48,19 @@ 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(); void qtVersion(); - void qTypeRevision_data(); - void qTypeRevision(); - void qTypeRevisionTypes(); - void qTypeRevisionComparison(); }; void tst_QVersionNumber::singleInstanceData() @@ -268,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() @@ -511,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); @@ -523,6 +504,46 @@ 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() @@ -558,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(); @@ -649,153 +709,6 @@ void tst_QVersionNumber::qtVersion() QCOMPARE(v.toString(), QString(qVersion())); } -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_QVersionNumber::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_QVersionNumber::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_QVersionNumber::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_QVersionNumber::qTypeRevisionComparison() -{ - 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 = 0; j < length; ++j) { - QCOMPARE(revisions[i] == revisions[j], i == j); - QCOMPARE(revisions[i] != revisions[j], i != j); - QCOMPARE(revisions[i] < revisions[j], i < j); - QCOMPARE(revisions[i] > revisions[j], i > j); - QCOMPARE(revisions[i] <= revisions[j], i <= j); - QCOMPARE(revisions[i] >= revisions[j], i >= j); - } - } -} - QTEST_APPLESS_MAIN(tst_QVersionNumber) #include "tst_qversionnumber.moc" |