diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp | 2789 |
1 files changed, 2192 insertions, 597 deletions
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index aeac08fc34..23d41cafb2 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -1,73 +1,149 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com> -** 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$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com> +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qvariant.h> -#include <QtCore/private/qvariant_p.h> -#include <qbitarray.h> -#include <qbytearraylist.h> -#include <qdatetime.h> -#include <qmap.h> -#include <qiodevice.h> -#include <qurl.h> -#include <qlocale.h> -#include <qdebug.h> -#include <qjsondocument.h> -#include <quuid.h> - -#include <limits.h> + +// 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_IMPL(func, arg, Variant, cvref, R) \ + static_assert(my_is_same_v<decltype( func < arg >(std::declval< Variant cvref >())), R cvref >) + +#define CHECK_GET_IF(Variant, cvref) \ + CHECK_IMPL(get_if, int, Variant, cvref *, int) + +#define CHECK_GET(Variant, cvref) \ + CHECK_IMPL(get, int, Variant, cvref, int) + +CHECK_GET_IF(QVariant, /* unadorned */); +CHECK_GET_IF(QVariant, const); + +CHECK_GET(QVariant, &); +CHECK_GET(QVariant, const &); +CHECK_GET(QVariant, &&); +CHECK_GET(QVariant, const &&); + +// check for a type derived from QVariant: + +struct MyVariant : QVariant +{ + using QVariant::QVariant; +}; + +CHECK_GET_IF(MyVariant, /* unadorned */); +CHECK_GET_IF(MyVariant, const); + +CHECK_GET(MyVariant, &); +CHECK_GET(MyVariant, const &); +CHECK_GET(MyVariant, &&); +CHECK_GET(MyVariant, const &&); + +#undef CHECK_GET_IF +#undef CHECK_GET +#undef CHECK_IMPL + +#include <QTest> + +// Please stick to alphabetic order. +#include <QAssociativeIterable> +#include <QBitArray> +#include <QBuffer> +#include <QByteArrayList> +#include <QDateTime> +#include <QDebug> +#include <QDir> +#include <QEasingCurve> +#include <QMap> +#include <QIODevice> +#include <QHash> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QLocale> +#include <QQueue> +#include <QRegularExpression> +#include <QScopeGuard> +#include <QSequentialIterable> +#include <QSet> +#include <QStack> +#include <QTimeZone> +#include <QtNumeric> +#include <QUrl> +#include <QUuid> + +#include <private/qcomparisontesthelper_p.h> +#include <private/qlocale_p.h> +#include <private/qmetatype_p.h> +#include "tst_qvariant_common.h" + +#include <limits> #include <float.h> #include <cmath> -#if __has_include(<variant>) && __cplusplus >= 201703L #include <variant> -#endif -#include <QRegularExpression> -#include <QDir> -#include <QBuffer> -#include "qnumeric.h" +#include <unordered_map> -#include <private/qlocale_p.h> -#include "tst_qvariant_common.h" +using namespace Qt::StringLiterals; class CustomNonQObject; +struct NonDefaultConstructible; + +template<typename T, typename = void> +struct QVariantFromValueCompiles +{ + static inline constexpr bool value = false; +}; + +template<typename T> +struct QVariantFromValueCompiles<T, std::void_t<decltype (QVariant::fromValue(std::declval<T>()))>> +{ + static inline constexpr bool value = true; +}; + +static_assert(QVariantFromValueCompiles<int>::value); +static_assert(!QVariantFromValueCompiles<QObject>::value); + +enum EnumTest_Enum0 { EnumTest_Enum0_value = 42, EnumTest_Enum0_negValue = -8 }; +Q_DECLARE_METATYPE(EnumTest_Enum0) +enum EnumTest_Enum1 : qint64 { EnumTest_Enum1_value = 42, EnumTest_Enum1_bigValue = (Q_INT64_C(1) << 33) + 50 }; +Q_DECLARE_METATYPE(EnumTest_Enum1) + +enum EnumTest_Enum3 : qint64 { EnumTest_Enum3_value = -47, EnumTest_Enum3_bigValue = (Q_INT64_C(1) << 56) + 5 }; +Q_DECLARE_METATYPE(EnumTest_Enum3) +enum EnumTest_Enum4 : quint64 { EnumTest_Enum4_value = 47, EnumTest_Enum4_bigValue = (Q_INT64_C(1) << 52) + 45 }; +Q_DECLARE_METATYPE(EnumTest_Enum4) +enum EnumTest_Enum5 : uint { EnumTest_Enum5_value = 47 }; +Q_DECLARE_METATYPE(EnumTest_Enum5) +enum EnumTest_Enum6 : uchar { EnumTest_Enum6_value = 47 }; +Q_DECLARE_METATYPE(EnumTest_Enum6) +enum class EnumTest_Enum7 { EnumTest_Enum7_value = 47, ensureSignedEnum7 = -1 }; +Q_DECLARE_METATYPE(EnumTest_Enum7) +enum EnumTest_Enum8 : short { EnumTest_Enum8_value = 47 }; +Q_DECLARE_METATYPE(EnumTest_Enum8) + +template <typename T> int qToUnderlying(QFlags<T> f) +{ + return f.toInt(); +} class tst_QVariant : public QObject { Q_OBJECT + static void runTestFunction() + { + QFETCH(QFunctionPointer, testFunction); + testFunction(); + } + public: - tst_QVariant(QObject *parent = 0) + tst_QVariant(QObject *parent = nullptr) : QObject(parent), customNonQObjectPointer(0) { - } @@ -100,6 +176,13 @@ private slots: void canConvert_data(); void canConvert(); + void canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue_data(); + void canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue(); + + void canConvertAndConvert_ReturnFalse_WhenConvertingQObjectBetweenPointerAndValue(); + + void convert(); + void toSize_data(); void toSize(); @@ -168,6 +251,7 @@ private slots: void qvariant_cast_QObject_derived(); void qvariant_cast_QObject_wrapper(); void qvariant_cast_QSharedPointerQObject(); + void qvariant_cast_const(); void toLocale(); @@ -188,9 +272,11 @@ private slots: void operator_eq_eq_data(); void operator_eq_eq(); +#if QT_DEPRECATED_SINCE(6, 0) void typeName_data(); void typeName(); void typeToName(); +#endif void streamInvalidVariant(); @@ -205,6 +291,9 @@ private slots: void variantHash(); void convertToQUint8() const; + void compareCompiles() const; + void compareNumerics_data() const; + void compareNumerics() const; void comparePointers() const; void voidStar() const; void dataStar() const; @@ -218,14 +307,17 @@ private slots: void loadBrokenUserType(); void invalidDate() const; + void compareCustomTypes_data() const; void compareCustomTypes() const; void timeToDateTime() const; void copyingUserTypes() const; + void valueClassHierarchyConversion() const; void convertBoolToByteArray() const; void convertBoolToByteArray_data() const; void convertByteArrayToBool() const; void convertByteArrayToBool_data() const; void convertIterables() const; + void convertConstNonConst() const; void toIntFromQString() const; void toIntFromDouble() const; void setValue(); @@ -243,8 +335,10 @@ private slots: void forwardDeclare(); void debugStream_data(); void debugStream(); +#if QT_DEPRECATED_SINCE(6, 0) void debugStreamType_data(); void debugStreamType(); +#endif void loadQt4Stream_data(); void loadQt4Stream(); @@ -260,11 +354,18 @@ private slots: void implicitConstruction(); + void iterateSequentialContainerElements_data(); + void iterateSequentialContainerElements() { runTestFunction(); } + void iterateAssociativeContainerElements_data(); + void iterateAssociativeContainerElements() { runTestFunction(); } void iterateContainerElements(); - void pairElements(); + void pairElements_data(); + void pairElements() { runTestFunction(); } - void enums(); - void metaEnums(); + void enums_data(); + void enums() { runTestFunction(); } + void metaEnums_data(); + void metaEnums() { runTestFunction(); } void nullConvert(); @@ -277,8 +378,43 @@ private slots: void sequentialIterableAppend(); void preferDirectConversionOverInterfaces(); + void mutableView(); + + void canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue(); + + void moveOperations(); + void equalsWithoutMetaObject(); + + void constructFromIncompatibleMetaType_data(); + void constructFromIncompatibleMetaType(); + void constructFromQtLT65MetaType(); + void copyNonDefaultConstructible(); + + void inplaceConstruct(); + void emplace(); + + void getIf_int() { getIf_impl(42); } + void getIf_QString() { getIf_impl(u"string"_s); }; + void getIf_NonDefaultConstructible(); + void getIfSpecial(); + + void get_int() { get_impl(42); } + void get_QString() { get_impl(u"string"_s); } + void get_NonDefaultConstructible(); private: + using StdVariant = std::variant<std::monostate, + // list here all the types with which we instantiate getIf_impl: + int, + QString, + NonDefaultConstructible + >; + template <typename T> + void getIf_impl(T t) const; + template <typename T> + void get_impl(T t) const; + template<typename T> + void canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl(const QByteArray &typeName); void dataStream_data(QDataStream::Version version); void loadQVariantFromDataStream(QDataStream::Version version); void saveQVariantFromDataStream(QDataStream::Version version); @@ -304,22 +440,32 @@ void tst_QVariant::constructor() QVariant varll2(varll); QCOMPARE(varll2, varll); - QVariant var3(QVariant::String); + QVariant var3 {QMetaType::fromType<QString>()}; QCOMPARE(var3.typeName(), "QString"); QVERIFY(var3.isNull()); QVERIFY(var3.isValid()); - QVariant var4(QVariant::Invalid); - QCOMPARE(var4.type(), QVariant::Invalid); + QVariant var3a = QVariant::fromMetaType(QMetaType::fromType<QString>()); + QCOMPARE(var3a.typeName(), "QString"); + QVERIFY(var3a.isNull()); + QVERIFY(var3a.isValid()); + + QVariant var4 {QMetaType()}; + QCOMPARE(var4.typeId(), QMetaType::UnknownType); QVERIFY(var4.isNull()); QVERIFY(!var4.isValid()); + QVariant var4a = QVariant::fromMetaType(QMetaType()); + QCOMPARE(var4a.typeId(), QMetaType::UnknownType); + QVERIFY(var4a.isNull()); + QVERIFY(!var4a.isValid()); + QVariant var5(QLatin1String("hallo")); - QCOMPARE(var5.type(), QVariant::String); + QCOMPARE(var5.typeId(), QMetaType::QString); QCOMPARE(var5.typeName(), "QString"); QVariant var6(qlonglong(0)); - QCOMPARE(var6.type(), QVariant::LongLong); + QCOMPARE(var6.typeId(), QMetaType::LongLong); QCOMPARE(var6.typeName(), "qlonglong"); QVariant var7 = 5; @@ -348,10 +494,18 @@ void tst_QVariant::constructor_invalid() QFETCH(uint, typeId); { QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type")); - QVariant variant(static_cast<QVariant::Type>(typeId)); + QVariant variant {QMetaType(typeId)}; QVERIFY(!variant.isValid()); QVERIFY(variant.isNull()); - QCOMPARE(variant.type(), int(QMetaType::UnknownType)); + QCOMPARE(variant.typeId(), int(QMetaType::UnknownType)); + QCOMPARE(variant.userType(), int(QMetaType::UnknownType)); + } + { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type")); + QVariant variant = QVariant::fromMetaType(QMetaType(typeId)); + QVERIFY(!variant.isValid()); + QVERIFY(variant.isNull()); + QCOMPARE(variant.typeId(), int(QMetaType::UnknownType)); QCOMPARE(variant.userType(), int(QMetaType::UnknownType)); } { @@ -365,9 +519,9 @@ void tst_QVariant::constructor_invalid() void tst_QVariant::copy_constructor() { - QVariant var7(QVariant::Int); + QVariant var7 {QMetaType::fromType<int>()}; QVariant var8(var7); - QCOMPARE(var8.type(), QVariant::Int); + QCOMPARE(var8.typeId(), QMetaType::Int); QVERIFY(var8.isNull()); } @@ -378,14 +532,16 @@ void tst_QVariant::isNull() QVariant var; QVERIFY( var.isNull() ); - QString str1; - QVariant var1( str1 ); - QVERIFY( !var1.isNull() ); + QVariant empty = QString(); + QVERIFY(empty.toString().isNull()); + QVERIFY(!empty.isNull()); + QVERIFY(empty.isValid()); + QCOMPARE(empty.typeName(), "QString"); QVariant var3( QString( "blah" ) ); QVERIFY( !var3.isNull() ); - var3 = QVariant(QVariant::String); + var3 = QVariant(QMetaType::fromType<QString>()); QVERIFY( var3.isNull() ); QVariant var4( 0 ); @@ -398,12 +554,12 @@ void tst_QVariant::isNull() QVERIFY( !var6.isNull() ); var6 = QVariant(); QVERIFY( var6.isNull() ); - var6.convert( QVariant::String ); + var6.convert(QMetaType::fromType<QString>()); QVERIFY( var6.isNull() ); QVariant varLL( (qlonglong)0 ); QVERIFY( !varLL.isNull() ); - QVariant var8(QMetaType(QMetaType::Nullptr), nullptr); + QVariant var8(QMetaType::fromType<std::nullptr_t>(), nullptr); QVERIFY(var8.isNull()); var8 = QVariant::fromValue<std::nullptr_t>(nullptr); QVERIFY(var8.isNull()); @@ -412,12 +568,12 @@ void tst_QVariant::isNull() var9 = QVariant::fromValue<QJsonValue>(QJsonValue(QJsonValue::Null)); QVERIFY(!var9.isNull()); - QVariant var10(QMetaType(QMetaType::VoidStar), nullptr); + QVariant var10(QMetaType::fromType<void*>(), nullptr); QVERIFY(var10.isNull()); var10 = QVariant::fromValue<void*>(nullptr); QVERIFY(var10.isNull()); - QVariant var11(QMetaType(QMetaType::QObjectStar), nullptr); + QVariant var11(QMetaType::fromType<QObject*>(), nullptr); QVERIFY(var11.isNull()); var11 = QVariant::fromValue<QObject*>(nullptr); QVERIFY(var11.isNull()); @@ -432,9 +588,9 @@ void tst_QVariant::swap() { QVariant v1 = 1, v2 = 2.0; v1.swap(v2); - QCOMPARE(v1.type(),QVariant::Double); + QCOMPARE(v1.typeId(), QMetaType::Double); QCOMPARE(v1.toDouble(),2.0); - QCOMPARE(v2.type(),QVariant::Int); + QCOMPARE(v2.typeId(), QMetaType::Int); QCOMPARE(v2.toInt(),1); } @@ -459,7 +615,7 @@ void tst_QVariant::canConvert_data() << var << Y << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N; var = QVariant(QByteArray()); QTest::newRow("ByteArray") - << var << N << N << Y << N << Y << Y << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; + << var << N << N << Y << N << Y << Y << N << N << N << Y << N << N << Y << N << N << Y << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; var = QVariant(QDate()); QTest::newRow("Date") << var << N << N << N << N << N << N << N << Y << Y << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N; @@ -477,7 +633,7 @@ void tst_QVariant::canConvert_data() << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; var = QVariant(); QTest::newRow("Invalid") - << var << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N; + << var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N; var = QVariant(QList<QVariant>()); QTest::newRow("List") << var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N; @@ -498,7 +654,7 @@ void tst_QVariant::canConvert_data() << var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N; var = QVariant(QString()); QTest::newRow("String") - << var << N << N << Y << N << Y << Y << N << Y << Y << Y << Y << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << Y << Y << Y << Y; + << var << N << N << Y << N << Y << Y << N << Y << Y << Y << Y << N << Y << N << Y << Y << Y << N << N << N << N << N << N << N << N << N << Y << Y << Y << Y << Y; var = QVariant(QStringList("entry")); QTest::newRow("StringList") << var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N << N << N << N << N << Y << Y << N << N << N; @@ -508,9 +664,6 @@ void tst_QVariant::canConvert_data() var = QVariant((uint)1); QTest::newRow("UInt") << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; - var = QVariant((int)1); - QTest::newRow("Int") - << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; var = QVariant((qulonglong)1); QTest::newRow("ULongLong") << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; @@ -520,12 +673,12 @@ void tst_QVariant::canConvert_data() var = QVariant::fromValue<signed char>(-1); QTest::newRow("SChar") << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; - var = QVariant((short)-3); + var = QVariant::fromValue((short)-3); QTest::newRow("Short") - << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; - var = QVariant((ushort)7); + << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; + var = QVariant::fromValue((ushort)7); QTest::newRow("UShort") - << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; + << var << N << N << Y << N << Y << N << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; var = QVariant::fromValue<QJsonValue>(QJsonValue(QStringLiteral("hello"))); QTest::newRow("JsonValue") << var << N << N << Y << N << N << N << N << N << N << Y << N << N << Y << N << N << Y << Y << Y << N << N << N << N << N << N << N << N << Y << N << N << Y << Y; @@ -544,13 +697,131 @@ void tst_QVariant::canConvert() { TST_QVARIANT_CANCONVERT_FETCH_DATA + // This test links against QtGui but not QtWidgets, so QSizePolicy isn't real for it. + QTest::ignoreMessage(QtWarningMsg, // QSizePolicy's id is 0x2000, a.k.a. 8192 + "Trying to construct an instance of an invalid type, type id: 8192"); TST_QVARIANT_CANCONVERT_COMPARE_DATA +#if QT_DEPRECATED_SINCE(6, 0) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED // Invalid type ids + QTest::ignoreMessage(QtWarningMsg, + "Trying to construct an instance of an invalid type, type id: -1"); QCOMPARE(val.canConvert(-1), false); + QTest::ignoreMessage(QtWarningMsg, + "Trying to construct an instance of an invalid type, type id: -23"); QCOMPARE(val.canConvert(-23), false); + QTest::ignoreMessage(QtWarningMsg, + "Trying to construct an instance of an invalid type, type id: -23876"); QCOMPARE(val.canConvert(-23876), false); + QTest::ignoreMessage(QtWarningMsg, + "Trying to construct an instance of an invalid type, type id: 23876"); QCOMPARE(val.canConvert(23876), false); +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 0) +} + +namespace { + +// Used for testing canConvert/convert of QObject derived types +struct QObjectDerived : QObject +{ + Q_OBJECT +}; + +// Adds a test table row for checking value <-> pointer conversion +// If type is a pointer, the target type is value type and vice versa. +template<typename T> +void addRowForPointerValueConversion() +{ + using ValueType = std::remove_pointer_t<T>; + if constexpr (!std::is_same_v<ValueType, std::nullptr_t>) { + + static ValueType instance{}; // static since we may need a pointer to a valid object + + QVariant variant; + if constexpr (std::is_pointer_v<T>) + variant = QVariant::fromValue(&instance); + else + variant = QVariant::fromValue(instance); + + // Toggle pointer/value type + using TargetType = std::conditional_t<std::is_pointer_v<T>, ValueType, T *>; + + const QMetaType fromType = QMetaType::fromType<T>(); + const QMetaType toType = QMetaType::fromType<TargetType>(); + + QTest::addRow("%s->%s", fromType.name(), toType.name()) + << variant << QMetaType::fromType<TargetType>(); + } +} + +} // namespace + +void tst_QVariant::canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue_data() +{ + QTest::addColumn<QVariant>("variant"); + QTest::addColumn<QMetaType>("targetType"); + +#define ADD_ROW(typeName, typeNameId, realType) \ + addRowForPointerValueConversion<realType>(); \ + addRowForPointerValueConversion<realType *>(); + + // Add rows for static primitive types + QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ADD_ROW) + + // Add rows for static core types + QT_FOR_EACH_STATIC_CORE_CLASS(ADD_ROW) +#undef ADD_ROW + +} + +void tst_QVariant::canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue() +{ + QFETCH(QVariant, variant); + QFETCH(QMetaType, targetType); + + QVERIFY(!variant.canConvert(targetType)); + + QVERIFY(!variant.convert(targetType)); + + // As per the documentation, when QVariant::convert fails, the + // QVariant is cleared and changed to the requested type. + QVERIFY(variant.isNull()); + QCOMPARE(variant.metaType(), targetType); +} + +void tst_QVariant::canConvertAndConvert_ReturnFalse_WhenConvertingQObjectBetweenPointerAndValue() +{ + // Types derived from QObject are non-copyable and require their own test. + // We only test pointer -> value conversion, because constructing a QVariant + // from a non-copyable object will just set the QVariant to null. + + QObjectDerived object; + QVariant variant = QVariant::fromValue(&object); + + constexpr QMetaType targetType = QMetaType::fromType<QObjectDerived>(); + QVERIFY(!variant.canConvert(targetType)); + + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression(".*does not support destruction and copy construction")); + + QVERIFY(!variant.convert(targetType)); + + // When the QVariant::convert fails, the QVariant is cleared, and since the target type is + // invalid for QVariant, the QVariant's type is also cleared to an unknown type. + QVERIFY(variant.isNull()); + QCOMPARE(variant.metaType(), QMetaType()); +} + +void tst_QVariant::convert() +{ + // verify that after convert(), the variant's type has been changed + QVariant var = QVariant::fromValue(QString("A string")); + var.convert(QMetaType::fromType<int>()); + QCOMPARE(var.metaType(), QMetaType::fromType<int>()); + QCOMPARE(var.toInt(), 0); } void tst_QVariant::toInt_data() @@ -596,12 +867,19 @@ void tst_QVariant::toInt_data() QTest::newRow("undefined-QJsonValue") << QVariant(QJsonValue(QJsonValue::Undefined)) << 0 << false; } +#if QT_DEPRECATED_SINCE(6, 0) +# define EXEC_DEPRECATED_CALL(x) QT_IGNORE_DEPRECATIONS(x) +#else +# define EXEC_DEPRECATED_CALL(x) +#endif + void tst_QVariant::toInt() { QFETCH( QVariant, value ); QFETCH( int, result ); QFETCH( bool, valueOK ); - QVERIFY( value.isValid() == value.canConvert( QVariant::Int ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.isValid() == value.canConvert( QVariant::Int ) );) + QVERIFY( value.isValid() == value.canConvert(QMetaType::fromType<int>()) ); bool ok; int i = value.toInt( &ok ); QCOMPARE( i, result ); @@ -650,7 +928,8 @@ void tst_QVariant::toUInt() QFETCH( uint, result ); QFETCH( bool, valueOK ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::UInt ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::UInt ) );) + QVERIFY( value.canConvert(QMetaType::fromType<uint>()) ); bool ok; uint i = value.toUInt( &ok ); @@ -674,7 +953,8 @@ void tst_QVariant::toSize() QFETCH( QVariant, value ); QFETCH( QSize, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Size ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Size ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QSize>()) ); QSize i = value.toSize(); QCOMPARE( i, result ); @@ -695,7 +975,8 @@ void tst_QVariant::toSizeF() QFETCH( QVariant, value ); QFETCH( QSizeF, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::SizeF ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::SizeF ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QSizeF>()) ); QSizeF i = value.toSizeF(); QCOMPARE( i, result ); @@ -716,7 +997,8 @@ void tst_QVariant::toLine() QFETCH( QVariant, value ); QFETCH( QLine, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Line ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Line ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QLine>()) ); QLine i = value.toLine(); QCOMPARE( i, result ); @@ -737,7 +1019,8 @@ void tst_QVariant::toLineF() QFETCH( QVariant, value ); QFETCH( QLineF, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::LineF ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::LineF ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QLineF>()) ); QLineF i = value.toLineF(); QCOMPARE( i, result ); @@ -759,7 +1042,8 @@ void tst_QVariant::toPoint() QFETCH( QVariant, value ); QFETCH( QPoint, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Point ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Point ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QPoint>()) ); QPoint i = value.toPoint(); QCOMPARE( i, result ); } @@ -780,7 +1064,8 @@ void tst_QVariant::toRect() QFETCH( QVariant, value ); QFETCH( QRect, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Rect ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Rect ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QRect>()) ); QRect i = value.toRect(); QCOMPARE( i, result ); } @@ -798,7 +1083,8 @@ void tst_QVariant::toChar() QFETCH( QVariant, value ); QFETCH( QChar, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Char ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Char ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QChar>()) ); QChar i = value.toChar(); QCOMPARE( i, result ); @@ -836,7 +1122,8 @@ void tst_QVariant::toBool() QFETCH( QVariant, value ); QFETCH( bool, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Bool ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Bool ) );) + QVERIFY( value.canConvert(QMetaType::fromType<bool>()) ); bool i = value.toBool(); QCOMPARE( i, result ); @@ -855,7 +1142,8 @@ void tst_QVariant::toPointF() QFETCH( QVariant, value ); QFETCH( QPointF, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::PointF ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::PointF ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QPointF>()) ); QPointF d = value.toPointF(); QCOMPARE( d, result ); } @@ -875,7 +1163,8 @@ void tst_QVariant::toRectF() QFETCH( QVariant, value ); QFETCH( QRectF, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::RectF ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::RectF ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QRectF>()) ); QRectF d = value.toRectF(); QCOMPARE( d, result ); } @@ -902,7 +1191,8 @@ void tst_QVariant::toDouble() QFETCH( double, result ); QFETCH( bool, valueOK ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Double ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Double ) );) + QVERIFY( value.canConvert(QMetaType::fromType<double>()) ); bool ok; double d = value.toDouble( &ok ); QCOMPARE( d, result ); @@ -931,7 +1221,8 @@ void tst_QVariant::toFloat() QFETCH(float, result); QFETCH(bool, valueOK); QVERIFY(value.isValid()); - QVERIFY(value.canConvert(QMetaType::Float)); + EXEC_DEPRECATED_CALL(QVERIFY(value.canConvert(QMetaType::Float));) + QVERIFY(value.canConvert(QMetaType::fromType<float>())); bool ok; float d = value.toFloat(&ok); QCOMPARE(d, result); @@ -982,7 +1273,8 @@ void tst_QVariant::toLongLong() QFETCH( qlonglong, result ); QFETCH( bool, valueOK ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::LongLong ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::LongLong ) );) + QVERIFY( value.canConvert(QMetaType::fromType<qlonglong>()) ); bool ok; qlonglong ll = value.toLongLong( &ok ); QCOMPARE( ll, result ); @@ -1037,7 +1329,8 @@ void tst_QVariant::toULongLong() QFETCH( qulonglong, result ); QFETCH( bool, valueOK ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::ULongLong ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::ULongLong ) );) + QVERIFY( value.canConvert(QMetaType::fromType<qulonglong>()) ); bool ok; qulonglong ll = value.toULongLong( &ok ); QCOMPARE( ll, result ); @@ -1077,12 +1370,13 @@ void tst_QVariant::toByteArray() QFETCH( QVariant, value ); QFETCH( QByteArray, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::ByteArray ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::ByteArray ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QByteArray>()) ); QByteArray ba = value.toByteArray(); QCOMPARE( ba.isNull(), result.isNull() ); QCOMPARE( ba, result ); - QVERIFY( value.convert( QVariant::ByteArray ) ); + QVERIFY( value.convert(QMetaType::fromType<QByteArray>()) ); QCOMPARE( value.toByteArray(), result ); } @@ -1119,12 +1413,13 @@ void tst_QVariant::toString() QFETCH( QVariant, value ); QFETCH( QString, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::String ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::String ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QString>()) ); QString str = value.toString(); QCOMPARE( str.isNull(), result.isNull() ); QCOMPARE( str, result ); - QVERIFY( value.convert( QVariant::String ) ); + QVERIFY( value.convert(QMetaType::fromType<QString>()) ); QCOMPARE( value.toString(), result ); } @@ -1143,7 +1438,8 @@ void tst_QVariant::toDate() QFETCH( QVariant, value ); QFETCH( QDate, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Date ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Date ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QDate>()) ); QCOMPARE( value.toDate(), result ); } @@ -1163,7 +1459,8 @@ void tst_QVariant::toTime() QFETCH( QVariant, value ); QFETCH( QTime, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::Time ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::Time ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QTime>()) ); QCOMPARE( value.toTime(), result ); } @@ -1176,8 +1473,9 @@ void tst_QVariant::toDateTime_data() << QDateTime( QDate( 2002, 10, 10 ), QTime( 12, 34, 56 ) ); QTest::newRow( "qdate" ) << QVariant( QDate( 2002, 10, 10 ) ) << QDateTime( QDate( 2002, 10, 10 ), QTime( 0, 0, 0 ) ); QTest::newRow( "qstring" ) << QVariant( QString( "2002-10-10T12:34:56" ) ) << QDateTime( QDate( 2002, 10, 10 ), QTime( 12, 34, 56 ) ); - QTest::newRow( "qstring-utc" ) << QVariant( QString( "2002-10-10T12:34:56Z" ) ) - << QDateTime( QDate( 2002, 10, 10 ), QTime( 12, 34, 56 ), Qt::UTC ); + QTest::newRow("qstring-utc") + << QVariant(QString("2002-10-10T12:34:56Z")) + << QDateTime(QDate(2002, 10, 10), QTime(12, 34, 56), QTimeZone::UTC); QTest::newRow( "qstring-with-ms" ) << QVariant( QString( "2002-10-10T12:34:56.789" ) ) << QDateTime( QDate( 2002, 10, 10 ), QTime( 12, 34, 56, 789 ) ); } @@ -1187,10 +1485,13 @@ void tst_QVariant::toDateTime() QFETCH( QVariant, value ); QFETCH( QDateTime, result ); QVERIFY( value.isValid() ); - QVERIFY( value.canConvert( QVariant::DateTime ) ); + EXEC_DEPRECATED_CALL(QVERIFY( value.canConvert( QVariant::DateTime ) );) + QVERIFY( value.canConvert(QMetaType::fromType<QDateTime>()) ); QCOMPARE( value.toDateTime(), result ); } +#undef EXEC_DEPRECATED_CALL + void tst_QVariant::toLocale() { QVariant variant; @@ -1222,7 +1523,6 @@ struct CustomStreamableClass return i == other.i; } }; -Q_DECLARE_METATYPE(CustomStreamableClass); QDataStream &operator<<(QDataStream &out, const CustomStreamableClass &myObj) { @@ -1233,11 +1533,10 @@ QDataStream &operator>>(QDataStream &in, CustomStreamableClass &myObj) { return in >> myObj.i; } +Q_DECLARE_METATYPE(CustomStreamableClass); void tst_QVariant::writeToReadFromDataStream_data() { - qRegisterMetaTypeStreamOperators<CustomStreamableClass>(); - QTest::addColumn<QVariant>("writeVariant"); QTest::addColumn<bool>("isNull"); { @@ -1248,16 +1547,16 @@ void tst_QVariant::writeToReadFromDataStream_data() } QTest::newRow( "invalid" ) << QVariant() << true; - QTest::newRow( "bitarray_invalid" ) << QVariant(QVariant::BitArray) << true; + QTest::newRow( "bitarray_invalid" ) << QVariant(QMetaType::fromType<QBitArray>()) << true; QTest::newRow( "bitarray_empty" ) << QVariant( QBitArray() ) << false; QBitArray bitarray( 3 ); bitarray[0] = 0; bitarray[1] = 1; bitarray[2] = 0; QTest::newRow( "bitarray_valid" ) << QVariant( bitarray ) << false; - QTest::newRow( "bytearray_invalid" ) << QVariant(QVariant::ByteArray) << true; + QTest::newRow( "bytearray_invalid" ) << QVariant(QMetaType::fromType<QByteArray>()) << true; QTest::newRow( "bytearray_empty" ) << QVariant( QByteArray() ) << false; - QTest::newRow( "int_invalid") << QVariant(QVariant::Int) << true; + QTest::newRow( "int_invalid") << QVariant(QMetaType::fromType<int>()) << true; QByteArray bytearray(5, ' '); bytearray[0] = 'T'; bytearray[1] = 'e'; @@ -1265,10 +1564,10 @@ void tst_QVariant::writeToReadFromDataStream_data() bytearray[3] = 't'; bytearray[4] = '\0'; QTest::newRow( "bytearray_valid" ) << QVariant( bytearray ) << false; - QTest::newRow( "date_invalid" ) << QVariant(QVariant::Date) << true; + QTest::newRow( "date_invalid" ) << QVariant(QMetaType::fromType<QDate>()) << true; QTest::newRow( "date_empty" ) << QVariant( QDate() ) << false; QTest::newRow( "date_valid" ) << QVariant( QDate( 2002, 07, 06 ) ) << false; - QTest::newRow( "datetime_invalid" ) << QVariant(QVariant::DateTime) << true; + QTest::newRow( "datetime_invalid" ) << QVariant(QMetaType::fromType<QDateTime>()) << true; QTest::newRow( "datetime_empty" ) << QVariant( QDateTime() ) << false; QTest::newRow( "datetime_valid" ) << QVariant( QDateTime( QDate( 2002, 07, 06 ), QTime( 14, 0, 0 ) ) ) << false; QTest::newRow( "double_valid" ) << QVariant( 123.456 ) << false; @@ -1280,22 +1579,22 @@ void tst_QVariant::writeToReadFromDataStream_data() vMap.insert( "double", QVariant( 3.45 ) ); vMap.insert( "float", QVariant( 3.45f ) ); QTest::newRow( "map_valid" ) << QVariant( vMap ) << false; - QTest::newRow( "point_invalid" ) << QVariant(QVariant::Point) << true; + QTest::newRow( "point_invalid" ) << QVariant(QMetaType::fromType<QPoint>()) << true; QTest::newRow( "point_empty" ) << QVariant::fromValue( QPoint() ) << false; QTest::newRow( "point_valid" ) << QVariant::fromValue( QPoint( 10, 10 ) ) << false; - QTest::newRow( "rect_invalid" ) << QVariant(QVariant::Rect) << true; + QTest::newRow( "rect_invalid" ) << QVariant(QMetaType::fromType<QRect>()) << true; QTest::newRow( "rect_empty" ) << QVariant( QRect() ) << false; QTest::newRow( "rect_valid" ) << QVariant( QRect( 10, 10, 20, 20 ) ) << false; - QTest::newRow( "size_invalid" ) << QVariant(QVariant::Size) << true; + QTest::newRow( "size_invalid" ) << QVariant(QMetaType::fromType<QSize>()) << true; QTest::newRow( "size_empty" ) << QVariant( QSize( 0, 0 ) ) << false; QTest::newRow( "size_valid" ) << QVariant( QSize( 10, 10 ) ) << false; - QTest::newRow( "string_invalid" ) << QVariant(QVariant::String) << true; + QTest::newRow( "string_invalid" ) << QVariant(QMetaType::fromType<QString>()) << true; QTest::newRow( "string_empty" ) << QVariant( QString() ) << false; QTest::newRow( "string_valid" ) << QVariant( QString( "Test" ) ) << false; QStringList stringlist; stringlist << "One" << "Two" << "Three"; QTest::newRow( "stringlist_valid" ) << QVariant( stringlist ) << false; - QTest::newRow( "time_invalid" ) << QVariant(QVariant::Time) << true; + QTest::newRow( "time_invalid" ) << QVariant(QMetaType::fromType<QTime>()) << true; QTest::newRow( "time_empty" ) << QVariant( QTime() ) << false; QTest::newRow( "time_valid" ) << QVariant( QTime( 14, 0, 0 ) ) << false; QTest::newRow( "uint_valid" ) << QVariant( (uint)123 ) << false; @@ -1305,27 +1604,27 @@ void tst_QVariant::writeToReadFromDataStream_data() QTest::newRow( "regularexpression_empty" ) << QVariant(QRegularExpression()) << false; // types known to QMetaType, but not part of QVariant::Type - QTest::newRow("QMetaType::Long invalid") << QVariant(QMetaType(QMetaType::Long), nullptr) << true; + QTest::newRow("QMetaType::Long invalid") << QVariant(QMetaType::fromType<long>(), nullptr) << true; long longInt = -1l; - QTest::newRow("QMetaType::Long") << QVariant(QMetaType(QMetaType::Long), &longInt) << false; - QTest::newRow("QMetaType::Short invalid") << QVariant(QMetaType(QMetaType::Short), nullptr) << true; + QTest::newRow("QMetaType::Long") << QVariant(QMetaType::fromType<long>(), &longInt) << false; + QTest::newRow("QMetaType::Short invalid") << QVariant(QMetaType::fromType<short>(), nullptr) << true; short shortInt = 1; - QTest::newRow("QMetaType::Short") << QVariant(QMetaType(QMetaType::Short), &shortInt) << false; - QTest::newRow("QMetaType::Char invalid") << QVariant(QMetaType(QMetaType::Char), nullptr) << true; + QTest::newRow("QMetaType::Short") << QVariant(QMetaType::fromType<short>(), &shortInt) << false; + QTest::newRow("QMetaType::Char invalid") << QVariant(QMetaType::fromType<QChar>(), nullptr) << true; char ch = 'c'; - QTest::newRow("QMetaType::Char") << QVariant(QMetaType(QMetaType::Char), &ch) << false; - QTest::newRow("QMetaType::ULong invalid") << QVariant(QMetaType(QMetaType::ULong), nullptr) << true; + QTest::newRow("QMetaType::Char") << QVariant(QMetaType::fromType<char>(), &ch) << false; + QTest::newRow("QMetaType::ULong invalid") << QVariant(QMetaType::fromType<ulong>(), nullptr) << true; ulong ulongInt = 1ul; - QTest::newRow("QMetaType::ULong") << QVariant(QMetaType(QMetaType::ULong), &ulongInt) << false; - QTest::newRow("QMetaType::UShort invalid") << QVariant(QMetaType(QMetaType::UShort), nullptr) << true; + QTest::newRow("QMetaType::ULong") << QVariant(QMetaType::fromType<ulong>(), &ulongInt) << false; + QTest::newRow("QMetaType::UShort invalid") << QVariant(QMetaType::fromType<ushort>(), nullptr) << true; ushort ushortInt = 1u; - QTest::newRow("QMetaType::UShort") << QVariant(QMetaType(QMetaType::UShort), &ushortInt) << false; - QTest::newRow("QMetaType::UChar invalid") << QVariant(QMetaType(QMetaType::UChar), nullptr) << true; + QTest::newRow("QMetaType::UShort") << QVariant(QMetaType::fromType<ushort>(), &ushortInt) << false; + QTest::newRow("QMetaType::UChar invalid") << QVariant(QMetaType::fromType<uchar>(), nullptr) << true; uchar uch = 0xf0; - QTest::newRow("QMetaType::UChar") << QVariant(QMetaType(QMetaType::UChar), &uch) << false; - QTest::newRow("QMetaType::Float invalid") << QVariant(QMetaType(QMetaType::Float), nullptr) << true; + QTest::newRow("QMetaType::UChar") << QVariant(QMetaType::fromType<uchar>(), &uch) << false; + QTest::newRow("QMetaType::Float invalid") << QVariant(QMetaType::fromType<float>(), nullptr) << true; float f = 1.234f; - QTest::newRow("QMetaType::Float") << QVariant(QMetaType(QMetaType::Float), &f) << false; + QTest::newRow("QMetaType::Float") << QVariant(QMetaType::fromType<float>(), &f) << false; CustomStreamableClass custom = {123}; QTest::newRow("Custom type") << QVariant::fromValue(custom) << false; } @@ -1347,10 +1646,11 @@ void tst_QVariant::writeToReadFromDataStream() // Since only a few won't match since the serial numbers are different // I won't bother adding another bool in the data test. const int writeType = writeVariant.userType(); - if (writeType == qMetaTypeId<CustomStreamableClass>()) - QCOMPARE(qvariant_cast<CustomStreamableClass>(readVariant), qvariant_cast<CustomStreamableClass>(writeVariant)); - else if ( writeType != QVariant::Invalid && writeType != QVariant::Bitmap && writeType != QVariant::Pixmap - && writeType != QVariant::Image) { + if (writeType == qMetaTypeId<CustomStreamableClass>()) { + QCOMPARE(qvariant_cast<CustomStreamableClass>(readVariant), + qvariant_cast<CustomStreamableClass>(writeVariant)); + } else if ( writeType != QMetaType::UnknownType && writeType != QMetaType::QBitmap + && writeType != QMetaType::QPixmap && writeType != QMetaType::QImage) { switch (writeType) { default: QCOMPARE( readVariant, writeVariant ); @@ -1425,7 +1725,7 @@ void tst_QVariant::checkDataStream() // constructed. However, it might be worth considering changing that behavior // in the future. // QCOMPARE(in.status(), QDataStream::ReadCorruptData); - QCOMPARE(v.type(), QVariant::Invalid); + QCOMPARE(v.typeId(), QMetaType::UnknownType); } void tst_QVariant::operator_eq_eq_data() @@ -1442,7 +1742,7 @@ void tst_QVariant::operator_eq_eq_data() // Int QTest::newRow( "int1int1" ) << i1 << i1 << true; QTest::newRow( "int1int0" ) << i1 << i0 << false; - QTest::newRow( "nullint" ) << i0 << QVariant(QVariant::Int) << true; + QTest::newRow( "nullint" ) << i0 << QVariant(QMetaType::fromType<int>()) << true; // LongLong and ULongLong QVariant ll1( (qlonglong)1 ); @@ -1460,6 +1760,10 @@ void tst_QVariant::operator_eq_eq_data() QVariant mIntString(QByteArray("-42")); QVariant mIntQString(QString("-42")); + QVariant mIntZero(0); + QVariant mIntStringZero(QByteArray("0")); + QVariant mIntQStringZero(QString("0")); + QVariant mUInt(42u); QVariant mUIntString(QByteArray("42")); QVariant mUIntQString(QString("42")); @@ -1500,6 +1804,9 @@ void tst_QVariant::operator_eq_eq_data() QVariant mBoolString(QByteArray("false")); QVariant mBoolQString(QString("false")); + QVariant mTextString(QByteArray("foobar")); + QVariant mTextQString(QString("foobar")); + QTest::newRow( "double_int" ) << QVariant(42.0) << QVariant(42) << true; QTest::newRow( "float_int" ) << QVariant(42.f) << QVariant(42) << true; QTest::newRow( "mInt_mIntString" ) << mInt << mIntString << false; @@ -1507,6 +1814,21 @@ void tst_QVariant::operator_eq_eq_data() QTest::newRow( "mInt_mIntQString" ) << mInt << mIntQString << true; QTest::newRow( "mIntQString_mInt" ) << mIntQString << mInt << true; + QTest::newRow( "mIntZero_mIntStringZero" ) << mIntZero << mIntStringZero << false; + QTest::newRow( "mIntStringZero_mIntZero" ) << mIntStringZero << mIntZero << false; + QTest::newRow( "mIntZero_mIntQStringZero" ) << mIntZero << mIntQStringZero << true; + QTest::newRow( "mIntQStringZero_mIntZero" ) << mIntQStringZero << mIntZero << true; + + QTest::newRow( "mInt_mTextString" ) << mInt << mTextString << false; + QTest::newRow( "mTextString_mInt" ) << mTextString << mInt << false; + QTest::newRow( "mInt_mTextQString" ) << mInt << mTextQString << false; + QTest::newRow( "mTextQString_mInt" ) << mTextQString << mInt << false; + + QTest::newRow( "mIntZero_mTextString" ) << mIntZero << mTextString << false; + QTest::newRow( "mTextString_mIntZero" ) << mTextString << mIntZero << false; + QTest::newRow( "mIntZero_mTextQString" ) << mIntZero << mTextQString << false; + QTest::newRow( "mTextQString_mIntZero" ) << mTextQString << mIntZero << false; + QTest::newRow( "mUInt_mUIntString" ) << mUInt << mUIntString << false; QTest::newRow( "mUIntString_mUInt" ) << mUIntString << mUInt << false; QTest::newRow( "mUInt_mUIntQString" ) << mUInt << mUIntQString << true; @@ -1517,6 +1839,11 @@ void tst_QVariant::operator_eq_eq_data() QTest::newRow( "mDouble_mDoubleQString" ) << mDouble << mDoubleQString << true; QTest::newRow( "mDoubleQString_mDouble" ) << mDoubleQString << mDouble << true; + QTest::newRow( "mDouble_mTextString" ) << mDouble << mTextString << false; + QTest::newRow( "mTextString_mDouble" ) << mTextString << mDouble << false; + QTest::newRow( "mDouble_mTextQString" ) << mDouble << mTextQString << false; + QTest::newRow( "mTextQString_mDouble" ) << mTextQString << mDouble << false; + QTest::newRow( "mFloat_mFloatString" ) << mFloat << mFloatString << false; QTest::newRow( "mFloatString_mFloat" ) << mFloatString << mFloat << false; QTest::newRow( "mFloat_mFloatQString" ) << mFloat << mFloatQString << true; @@ -1683,9 +2010,11 @@ void tst_QVariant::operator_eq_eq() QFETCH( QVariant, left ); QFETCH( QVariant, right ); QFETCH( bool, equal ); - QCOMPARE( left == right, equal ); + QT_TEST_EQUALITY_OPS(left, right, equal); } +#if QT_DEPRECATED_SINCE(6, 0) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED void tst_QVariant::typeName_data() { QTest::addColumn<int>("type"); @@ -1713,7 +2042,8 @@ void tst_QVariant::typeName_data() QTest::newRow("20") << int(QVariant::Region) << QByteArray("QRegion"); QTest::newRow("21") << int(QVariant::Bitmap) << QByteArray("QBitmap"); QTest::newRow("22") << int(QVariant::Cursor) << QByteArray("QCursor"); - QTest::newRow("23") << int(QVariant::SizePolicy) << QByteArray("QSizePolicy"); + // The test below doesn't work as long as we don't link against widgets +// QTest::newRow("23") << int(QVariant::SizePolicy) << QByteArray("QSizePolicy"); QTest::newRow("24") << int(QVariant::Date) << QByteArray("QDate"); QTest::newRow("25") << int(QVariant::Time) << QByteArray("QTime"); QTest::newRow("26") << int(QVariant::DateTime) << QByteArray("QDateTime"); @@ -1757,17 +2087,23 @@ void tst_QVariant::typeToName() // assumes that QVariant::Type contains consecutive values int max = QVariant::LastGuiType; - for ( int t = 1; t <= max; t++ ) { + for (int t = 1; t <= max; ++t) { + if (!QMetaType::isRegistered(t)) { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + "^Trying to construct an instance of an invalid type")); + } const char *n = QVariant::typeToName( (QVariant::Type)t ); if (n) QCOMPARE( int(QVariant::nameToType( n )), t ); - } + QCOMPARE(QVariant::typeToName(QVariant::Int), "int"); // not documented but we return 0 if the type is out of range // by testing this we catch cases where QVariant is extended // but type_map is not updated accordingly - QCOMPARE( QVariant::typeToName( QVariant::Type(max+1) ), (char*)0 ); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + "^Trying to construct an instance of an invalid type")); + QCOMPARE(QVariant::typeToName(QVariant::Type(max + 1)), (const char *)nullptr); // invalid type names QVERIFY( QVariant::nameToType( 0 ) == QVariant::Invalid ); QVERIFY( QVariant::nameToType( "" ) == QVariant::Invalid ); @@ -1781,6 +2117,8 @@ void tst_QVariant::typeToName() QCOMPARE(QVariant::nameToType("Q_LLONG"), QVariant::Invalid); QCOMPARE(QVariant::nameToType("Q_ULLONG"), QVariant::Invalid); } +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 0) void tst_QVariant::streamInvalidVariant() { @@ -1791,7 +2129,7 @@ void tst_QVariant::streamInvalidVariant() QVariant writeVariant; QVariant readVariant; - QVERIFY( writeVariant.type() == QVariant::Invalid ); + QVERIFY( writeVariant.typeId() == QMetaType::UnknownType ); QByteArray data; QDataStream writeStream( &data, QIODevice::WriteOnly ); @@ -1803,7 +2141,7 @@ void tst_QVariant::streamInvalidVariant() QVERIFY( readX == writeX ); // Two invalid QVariant's aren't necessarily the same, so == will // return false if one is invalid, so check the type() instead - QVERIFY( readVariant.type() == QVariant::Invalid ); + QVERIFY( readVariant.typeId() == QMetaType::UnknownType ); QVERIFY( readY == writeY ); } @@ -1849,11 +2187,13 @@ void tst_QVariant::userType() QVariant userVar; userVar.setValue(data); - QCOMPARE(userVar.type(), QVariant::UserType); + QVERIFY(QMetaType::fromName("MyType").isValid()); + QCOMPARE(QMetaType::fromName("MyType"), QMetaType::fromType<MyType>()); + QVERIFY(userVar.typeId() > QMetaType::User); QCOMPARE(userVar.userType(), qMetaTypeId<MyType>()); QCOMPARE(userVar.typeName(), "MyType"); QVERIFY(!userVar.isNull()); - QVERIFY(!userVar.canConvert(QVariant::String)); + QVERIFY(!userVar.canConvert<QString>()); QVariant userVar2(userVar); QCOMPARE(userVar, userVar2); @@ -1878,11 +2218,11 @@ void tst_QVariant::userType() QVariant userVar; userVar.setValue(&data); - QCOMPARE(userVar.type(), QVariant::UserType); + QVERIFY(userVar.typeId() > QMetaType::User); QCOMPARE(userVar.userType(), qMetaTypeId<MyType*>()); QCOMPARE(userVar.typeName(), "MyType*"); QVERIFY(!userVar.isNull()); - QVERIFY(!userVar.canConvert(QVariant::String)); + QVERIFY(!userVar.canConvert<QString>()); QVariant userVar2(userVar); QCOMPARE(userVar, userVar2); @@ -1972,7 +2312,15 @@ void tst_QVariant::podUserType() pod.a = 10; pod.b = 20; + // one of these two must register the type + // (QVariant::fromValue calls QMetaType::fromType) QVariant pod_as_variant = QVariant::fromValue(pod); + QMetaType mt = QMetaType::fromType<MyTypePOD>(); + QCOMPARE(pod_as_variant.metaType(), mt); + QCOMPARE(pod_as_variant.metaType().name(), mt.name()); + QCOMPARE(QMetaType::fromName(mt.name()), mt); + QCOMPARE_NE(pod_as_variant.typeId(), 0); + MyTypePOD pod2 = qvariant_cast<MyTypePOD>(pod_as_variant); QCOMPARE(pod.a, pod2.a); @@ -1990,37 +2338,37 @@ void tst_QVariant::basicUserType() QVariant v; { int i = 7; - v = QVariant(QMetaType(QMetaType::Int), &i); + v = QVariant(QMetaType::fromType<int>(), &i); } - QCOMPARE(v.type(), QVariant::Int); + QCOMPARE(v.typeId(), QMetaType::Int); QCOMPARE(v.toInt(), 7); { QString s("foo"); - v = QVariant(QMetaType(QMetaType::QString), &s); + v = QVariant(QMetaType::fromType<QString>(), &s); } - QCOMPARE(v.type(), QVariant::String); + QCOMPARE(v.typeId(), QMetaType::QString); QCOMPARE(v.toString(), QString("foo")); { double d = 4.4; - v = QVariant(QMetaType(QMetaType::Double), &d); + v = QVariant(QMetaType::fromType<double>(), &d); } - QCOMPARE(v.type(), QVariant::Double); + QCOMPARE(v.typeId(), QMetaType::Double); QCOMPARE(v.toDouble(), 4.4); { float f = 4.5f; - v = QVariant(QMetaType(QMetaType::Float), &f); + v = QVariant(QMetaType::fromType<float>(), &f); } QCOMPARE(v.userType(), int(QMetaType::Float)); QCOMPARE(v.toDouble(), 4.5); { QByteArray ba("bar"); - v = QVariant(QMetaType(QMetaType::QByteArray), &ba); + v = QVariant(QMetaType::fromType<QByteArray>(), &ba); } - QCOMPARE(v.type(), QVariant::ByteArray); + QCOMPARE(v.typeId(), QMetaType::QByteArray); QCOMPARE(v.toByteArray(), QByteArray("bar")); } @@ -2184,10 +2532,8 @@ void tst_QVariant::saveLoadCustomTypes() auto tp = QMetaType::fromType<Blah>(); QVariant v = QVariant(tp, &i); - qRegisterMetaTypeStreamOperators<Blah>("Blah"); - QCOMPARE(v.userType(), tp.id()); - QCOMPARE(v.type(), QVariant::UserType); + QVERIFY(v.typeId() > QMetaType::User); { QDataStream stream(&data, QIODevice::WriteOnly); stream << v; @@ -2200,7 +2546,7 @@ void tst_QVariant::saveLoadCustomTypes() stream >> v; } - QCOMPARE(int(v.userType()), QMetaType::type("Blah")); + QCOMPARE(int(v.userType()), QMetaType::fromName("Blah").id()); int value = *(int*)v.constData(); QCOMPARE(value, 42); } @@ -2278,7 +2624,7 @@ void tst_QVariant::variantHash() class CustomQObject : public QObject { Q_OBJECT public: - CustomQObject(QObject *parent = 0) : QObject(parent) {} + CustomQObject(QObject *parent = nullptr) : QObject(parent) {} }; Q_DECLARE_METATYPE(CustomQObject*) @@ -2299,7 +2645,7 @@ void tst_QVariant::qvariant_cast_QObject_data() QTest::addColumn<bool>("isNull"); QObject *obj = new QObject; obj->setObjectName(QString::fromLatin1("Hello")); - QTest::newRow("from QObject") << QVariant(QMetaType(QMetaType::QObjectStar), &obj) << true << false; + QTest::newRow("from QObject") << QVariant(QMetaType::fromType<QObject*>(), &obj) << true << false; QTest::newRow("from QObject2") << QVariant::fromValue(obj) << true << false; QTest::newRow("from String") << QVariant(QLatin1String("1, 2, 3")) << false << false; QTest::newRow("from int") << QVariant((int) 123) << false << false; @@ -2334,32 +2680,32 @@ void tst_QVariant::qvariant_cast_QObject() if (!isNull) QCOMPARE(o->objectName(), QString::fromLatin1("Hello")); QVERIFY(data.canConvert<QObject*>()); - QVERIFY(data.canConvert(QMetaType::QObjectStar)); - QVERIFY(data.canConvert(::qMetaTypeId<QObject*>())); - QCOMPARE(data.value<QObject*>() == 0, isNull); - QVERIFY(data.convert(QMetaType::QObjectStar)); + QVERIFY(data.canConvert(QMetaType::fromType<QObject*>())); + QVERIFY(data.canConvert(QMetaType(::qMetaTypeId<QObject*>()))); + QCOMPARE(data.value<QObject*>() == nullptr, isNull); + QVERIFY(data.convert(QMetaType::fromType<QObject*>())); QCOMPARE(data.userType(), int(QMetaType::QObjectStar)); } else { QVERIFY(!data.canConvert<QObject*>()); - QVERIFY(!data.canConvert(QMetaType::QObjectStar)); - QVERIFY(!data.canConvert(::qMetaTypeId<QObject*>())); + QVERIFY(!data.canConvert(QMetaType::fromType<QObject*>())); + QVERIFY(!data.canConvert(QMetaType(::qMetaTypeId<QObject*>()))); QVERIFY(!data.value<QObject*>()); - QVERIFY(!data.convert(QMetaType::QObjectStar)); QVERIFY(data.userType() != QMetaType::QObjectStar); + QVERIFY(!data.convert(QMetaType::fromType<QObject*>())); } } class CustomQObjectDerived : public CustomQObject { Q_OBJECT public: - CustomQObjectDerived(QObject *parent = 0) : CustomQObject(parent) {} + CustomQObjectDerived(QObject *parent = nullptr) : CustomQObject(parent) {} }; Q_DECLARE_METATYPE(CustomQObjectDerived*) class CustomQObjectDerivedNoMetaType : public CustomQObject { Q_OBJECT public: - CustomQObjectDerivedNoMetaType(QObject *parent = 0) : CustomQObject(parent) {} + CustomQObjectDerivedNoMetaType(QObject *parent = nullptr) : CustomQObject(parent) {} }; void tst_QVariant::qvariant_cast_QObject_derived() @@ -2386,7 +2732,7 @@ void tst_QVariant::qvariant_cast_QObject_derived() QObject *object = new CustomQObjectDerivedNoMetaType(this); QVariant data = QVariant::fromValue(object); QVERIFY(data.canConvert<CustomQObjectDerivedNoMetaType*>()); - QVERIFY(data.convert(qMetaTypeId<CustomQObjectDerivedNoMetaType*>())); + QVERIFY(data.convert(QMetaType(qMetaTypeId<CustomQObjectDerivedNoMetaType*>()))); QCOMPARE(data.value<CustomQObjectDerivedNoMetaType*>(), object); QCOMPARE(data.isNull(), false); } @@ -2394,7 +2740,7 @@ void tst_QVariant::qvariant_cast_QObject_derived() struct QObjectWrapper { - explicit QObjectWrapper(QObject *o = 0) : obj(o) {} + explicit QObjectWrapper(QObject *o = nullptr) : obj(o) {} QObject* getObject() const { return obj; @@ -2423,7 +2769,7 @@ class SmartPointer T* pointer; public: typedef T element_type; - explicit SmartPointer(T *t = 0) + explicit SmartPointer(T *t = nullptr) : pointer(t) { } @@ -2466,7 +2812,7 @@ void tst_QVariant::qvariant_cast_QObject_wrapper() QObjectWrapper wrapper(object); QVariant v = QVariant::fromValue(wrapper); QCOMPARE(v.value<QObject*>(), object); - v.convert(qMetaTypeId<QObject*>()); + v.convert(QMetaType(qMetaTypeId<QObject*>())); QCOMPARE(v.value<QObject*>(), object); MyNS::SequentialContainer<int> sc; @@ -2490,18 +2836,6 @@ void tst_QVariant::qvariant_cast_QObject_wrapper() QVERIFY(spVar.canConvert<QObject*>()); QCOMPARE(f, spVar.value<QObject*>()); } -#if QT_DEPRECATED_SINCE(5, 0) - { - QFile *f = new QFile(this); -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - QWeakPointer<QFile> sp(f); -QT_WARNING_POP - QVariant spVar = QVariant::fromValue(sp); - QVERIFY(spVar.canConvert<QObject*>()); - QCOMPARE(f, spVar.value<QObject*>()); - } -#endif { QFile *f = new QFile(this); QSharedPointer<QObject> sp(f); @@ -2581,6 +2915,17 @@ void tst_QVariant::qvariant_cast_QSharedPointerQObject() qRegisterMetaType<QSharedPointer<QObject> >(); } +void tst_QVariant::qvariant_cast_const() +{ + int i = 42; + QVariant v = QVariant::fromValue(&i); + QVariant vConst = QVariant::fromValue(const_cast<const int*>(&i)); + QCOMPARE(v.value<int *>(), &i); + QCOMPARE(v.value<const int *>(), &i); + QCOMPARE(vConst.value<int *>(), nullptr); + QCOMPARE(vConst.value<const int *>(), &i); +} + void tst_QVariant::convertToQUint8() const { /* qint8. */ @@ -2638,18 +2983,289 @@ void tst_QVariant::convertToQUint8() const } } -void tst_QVariant::comparePointers() const +void tst_QVariant::compareCompiles() const { - class MyClass - { + QTestPrivate::testEqualityOperatorsCompile<QVariant>(); +} + +void tst_QVariant::compareNumerics_data() const +{ + QTest::addColumn<QVariant>("v1"); + QTest::addColumn<QVariant>("v2"); + QTest::addColumn<QPartialOrdering>("result"); + + QTest::addRow("invalid-invalid") + << QVariant() << QVariant() << QPartialOrdering::Unordered; + + static const auto asString = [](const QVariant &v) { + if (v.isNull()) + return QStringLiteral("null"); + if (v.metaType().flags() & QMetaType::IsEnumeration) + return v.metaType().flags() & QMetaType::IsUnsignedEnumeration ? + QString::number(v.toULongLong()) : + QString::number(v.toLongLong()); + switch (v.typeId()) { + case QMetaType::Char16: + return QString::number(qvariant_cast<char16_t>(v)); + case QMetaType::Char32: + return QString::number(qvariant_cast<char32_t>(v)); + case QMetaType::Char: + case QMetaType::UChar: + return QString::number(v.toUInt()); + case QMetaType::SChar: + return QString::number(v.toInt()); + } + return v.toString(); }; - MyClass myClass; + auto addCompareToInvalid = [](auto value) { + QVariant v = QVariant::fromValue(value); + QTest::addRow("invalid-%s(%s)", v.typeName(), qPrintable(asString(v))) + << QVariant() << v << QPartialOrdering::Unordered; + QTest::addRow("%s(%s)-invalid", v.typeName(), qPrintable(asString(v))) + << v << QVariant() << QPartialOrdering::Unordered; + }; + addCompareToInvalid(false); + addCompareToInvalid(true); + addCompareToInvalid(char(0)); + addCompareToInvalid(qint8(0)); + addCompareToInvalid(quint8(0)); + addCompareToInvalid(short(0)); + addCompareToInvalid(ushort(0)); + addCompareToInvalid(int(0)); + addCompareToInvalid(uint(0)); + addCompareToInvalid(long(0)); + addCompareToInvalid(ulong(0)); + addCompareToInvalid(qint64(0)); + addCompareToInvalid(quint64(0)); + addCompareToInvalid(0.f); + addCompareToInvalid(0.0); + addCompareToInvalid(QCborSimpleType{}); - QVariant v = QVariant::fromValue<void *>(&myClass); - QVariant v2 = QVariant::fromValue<void *>(&myClass); +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wsign-compare") +QT_WARNING_DISABLE_GCC("-Wsign-compare") +QT_WARNING_DISABLE_MSVC(4018) // '<': signed/unsigned mismatch + static const auto addComparePairWithResult = [](auto value1, auto value2, QPartialOrdering order) { + QVariant v1 = QVariant::fromValue(value1); + QVariant v2 = QVariant::fromValue(value2); + QTest::addRow("%s(%s)-%s(%s)", v1.typeName(), qPrintable(asString(v1)), + v2.typeName(), qPrintable(asString(v2))) + << v1 << v2 << order; + }; - QCOMPARE(v, v2); + static const auto addComparePair = [](auto value1, auto value2) { + QPartialOrdering order = QPartialOrdering::Unordered; + if (value1 == value2) + order = QPartialOrdering::Equivalent; + else if (value1 < value2) + order = QPartialOrdering::Less; + else if (value1 > value2) + order = QPartialOrdering::Greater; + addComparePairWithResult(value1, value2, order); + }; +QT_WARNING_POP + + // homogeneous first + static const auto addList = [](auto list) { + for (auto v1 : list) + for (auto v2 : list) + addComparePair(v1, v2); + }; + + auto addSingleType = [](auto zero) { + using T = decltype(zero); + T one = T(zero + 1); + T min = std::numeric_limits<T>::min(); + T max = std::numeric_limits<T>::max(); + T mid = max / 2 + 1; + if (min != zero) + addList(std::array{zero, one, min, mid, max}); + else + addList(std::array{zero, one, mid, max}); + }; + addList(std::array{ false, true }); + addList(std::array{ QCborSimpleType{}, QCborSimpleType::False, QCborSimpleType(0xff) }); + addSingleType(char(0)); + addSingleType(char16_t(0)); + addSingleType(char32_t(0)); + addSingleType(qint8(0)); + addSingleType(quint8(0)); + addSingleType(qint16(0)); + addSingleType(quint16(0)); + addSingleType(qint32(0)); + addSingleType(quint32(0)); + addSingleType(qint64(0)); + addSingleType(quint64(0)); + addSingleType(0.f); + addSingleType(0.0); + addList(std::array{ EnumTest_Enum0{}, EnumTest_Enum0_value, EnumTest_Enum0_negValue }); + addList(std::array{ EnumTest_Enum1{}, EnumTest_Enum1_value, EnumTest_Enum1_bigValue }); + addList(std::array{ EnumTest_Enum7{}, EnumTest_Enum7::EnumTest_Enum7_value, EnumTest_Enum7::ensureSignedEnum7 }); + addList(std::array{ Qt::AlignRight|Qt::AlignHCenter, Qt::AlignCenter|Qt::AlignVCenter }); + + // heterogeneous + addComparePair(char(0), qint8(-127)); + addComparePair(char(127), qint8(127)); + addComparePair(char(127), quint8(127)); + addComparePair(qint8(-1), quint8(255)); + addComparePair(char16_t(256), qint8(-1)); + addComparePair(char16_t(256), short(-1)); + addComparePair(char16_t(256), int(-1)); + addComparePair(char32_t(256), int(-1)); + addComparePair(0U, -1); + addComparePair(~0U, -1); + addComparePair(Q_UINT64_C(0), -1); + addComparePair(~Q_UINT64_C(0), -1); + addComparePair(Q_UINT64_C(0), Q_INT64_C(-1)); + addComparePair(~Q_UINT64_C(0), Q_INT64_C(-1)); + addComparePair(INT_MAX, uint(INT_MAX)); + addComparePair(INT_MAX, qint64(INT_MAX) + 1); + addComparePair(INT_MAX, UINT_MAX); + addComparePair(INT_MAX, qint64(UINT_MAX)); + addComparePair(INT_MAX, qint64(UINT_MAX) + 1); + addComparePair(INT_MAX, quint64(UINT_MAX)); + addComparePair(INT_MAX, quint64(UINT_MAX) + 1); + addComparePair(INT_MAX, LONG_MIN); + addComparePair(INT_MAX, LONG_MAX); + addComparePair(INT_MAX, LLONG_MIN); + addComparePair(INT_MAX, LLONG_MAX); + addComparePair(INT_MIN, uint(INT_MIN)); + addComparePair(INT_MIN, uint(INT_MIN) + 1); + addComparePair(INT_MIN + 1, uint(INT_MIN)); + addComparePair(INT_MIN + 1, uint(INT_MIN) + 1); + addComparePair(INT_MIN, qint64(INT_MIN) - 1); + addComparePair(INT_MIN + 1, qint64(INT_MIN) + 1); + addComparePair(INT_MIN + 1, qint64(INT_MIN) - 1); + addComparePair(INT_MIN, UINT_MAX); + addComparePair(INT_MIN, qint64(UINT_MAX)); + addComparePair(INT_MIN, qint64(UINT_MAX) + 1); + addComparePair(INT_MIN, quint64(UINT_MAX)); + addComparePair(INT_MIN, quint64(UINT_MAX) + 1); + addComparePair(UINT_MAX, qint64(UINT_MAX) + 1); + addComparePair(UINT_MAX, quint64(UINT_MAX) + 1); + addComparePair(UINT_MAX, qint64(INT_MIN) - 1); + addComparePair(UINT_MAX, quint64(INT_MIN) + 1); + addComparePair(LLONG_MAX, quint64(LLONG_MAX)); + addComparePair(LLONG_MAX, quint64(LLONG_MAX) + 1); + addComparePair(LLONG_MIN, quint64(LLONG_MAX)); + addComparePair(LLONG_MIN, quint64(LLONG_MAX) + 1); + addComparePair(LLONG_MIN, quint64(LLONG_MIN) + 1); + addComparePair(LLONG_MIN + 1, quint64(LLONG_MIN) + 1); + addComparePair(LLONG_MIN, LLONG_MAX - 1); + // addComparePair(LLONG_MIN, LLONG_MAX); // already added by addSingleType() + + // floating point + addComparePair(0.f, 0); + addComparePair(0.f, 0U); + addComparePair(0.f, Q_INT64_C(0)); + addComparePair(0.f, Q_UINT64_C(0)); + addComparePair(0.f, 0.); + addComparePair(0.f, 1.); + addComparePair(float(1 << 24), 1 << 24); + addComparePair(float(1 << 24) - 1, (1 << 24) - 1); + addComparePair(-float(1 << 24), 1 << 24); + addComparePair(-float(1 << 24) + 1, -(1 << 24) + 1); + addComparePair(HUGE_VALF, qInf()); + addComparePair(HUGE_VALF, -qInf()); + addComparePair(qQNaN(), std::numeric_limits<float>::quiet_NaN()); + if (sizeof(qreal) == sizeof(double)) { + addComparePair(std::numeric_limits<float>::min(), std::numeric_limits<double>::min()); + addComparePair(std::numeric_limits<float>::min(), std::numeric_limits<double>::max()); + addComparePair(std::numeric_limits<float>::max(), std::numeric_limits<double>::min()); + addComparePair(std::numeric_limits<float>::max(), std::numeric_limits<double>::max()); + addComparePair(double(Q_INT64_C(1) << 53), Q_INT64_C(1) << 53); + addComparePair(double(Q_INT64_C(1) << 53) - 1, (Q_INT64_C(1) << 53) - 1); + addComparePair(-double(Q_INT64_C(1) << 53), Q_INT64_C(1) << 53); + addComparePair(-double(Q_INT64_C(1) << 53) + 1, (Q_INT64_C(1) << 53) + 1); + } + + // enums vs integers + addComparePair(EnumTest_Enum0_value, 0); + addComparePair(EnumTest_Enum0_value, 0U); + addComparePair(EnumTest_Enum0_value, 0LL); + addComparePair(EnumTest_Enum0_value, 0ULL); + addComparePair(EnumTest_Enum0_value, int(EnumTest_Enum0_value)); + addComparePair(EnumTest_Enum0_value, qint64(EnumTest_Enum0_value)); + addComparePair(EnumTest_Enum0_value, quint64(EnumTest_Enum0_value)); + addComparePair(EnumTest_Enum0_negValue, int(EnumTest_Enum0_value)); + addComparePair(EnumTest_Enum0_negValue, qint64(EnumTest_Enum0_value)); + addComparePair(EnumTest_Enum0_negValue, quint64(EnumTest_Enum0_value)); + addComparePair(EnumTest_Enum0_negValue, int(EnumTest_Enum0_negValue)); + addComparePair(EnumTest_Enum0_negValue, qint64(EnumTest_Enum0_negValue)); + addComparePair(EnumTest_Enum0_negValue, quint64(EnumTest_Enum0_negValue)); + + addComparePair(EnumTest_Enum1_value, 0); + addComparePair(EnumTest_Enum1_value, 0U); + addComparePair(EnumTest_Enum1_value, 0LL); + addComparePair(EnumTest_Enum1_value, 0ULL); + addComparePair(EnumTest_Enum1_value, int(EnumTest_Enum1_value)); + addComparePair(EnumTest_Enum1_value, qint64(EnumTest_Enum1_value)); + addComparePair(EnumTest_Enum1_value, quint64(EnumTest_Enum1_value)); + addComparePair(EnumTest_Enum1_bigValue, int(EnumTest_Enum1_value)); + addComparePair(EnumTest_Enum1_bigValue, qint64(EnumTest_Enum1_value)); + addComparePair(EnumTest_Enum1_bigValue, quint64(EnumTest_Enum1_value)); + addComparePair(EnumTest_Enum1_bigValue, int(EnumTest_Enum1_bigValue)); + addComparePair(EnumTest_Enum1_bigValue, qint64(EnumTest_Enum1_bigValue)); + addComparePair(EnumTest_Enum1_bigValue, quint64(EnumTest_Enum1_bigValue)); + + addComparePair(EnumTest_Enum3_value, 0); + addComparePair(EnumTest_Enum3_value, 0U); + addComparePair(EnumTest_Enum3_value, 0LL); + addComparePair(EnumTest_Enum3_value, 0ULL); + addComparePair(EnumTest_Enum3_value, int(EnumTest_Enum3_value)); + addComparePair(EnumTest_Enum3_value, qint64(EnumTest_Enum3_value)); + addComparePair(EnumTest_Enum3_value, quint64(EnumTest_Enum3_value)); + addComparePair(EnumTest_Enum3_bigValue, int(EnumTest_Enum3_value)); + addComparePair(EnumTest_Enum3_bigValue, qint64(EnumTest_Enum3_value)); + addComparePair(EnumTest_Enum3_bigValue, quint64(EnumTest_Enum3_value)); + addComparePair(EnumTest_Enum3_bigValue, int(EnumTest_Enum3_bigValue)); + addComparePair(EnumTest_Enum3_bigValue, qint64(EnumTest_Enum3_bigValue)); + addComparePair(EnumTest_Enum3_bigValue, quint64(EnumTest_Enum3_bigValue)); + + // enums of different types always compare as unordered + addComparePairWithResult(EnumTest_Enum0_value, EnumTest_Enum1_value, QPartialOrdering::Unordered); +} + +void tst_QVariant::compareNumerics() const +{ + QFETCH(QVariant, v1); + QFETCH(QVariant, v2); + QFETCH(QPartialOrdering, result); + QCOMPARE(QVariant::compare(v1, v2), result); + + QEXPECT_FAIL("invalid-invalid", "needs fixing", Abort); + QT_TEST_EQUALITY_OPS(v1, v2, is_eq(result)); +} + +void tst_QVariant::comparePointers() const +{ + class NonQObjectClass {}; + const std::array<NonQObjectClass, 2> arr{ NonQObjectClass{}, NonQObjectClass{} }; + + const QVariant nonObjV1 = QVariant::fromValue<const void*>(&arr[0]); + const QVariant nonObjV2 = QVariant::fromValue<const void*>(&arr[1]); + + Qt::partial_ordering expectedOrdering = Qt::partial_ordering::equivalent; + QCOMPARE(QVariant::compare(nonObjV1, nonObjV1), expectedOrdering); + QT_TEST_EQUALITY_OPS(nonObjV1, nonObjV1, is_eq(expectedOrdering)); + + expectedOrdering = Qt::partial_ordering::less; + QCOMPARE(QVariant::compare(nonObjV1, nonObjV2), expectedOrdering); + QT_TEST_EQUALITY_OPS(nonObjV1, nonObjV2, is_eq(expectedOrdering)); + + class QObjectClass : public QObject + { + public: + QObjectClass(QObject *parent = nullptr) : QObject(parent) {} + }; + const QObjectClass c1; + const QObjectClass c2; + + const QVariant objV1 = QVariant::fromValue(&c1); + const QVariant objV2 = QVariant::fromValue(&c2); + QT_TEST_EQUALITY_OPS(objV1, objV1, true); + QT_TEST_EQUALITY_OPS(objV1, objV2, false); } struct Data {}; @@ -2669,7 +3285,7 @@ void tst_QVariant::voidStar() const v2 = QVariant::fromValue(p2); QCOMPARE(v1, v2); - p2 = 0; + p2 = nullptr; v2 = QVariant::fromValue(p2); QVERIFY(v1 != v2); } @@ -2699,7 +3315,7 @@ void tst_QVariant::canConvertQStringList() const QVariant v(input); - QCOMPARE(v.canConvert(QVariant::String), canConvert); + QCOMPARE(v.canConvert<QString>(), canConvert); QCOMPARE(v.toString(), result); } @@ -2709,23 +3325,23 @@ void tst_QVariant::canConvertQStringList_data() const QTest::addColumn<QStringList>("input"); QTest::addColumn<QString>("result"); - QTest::newRow("An empty list") << false << QStringList() << QString(); + QTest::newRow("An empty list") << true << QStringList() << QString(); QTest::newRow("A single item") << true << QStringList(QLatin1String("foo")) << QString::fromLatin1("foo"); QTest::newRow("A single, but empty item") << true << QStringList(QString()) << QString(); QStringList l; l << "a" << "b"; - QTest::newRow("Two items") << false << l << QString(); + QTest::newRow("Two items") << true << l << QString(); l << "c"; - QTest::newRow("Three items") << false << l << QString(); + QTest::newRow("Three items") << true << l << QString(); } template<typename T> void convertMetaType() { QVERIFY(QVariant::fromValue<T>(10).isValid()); - QVERIFY(QVariant::fromValue<T>(10).canConvert(QVariant::Int)); + QVERIFY(QVariant::fromValue<T>(10).template canConvert<int>()); QCOMPARE(QVariant::fromValue<T>(10).toInt(), 10); QCOMPARE(QVariant::fromValue<T>(10), QVariant::fromValue<T>(10)); } @@ -2766,7 +3382,7 @@ void tst_QVariant::variantToDateTimeWithoutWarnings() const { QVariant v1(QLatin1String("xyz")); - v1.convert(QVariant::DateTime); + v1.convert(QMetaType::fromType<QDateTime>()); QVariant v2(QLatin1String("xyz")); QDateTime dt1(v2.toDateTime()); @@ -2780,7 +3396,7 @@ void tst_QVariant::invalidDateTime() const { QVariant variant(QString::fromLatin1("Invalid date time string")); QVERIFY(!variant.toDateTime().isValid()); - QVERIFY(!variant.convert(QVariant::DateTime)); + QVERIFY(!variant.convert(QMetaType::fromType<QDateTime>())); } struct MyClass @@ -2796,7 +3412,7 @@ void tst_QVariant::loadUnknownUserType() qRegisterMetaType<MyClass>("MyClass"); QTest::ignoreMessage(QtWarningMsg, "QVariant::load: unable to load type " + QByteArray::number(qMetaTypeId<MyClass>()) +"."); - char data[] = {0, 0, QMetaType::User >> 8 , char(QMetaType::User), 0, 0, 0, 0, 8, 'M', 'y', 'C', 'l', 'a', 's', 's', 0}; + char data[] = {0, QMetaType::User >> 16, char(QMetaType::User >> 8) , char(QMetaType::User), 0, 0, 0, 0, 8, 'M', 'y', 'C', 'l', 'a', 's', 's', 0}; QByteArray ba(data, sizeof(data)); QDataStream ds(&ba, QIODevice::ReadOnly); @@ -2821,22 +3437,22 @@ void tst_QVariant::invalidDate() const { QString foo("Hello"); QVariant variant(foo); - QVERIFY(!variant.convert(QVariant::Date)); + QVERIFY(!variant.convert(QMetaType::fromType<QDate>())); variant = foo; - QVERIFY(!variant.convert(QVariant::DateTime)); + QVERIFY(!variant.convert(QMetaType::fromType<QDateTime>())); variant = foo; - QVERIFY(!variant.convert(QVariant::Time)); + QVERIFY(!variant.convert(QMetaType::fromType<QTime>())); variant = foo; - QVERIFY(!variant.convert(QVariant::Int)); + QVERIFY(!variant.convert(QMetaType::fromType<int>())); variant = foo; - QVERIFY(!variant.convert(QVariant::Double)); + QVERIFY(!variant.convert(QMetaType::fromType<double>())); variant = foo; - QVERIFY(!variant.convert(QVariant::Type(QMetaType::Float))); + QVERIFY(!variant.convert(QMetaType::fromType<float>())); } struct WontCompare @@ -2848,40 +3464,54 @@ Q_DECLARE_METATYPE(WontCompare); struct WillCompare { int x; + + friend bool operator==(const WillCompare &a, const WillCompare &b) + { return a.x == b.x; } + friend bool operator<(const WillCompare &a, const WillCompare &b) + { return a.x < b.x; } }; -bool operator==(const WillCompare &a, const WillCompare &b) { return a.x == b.x; } Q_DECLARE_METATYPE(WillCompare); -void tst_QVariant::compareCustomTypes() const +void tst_QVariant::compareCustomTypes_data() const { - { - WontCompare f1{0}; - const QVariant variant1(QVariant::fromValue(f1)); + QTest::addColumn<QVariant>("v1"); + QTest::addColumn<QVariant>("v2"); + QTest::addColumn<Qt::partial_ordering>("expectedOrdering"); - WontCompare f2{1}; - const QVariant variant2(QVariant::fromValue(f2)); + QTest::newRow("same_uncomparable") + << QVariant::fromValue(WontCompare{0}) + << QVariant::fromValue(WontCompare{0}) + << Qt::partial_ordering::unordered; - /* No comparison operator exists. */ - QVERIFY(variant1 != variant2); - QVERIFY(variant1 != variant1); - QVERIFY(variant2 != variant2); - } - { - WillCompare f1{0}; - const QVariant variant1(QVariant::fromValue(f1)); + QTest::newRow("same_comparable") + << QVariant::fromValue(WillCompare{0}) + << QVariant::fromValue(WillCompare{0}) + << Qt::partial_ordering::equivalent; - WillCompare f2 {1}; - const QVariant variant2(QVariant::fromValue(f2)); + QTest::newRow("different_comparable") + << QVariant::fromValue(WillCompare{1}) + << QVariant::fromValue(WillCompare{0}) + << Qt::partial_ordering::greater; - QVERIFY(variant1 != variant2); - QCOMPARE(variant1, variant1); - QCOMPARE(variant2, variant2); - } + QTest::newRow("qdatetime_vs_comparable") + << QVariant::fromValue(QDateTime::currentDateTimeUtc()) + << QVariant::fromValue(WillCompare{0}) + << Qt::partial_ordering::unordered; +} + +void tst_QVariant::compareCustomTypes() const +{ + QFETCH(const QVariant, v1); + QFETCH(const QVariant, v2); + QFETCH(const Qt::partial_ordering, expectedOrdering); + + QCOMPARE(QVariant::compare(v1, v2), expectedOrdering); + QT_TEST_EQUALITY_OPS(v1, v2, is_eq(expectedOrdering)); } void tst_QVariant::timeToDateTime() const { const QVariant val(QTime::currentTime()); - QVERIFY(!val.canConvert(QVariant::DateTime)); + QVERIFY(!val.canConvert<QDateTime>()); QVERIFY(!val.toDateTime().isValid()); } @@ -2909,6 +3539,43 @@ void tst_QVariant::copyingUserTypes() const QCOMPARE(copiedType.myValue, 42); } + +struct NonQObjectBase {}; +struct NonQObjectDerived : NonQObjectBase {}; + +void tst_QVariant::valueClassHierarchyConversion() const +{ + + { + // QVariant allows downcasting + QScopedPointer<CustomQObjectDerived> derived {new CustomQObjectDerived}; + QVariant var = QVariant::fromValue(derived.get()); + CustomQObject *object = var.value<CustomQObject *>(); + QVERIFY(object); + } + { + // QVariant supports upcasting to the correct dynamic type for QObjects + QScopedPointer<CustomQObjectDerived> derived {new CustomQObjectDerived}; + QVariant var = QVariant::fromValue<CustomQObject *>(derived.get()); + CustomQObjectDerived *object = var.value<CustomQObjectDerived *>(); + QVERIFY(object); + } + { + // QVariant forbids unsafe upcasting + QScopedPointer<CustomQObject> base {new CustomQObject}; + QVariant var = QVariant::fromValue(base.get()); + CustomQObjectDerived *object = var.value<CustomQObjectDerived *>(); + QVERIFY(!object); + } + { + // QVariant does not support upcastingfor non-QObjects + QScopedPointer<NonQObjectDerived> derived {new NonQObjectDerived}; + QVariant var = QVariant::fromValue<NonQObjectBase *>(derived.get()); + NonQObjectDerived *object = var.value<NonQObjectDerived *>(); + QVERIFY(!object); + } +} + void tst_QVariant::convertBoolToByteArray() const { QFETCH(QByteArray, input); @@ -2983,7 +3650,7 @@ void tst_QVariant::convertByteArrayToBool() const QFETCH(QByteArray, output); const QVariant variant(input); - QCOMPARE(variant.type(), QVariant::Bool); + QCOMPARE(variant.typeId(), QMetaType::Bool); QCOMPARE(variant.toBool(), input); QVERIFY(variant.canConvert<bool>()); @@ -3009,60 +3676,88 @@ void tst_QVariant::convertIterables() const { QStringList list; list.append("Hello"); - QCOMPARE(QVariant::fromValue(list).value<QVariantList>().count(), list.count()); + QCOMPARE(QVariant::fromValue(list).value<QVariantList>().size(), list.size()); } { QByteArrayList list; list.append("Hello"); - QCOMPARE(QVariant::fromValue(list).value<QVariantList>().count(), list.count()); + QCOMPARE(QVariant::fromValue(list).value<QVariantList>().size(), list.size()); } { QVariantList list; list.append("World"); - QCOMPARE(QVariant::fromValue(list).value<QVariantList>().count(), list.count()); + QCOMPARE(QVariant::fromValue(list).value<QVariantList>().size(), list.size()); } { QMap<QString, int> map; map.insert("3", 4); - QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().count(), map.count()); - QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().count(), map.count()); + QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().size(), map.size()); + QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().size(), map.size()); map.insert("4", 5); - QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().count(), map.count()); - QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().count(), map.count()); + QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().size(), map.size()); + QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().size(), map.size()); } { QVariantMap map; map.insert("3", 4); - QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().count(), map.count()); - QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().count(), map.count()); + QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().size(), map.size()); + QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().size(), map.size()); map.insert("4", 5); - QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().count(), map.count()); - QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().count(), map.count()); + QCOMPARE(QVariant::fromValue(map).value<QVariantHash>().size(), map.size()); + QCOMPARE(QVariant::fromValue(map).value<QVariantMap>().size(), map.size()); } { QHash<QString, int> hash; hash.insert("3", 4); - QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().count(), hash.count()); - QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().count(), hash.count()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().size(), hash.size()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().size(), hash.size()); hash.insert("4", 5); - QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().count(), hash.count()); - QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().count(), hash.count()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().size(), hash.size()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().size(), hash.size()); } { QVariantHash hash; hash.insert("3", 4); - QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().count(), hash.count()); - QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().count(), hash.count()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().size(), hash.size()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().size(), hash.size()); hash.insert("4", 5); - QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().count(), hash.count()); - QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().count(), hash.count()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantHash>().size(), hash.size()); + QCOMPARE(QVariant::fromValue(hash).value<QVariantMap>().size(), hash.size()); } } +struct Derived : QObject +{ + Q_OBJECT +}; + +void tst_QVariant::convertConstNonConst() const +{ + Derived *derived = new Derived; + QObject *obj = derived; + QObject const *unrelatedConstObj = new QObject; + auto cleanUp = qScopeGuard([&] { + delete unrelatedConstObj; + delete derived; + }); + QObject const *constObj = obj; + Derived const *constDerived = derived; + QCOMPARE(QVariant::fromValue(constObj).value<QObject *>(), obj); + QCOMPARE(QVariant::fromValue(obj).value<QObject const *>(), constObj); + + QCOMPARE(QVariant::fromValue(constDerived).value<QObject *>(), derived); + QCOMPARE(QVariant::fromValue(derived).value<QObject const *>(), derived); + + QObject const *derivedAsConstObject = derived; + // up cast and remove const is possible, albeit dangerous + QCOMPARE(QVariant::fromValue(derivedAsConstObject).value<Derived *>(), derived); + QCOMPARE(QVariant::fromValue(unrelatedConstObj).value<Derived *>(), nullptr); +} + /*! We verify that: 1. Converting the string "9.9" to int fails. This is the behavior of @@ -3101,7 +3796,7 @@ void tst_QVariant::toIntFromDouble() const QCOMPARE((int)d, 2147483630); QVariant var(d); - QVERIFY( var.canConvert( QVariant::Int ) ); + QVERIFY(var.canConvert<int>()); bool ok; int result = var.toInt(&ok); @@ -3123,13 +3818,13 @@ void tst_QVariant::fpStringRoundtrip() const QFETCH(QVariant, number); QVariant converted = number; - QVERIFY(converted.convert(QVariant::String)); - QVERIFY(converted.convert(number.type())); + QVERIFY(converted.convert(QMetaType::fromType<QString>())); + QVERIFY(converted.convert(QMetaType(number.typeId()))); QCOMPARE(converted, number); converted = number; - QVERIFY(converted.convert(QVariant::ByteArray)); - QVERIFY(converted.convert(number.type())); + QVERIFY(converted.convert(QMetaType::fromType<QByteArray>())); + QVERIFY(converted.convert(QMetaType(number.typeId()))); QCOMPARE(converted, number); } @@ -3167,7 +3862,7 @@ void tst_QVariant::numericalConvert() } switch (v.userType()) { - case QVariant::Double: + case QMetaType::Double: QCOMPARE(v.toString() , QString::number(num, 'g', QLocale::FloatingPointShortest)); break; case QMetaType::Float: @@ -3190,7 +3885,7 @@ template<class T> void playWithVariant(const T &orig, bool isNull, const QString { QVariant v2 = v; - if (!(QTypeInfo<T>::isStatic && QTypeInfo<T>::isComplex)) { + if (QTypeInfo<T>::isRelocatable) { // Type is movable so standard comparison algorithm in QVariant should work // In a custom type QVariant is not aware of ==operator so it won't be called, // which may cause problems especially visible when using a not-movable type @@ -3207,7 +3902,7 @@ template<class T> void playWithVariant(const T &orig, bool isNull, const QString v = QVariant(); QCOMPARE(v3, v); v = v2; - if (!(QTypeInfo<T>::isStatic && QTypeInfo<T>::isComplex)) { + if (QTypeInfo<T>::isRelocatable) { // Type is movable so standard comparison algorithm in QVariant should work // In a custom type QVariant is not aware of ==operator so it won't be called, // which may cause problems especially visible when using a not-movable type @@ -3232,10 +3927,8 @@ template<class T> void playWithVariant(const T &orig, bool isNull, const QString QCOMPARE(v.toBool(), toBool); QCOMPARE(qvariant_cast<T>(v), orig); - if (qMetaTypeId<T>() != qMetaTypeId<QVariant>()) { + if (qMetaTypeId<T>() != qMetaTypeId<QVariant>()) QCOMPARE(v.userType(), qMetaTypeId<T>()); - QCOMPARE(QVariant::typeToName(QVariant::Type(v.userType())), QMetaType::typeName(qMetaTypeId<T>())); - } } #define PLAY_WITH_VARIANT(Orig, IsNull, ToString, ToDouble, ToBool) \ @@ -3289,6 +3982,7 @@ struct MyMovable MyMovable() { v = count++; } ~MyMovable() { count--; } MyMovable(const MyMovable &o) : v(o.v) { count++; } + MyMovable &operator=(const MyMovable &o) { v = o.v; return *this; } bool operator==(const MyMovable &o) const { @@ -3319,6 +4013,8 @@ struct MyNotMovable if (!ok) qFatal("MyNotMovable has been moved"); return ok; } + // Make it too big to store it in the variant itself + void *dummy[4]; }; int MyNotMovable::count = 0; @@ -3328,7 +4024,7 @@ struct MyShared : QSharedData { }; QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(MyMovable, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(MyMovable, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(MyPrimitive) @@ -3433,17 +4129,12 @@ void tst_QVariant::moreCustomTypes() // Float is converted to double, adding insignificant bits PLAY_WITH_VARIANT(12.12f, false, "12.119999885559082", 12.12f, true); - PLAY_WITH_VARIANT('a', false, "a", 'a', true); - PLAY_WITH_VARIANT((unsigned char)('a'), false, "a", 'a', true); - PLAY_WITH_VARIANT( quint8(12), false, "\xc", 12, true); - PLAY_WITH_VARIANT( qint8(13), false, "\xd", 13, true); PLAY_WITH_VARIANT(quint16(14), false, "14", 14, true); PLAY_WITH_VARIANT( qint16(15), false, "15", 15, true); PLAY_WITH_VARIANT(quint32(16), false, "16", 16, true); PLAY_WITH_VARIANT( qint32(17), false, "17", 17, true); PLAY_WITH_VARIANT(quint64(18), false, "18", 18, true); PLAY_WITH_VARIANT( qint64(19), false, "19", 19, true); - PLAY_WITH_VARIANT( qint8(-12), false, QLatin1String("\xf4"), -12, true); // qint8 is char, so it gets converted via QChar PLAY_WITH_VARIANT( qint16(-13), false, "-13", -13, true); PLAY_WITH_VARIANT( qint32(-14), false, "-14", -14, true); PLAY_WITH_VARIANT( qint64(-15), false, "-15", -15, true); @@ -3515,11 +4206,11 @@ void tst_QVariant::movabilityTest() // prepare destination memory space to which variant will be moved QVariant buffer[1]; - QCOMPARE(buffer[0].type(), QVariant::Invalid); + QCOMPARE(buffer[0].typeId(), QMetaType::UnknownType); buffer[0].~QVariant(); - memcpy(buffer, &variant, sizeof(QVariant)); - QCOMPARE(buffer[0].type(), QVariant::UserType); + memcpy(static_cast<void *>(buffer), static_cast<void *>(&variant), sizeof(QVariant)); + QVERIFY(buffer[0].typeId() > QMetaType::User); QCOMPARE(buffer[0].userType(), qMetaTypeId<MyNotMovable>()); MyNotMovable tmp(buffer[0].value<MyNotMovable>()); @@ -3531,24 +4222,24 @@ void tst_QVariant::movabilityTest() void tst_QVariant::variantInVariant() { QVariant var1 = 5; - QCOMPARE(var1.type(), QVariant::Int); + QCOMPARE(var1.typeId(), QMetaType::Int); QVariant var2 = var1; QCOMPARE(var2, var1); - QCOMPARE(var2.type(), QVariant::Int); + QCOMPARE(var2.typeId(), QMetaType::Int); QVariant var3 = QVariant::fromValue(var1); QCOMPARE(var3, var1); - QCOMPARE(var3.type(), QVariant::Int); + QCOMPARE(var3.typeId(), QMetaType::Int); QVariant var4 = qvariant_cast<QVariant>(var1); QCOMPARE(var4, var1); - QCOMPARE(var4.type(), QVariant::Int); + QCOMPARE(var4.typeId(), QMetaType::Int); QVariant var5; var5 = var1; QCOMPARE(var5, var1); - QCOMPARE(var5.type(), QVariant::Int); + QCOMPARE(var5.typeId(), QMetaType::Int); QVariant var6; var6.setValue(var1); QCOMPARE(var6, var1); - QCOMPARE(var6.type(), QVariant::Int); + QCOMPARE(var6.typeId(), QMetaType::Int); QCOMPARE(QVariant::fromValue(var1), QVariant::fromValue(var2)); QCOMPARE(qvariant_cast<QVariant>(var3), QVariant::fromValue(var4)); @@ -3556,7 +4247,7 @@ void tst_QVariant::variantInVariant() QString str("hello"); QVariant var8 = qvariant_cast<QVariant>(QVariant::fromValue(QVariant::fromValue(str))); - QCOMPARE((int)var8.type(), (int)QVariant::String); + QCOMPARE(var8.typeId(), QMetaType::QString); QCOMPARE(qvariant_cast<QString>(QVariant(qvariant_cast<QVariant>(var8))), str); QVariant var9(QMetaType::fromType<QVariant>(), &var1); @@ -3658,12 +4349,12 @@ void tst_QVariant::userConversion() void tst_QVariant::modelIndexConversion() { QVariant modelIndexVariant = QModelIndex(); - QVERIFY(modelIndexVariant.canConvert(QMetaType::QPersistentModelIndex)); - QVERIFY(modelIndexVariant.convert(QMetaType::QPersistentModelIndex)); - QCOMPARE(modelIndexVariant.type(), QVariant::PersistentModelIndex); - QVERIFY(modelIndexVariant.canConvert(QMetaType::QModelIndex)); - QVERIFY(modelIndexVariant.convert(QMetaType::QModelIndex)); - QCOMPARE(modelIndexVariant.type(), QVariant::ModelIndex); + QVERIFY(modelIndexVariant.canConvert<QPersistentModelIndex>()); + QVERIFY(modelIndexVariant.convert(QMetaType::fromType<QPersistentModelIndex>())); + QCOMPARE(modelIndexVariant.typeId(), QMetaType::QPersistentModelIndex); + QVERIFY(modelIndexVariant.canConvert(QMetaType::fromType<QModelIndex>())); + QVERIFY(modelIndexVariant.convert(QMetaType::fromType<QModelIndex>())); + QCOMPARE(modelIndexVariant.typeId(), QMetaType::QModelIndex); } class Forward; @@ -3672,7 +4363,7 @@ Q_DECLARE_METATYPE(Forward*) void tst_QVariant::forwardDeclare() { - Forward *f = 0; + Forward *f = nullptr; QVariant v = QVariant::fromValue(f); QCOMPARE(qvariant_cast<Forward*>(v), f); } @@ -3736,7 +4427,8 @@ void tst_QVariant::dataStream_data(QDataStream::Version version) path = path.prepend(":/stream/").append("/"); QDir dir(path); uint i = 0; - foreach (const QFileInfo &fileInfo, dir.entryInfoList(QStringList() << "*.bin")) { + const auto entries = dir.entryInfoList(QStringList{u"*.bin"_s}); + for (const QFileInfo &fileInfo : entries) { QTest::newRow((path + fileInfo.fileName()).toLatin1()) << fileInfo.filePath(); i += 1; } @@ -3757,15 +4449,15 @@ void tst_QVariant::loadQVariantFromDataStream(QDataStream::Version version) QVariant loadedVariant; stream >> typeName >> loadedVariant; - const int id = QMetaType::type(typeName.toLatin1()); + const int id = QMetaType::fromName(typeName.toLatin1()).id(); if (id == QMetaType::Void) { // Void type is not supported by QVariant return; } - QVariant constructedVariant(static_cast<QVariant::Type>(id)); + QVariant constructedVariant {QMetaType(id)}; QCOMPARE(constructedVariant.userType(), id); - QCOMPARE(QMetaType::typeName(loadedVariant.userType()), typeName.toLatin1().constData()); + QCOMPARE(QMetaType(loadedVariant.userType()).name(), typeName.toLatin1().constData()); QCOMPARE(loadedVariant.userType(), constructedVariant.userType()); } @@ -3780,7 +4472,7 @@ void tst_QVariant::saveQVariantFromDataStream(QDataStream::Version version) QString typeName; dataFileStream >> typeName; QByteArray data = file.readAll(); - const int id = QMetaType::type(typeName.toLatin1()); + const int id = QMetaType::fromName(typeName.toLatin1()).id(); if (id == QMetaType::Void) { // Void type is not supported by QVariant return; @@ -3791,7 +4483,7 @@ void tst_QVariant::saveQVariantFromDataStream(QDataStream::Version version) QDataStream stream(&buffer); stream.setVersion(version); - QVariant constructedVariant(static_cast<QVariant::Type>(id)); + QVariant constructedVariant {QMetaType(id)}; QCOMPARE(constructedVariant.userType(), id); stream << constructedVariant; @@ -3809,12 +4501,13 @@ void tst_QVariant::debugStream_data() QTest::addColumn<QVariant>("variant"); QTest::addColumn<int>("typeId"); for (int id = 0; id < QMetaType::LastCoreType + 1; ++id) { - const char *tagName = QMetaType::typeName(id); - if (!tagName) - continue; - if (id != QMetaType::Void) { - QTest::newRow(tagName) << QVariant(static_cast<QVariant::Type>(id)) << id; + if (id && !QMetaType::isRegistered(id)) { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + "^Trying to construct an instance of an invalid type")); } + const char *tagName = QMetaType(id).name(); + if (tagName && id != QMetaType::Void) + QTest::newRow(tagName) << QVariant(QMetaType(id)) << id; } QTest::newRow("QBitArray(111)") << QVariant(QBitArray(3, true)) << qMetaTypeId<QBitArray>(); QTest::newRow("CustomStreamableClass") << QVariant(QMetaType::fromType<CustomStreamableClass>(), 0) << qMetaTypeId<CustomStreamableClass>(); @@ -3833,6 +4526,7 @@ void tst_QVariant::debugStream() QVERIFY(msgHandler.testPassed()); } +#if QT_DEPRECATED_SINCE(6, 0) struct MessageHandlerType : public MessageHandler { MessageHandlerType(const int typeId) @@ -3843,9 +4537,11 @@ struct MessageHandlerType : public MessageHandler // Format itself is not important, but basic data as a type name should be included in the output ok = msg.startsWith("QVariant::"); QVERIFY2(ok, (QString::fromLatin1("Message is not started correctly: '") + msg + '\'').toLatin1().constData()); +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED ok &= (currentId == QMetaType::UnknownType ? msg.contains("Invalid") : msg.contains(QMetaType::typeName(currentId))); +QT_WARNING_POP QVERIFY2(ok, (QString::fromLatin1("Message doesn't contain type name: '") + msg + '\'').toLatin1().constData()); } }; @@ -3861,9 +4557,10 @@ void tst_QVariant::debugStreamType() QFETCH(int, typeId); MessageHandlerType msgHandler(typeId); - qDebug() << QVariant::Type(typeId); + QT_IGNORE_DEPRECATIONS(qDebug() << QVariant::Type(typeId);) QVERIFY(msgHandler.testPassed()); } +#endif // QT_DEPRECATED_SINCE(6, 0) void tst_QVariant::implicitConstruction() { @@ -4018,6 +4715,42 @@ struct ContainerAPI<Container, QByteArray> } }; +template<typename Container> +struct ContainerAPI<Container, QChar> +{ + static void insert(Container &container, int value) + { + container.push_back(QChar::fromLatin1(char(value) + '0')); + } + + static bool compare(const QVariant &variant, QChar value) + { + return variant.value<QChar>() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + +template<typename Container> +struct ContainerAPI<Container, char> +{ + static void insert(Container &container, int value) + { + container.push_back(char(value) + '0'); + } + + static bool compare(const QVariant &variant, char value) + { + return variant.value<char>() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + #ifdef __has_include # if __has_include(<forward_list>) # define TEST_FORWARD_LIST @@ -4105,11 +4838,6 @@ struct KeyGetter<std::map<T, U> > }; -// We have no built-in defines to check the stdlib features. -// #define TEST_UNORDERED_MAP - -#ifdef TEST_UNORDERED_MAP -#include <unordered_map> typedef std::unordered_map<int, bool> StdUnorderedMap_int_bool; Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(std::unordered_map) @@ -4128,95 +4856,254 @@ struct KeyGetter<std::unordered_map<T, U> > return it->second; } }; -#endif -void tst_QVariant::iterateContainerElements() +template<typename Iterator> +void sortIterable(QSequentialIterable *iterable) { -#ifdef Q_COMPILER_RANGE_FOR + std::sort(Iterator(iterable->mutableBegin()), Iterator(iterable->mutableEnd()), + [&](const QVariant &a, const QVariant &b) { + return a.toInt() < b.toInt(); + }); +} -#define TEST_RANGE_FOR(CONTAINER) \ - numSeen = 0; \ - containerIter = intList.begin(); \ - for (QVariant v : listIter) { \ - QVERIFY(ContainerAPI<CONTAINER >::compare(v, *containerIter)); \ - QVERIFY(ContainerAPI<CONTAINER >::compare(v, varList.at(numSeen))); \ - ++containerIter; \ - ++numSeen; \ - } \ - QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); +template<typename Container> +static void testSequentialIteration() +{ + int numSeen = 0; + Container sequence; + ContainerAPI<Container>::insert(sequence, 1); + ContainerAPI<Container>::insert(sequence, 2); + ContainerAPI<Container>::insert(sequence, 3); + + QVariant listVariant = QVariant::fromValue(sequence); + QVERIFY(listVariant.canConvert<QVariantList>()); + QVariantList varList = listVariant.value<QVariantList>(); + QCOMPARE(varList.size(), (int)std::distance(sequence.begin(), sequence.end())); + QSequentialIterable listIter = listVariant.view<QSequentialIterable>(); + QCOMPARE(varList.size(), listIter.size()); + + typename Container::iterator containerIter = sequence.begin(); + const typename Container::iterator containerEnd = sequence.end(); + for (int i = 0; i < listIter.size(); ++i, ++containerIter, ++numSeen) + { + QVERIFY(ContainerAPI<Container >::compare(listIter.at(i), *containerIter)); + QVERIFY(ContainerAPI<Container >::compare(listIter.at(i), varList.at(i))); + } + QCOMPARE(numSeen, (int)std::distance(sequence.begin(), sequence.end())); + QCOMPARE(containerIter, containerEnd); + + numSeen = 0; + containerIter = sequence.begin(); + for (QVariant v : listIter) { + QVERIFY(ContainerAPI<Container>::compare(v, *containerIter)); + QVERIFY(ContainerAPI<Container>::compare(v, varList.at(numSeen))); + ++containerIter; + ++numSeen; + } + QCOMPARE(numSeen, (int)std::distance(sequence.begin(), sequence.end())); + + auto compareLists = [&]() { + int numSeen = 0; + auto varList = listVariant.value<QVariantList>(); + auto varIter = varList.begin(); + for (const QVariant &v : std::as_const(listIter)) { + QVERIFY(ContainerAPI<Container>::compare(v, *varIter)); + ++varIter; + ++numSeen; + } + QCOMPARE(varIter, varList.end()); + numSeen = 0; + auto constVarIter = varList.constBegin(); + for (QVariant v : listIter) { + QVERIFY(ContainerAPI<Container>::compare(v, *constVarIter)); + ++constVarIter; + ++numSeen; + } + QCOMPARE(numSeen, (int)std::distance(varList.begin(), varList.end())); + }; + compareLists(); + + QVariant first = listIter.at(0); + QVariant second = listIter.at(1); + QVariant third = listIter.at(2); + compareLists(); + listIter.addValue(third); + compareLists(); + listIter.addValue(second); + compareLists(); + listIter.addValue(first); + compareLists(); + + QCOMPARE(listIter.size(), 6); + + if (listIter.canRandomAccessIterate()) + sortIterable<QSequentialIterable::RandomAccessIterator>(&listIter); + else if (listIter.canReverseIterate()) + sortIterable<QSequentialIterable::BidirectionalIterator>(&listIter); + else if (listIter.canForwardIterate()) + return; // std::sort cannot sort with only forward iterators. + else + QFAIL("The container has no meaningful iterators"); + + compareLists(); + QCOMPARE(listIter.size(), 6); + QCOMPARE(listIter.at(0), first); + QCOMPARE(listIter.at(1), first); + QCOMPARE(listIter.at(2), second); + QCOMPARE(listIter.at(3), second); + QCOMPARE(listIter.at(4), third); + QCOMPARE(listIter.at(5), third); + + if (listIter.metaContainer().canRemoveValue()) { + listIter.removeValue(); + compareLists(); + QCOMPARE(listIter.size(), 5); + QCOMPARE(listIter.at(0), first); + QCOMPARE(listIter.at(1), first); + QCOMPARE(listIter.at(2), second); + QCOMPARE(listIter.at(3), second); + QCOMPARE(listIter.at(4), third); + } else { + // QString and QByteArray have no pop_back or pop_front and it's unclear what other + // method we should use to remove an item. + QVERIFY((std::is_same_v<Container, QString> || std::is_same_v<Container, QByteArray>)); + } -#else + auto i = listIter.mutableBegin(); + QVERIFY(i != listIter.mutableEnd()); -#define TEST_RANGE_FOR(CONTAINER) + *i = QStringLiteral("17"); + if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<int>()) + QCOMPARE(listIter.at(0).toInt(), 17); + else if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(listIter.at(0).toBool(), false); -#endif + *i = QStringLiteral("true"); + if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<int>()) + QCOMPARE(listIter.at(0).toInt(), 0); + else if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(listIter.at(0).toBool(), true); +} -#define TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(CONTAINER) \ - { \ - int numSeen = 0; \ - CONTAINER intList; \ - ContainerAPI<CONTAINER >::insert(intList, 1); \ - ContainerAPI<CONTAINER >::insert(intList, 2); \ - ContainerAPI<CONTAINER >::insert(intList, 3); \ - \ - QVariant listVariant = QVariant::fromValue(intList); \ - QVERIFY(listVariant.canConvert<QVariantList>()); \ - QVariantList varList = listVariant.value<QVariantList>(); \ - QCOMPARE(varList.size(), (int)std::distance(intList.begin(), intList.end())); \ - QSequentialIterable listIter = listVariant.value<QSequentialIterable>(); \ - QCOMPARE(varList.size(), listIter.size()); \ - \ - CONTAINER::iterator containerIter = intList.begin(); \ - const CONTAINER::iterator containerEnd = intList.end(); \ - for (int i = 0; i < listIter.size(); ++i, ++containerIter, ++numSeen) \ - { \ - QVERIFY(ContainerAPI<CONTAINER >::compare(listIter.at(i), *containerIter)); \ - QVERIFY(ContainerAPI<CONTAINER >::compare(listIter.at(i), varList.at(i))); \ - } \ - QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \ - QCOMPARE(containerIter, containerEnd); \ - \ - containerIter = intList.begin(); \ - numSeen = 0; \ - Q_FOREACH (const QVariant &v, listIter) { \ - QVERIFY(ContainerAPI<CONTAINER >::compare(v, *containerIter)); \ - QVERIFY(ContainerAPI<CONTAINER >::compare(v, varList.at(numSeen))); \ - ++containerIter; \ - ++numSeen; \ - } \ - QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \ - TEST_RANGE_FOR(CONTAINER) \ - } - -#define TEST_SEQUENTIAL_ITERATION(CONTAINER, VALUE_TYPE) \ - TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(CONTAINER<VALUE_TYPE > ) - - - TEST_SEQUENTIAL_ITERATION(QQueue, int) - TEST_SEQUENTIAL_ITERATION(QQueue, QVariant) - TEST_SEQUENTIAL_ITERATION(QQueue, QString) - TEST_SEQUENTIAL_ITERATION(QList, int) - TEST_SEQUENTIAL_ITERATION(QList, QVariant) - TEST_SEQUENTIAL_ITERATION(QList, QString) - TEST_SEQUENTIAL_ITERATION(QList, QByteArray) - TEST_SEQUENTIAL_ITERATION(QStack, int) - TEST_SEQUENTIAL_ITERATION(QStack, QVariant) - TEST_SEQUENTIAL_ITERATION(QStack, QString) - TEST_SEQUENTIAL_ITERATION(std::vector, int) - TEST_SEQUENTIAL_ITERATION(std::vector, QVariant) - TEST_SEQUENTIAL_ITERATION(std::vector, QString) - TEST_SEQUENTIAL_ITERATION(std::list, int) - TEST_SEQUENTIAL_ITERATION(std::list, QVariant) - TEST_SEQUENTIAL_ITERATION(std::list, QString) - TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(QStringList) - TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(QByteArrayList) +template<typename Container> +static void testAssociativeIteration() +{ + using Key = typename Container::key_type; + using Mapped = typename Container::mapped_type; + + int numSeen = 0; + Container mapping; + mapping[5] = true; + mapping[15] = false; + + QVariant mappingVariant = QVariant::fromValue(mapping); + QVariantMap varMap = mappingVariant.value<QVariantMap>(); + QVariantMap varHash = mappingVariant.value<QVariantMap>(); + QAssociativeIterable mappingIter = mappingVariant.view<QAssociativeIterable>(); + + typename Container::const_iterator containerIter = mapping.begin(); + const typename Container::const_iterator containerEnd = mapping.end(); + for ( ;containerIter != containerEnd; ++containerIter, ++numSeen) + { + Mapped expected = KeyGetter<Container>::value(containerIter); + Key key = KeyGetter<Container>::get(containerIter); + Mapped actual = qvariant_cast<Mapped>(mappingIter.value(key)); + QCOMPARE(qvariant_cast<Mapped>(varMap.value(QString::number(key))), expected); + QCOMPARE(qvariant_cast<Mapped>(varHash.value(QString::number(key))), expected); + QCOMPARE(actual, expected); + const QAssociativeIterable::const_iterator it = mappingIter.find(key); + QVERIFY(it != mappingIter.end()); + QCOMPARE(it.value().value<Mapped>(), expected); + } + QCOMPARE(numSeen, (int)std::distance(mapping.begin(), mapping.end())); + QCOMPARE(containerIter, containerEnd); + QVERIFY(mappingIter.find(10) == mappingIter.end()); + + auto i = mappingIter.mutableFind(QStringLiteral("nonono")); + QCOMPARE(i, mappingIter.mutableEnd()); + i = mappingIter.mutableFind(QStringLiteral("5")); + QVERIFY(i != mappingIter.mutableEnd()); + + *i = QStringLiteral("17"); + + if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>()) + QCOMPARE(mappingIter.value(5).toInt(), 17); + else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(mappingIter.value(5).toBool(), true); + + *i = QStringLiteral("true"); + if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>()) + QCOMPARE(mappingIter.value(5).toInt(), 0); + else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(mappingIter.value(5).toBool(), true); + + QVERIFY(mappingIter.containsKey("5")); + mappingIter.removeKey(QStringLiteral("5")); + QCOMPARE(mappingIter.find(5), mappingIter.end()); + + mappingIter.setValue(5, 44); + if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>()) + QCOMPARE(mappingIter.value(5).toInt(), 44); + else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(mappingIter.value(5).toBool(), true); + + // Test that find() does not coerce + auto container = Container(); + container[0] = true; + + QVariant containerVariant = QVariant::fromValue(container); + QAssociativeIterable iter = containerVariant.value<QAssociativeIterable>(); + auto f = iter.constFind(QStringLiteral("anything")); + QCOMPARE(f, iter.constEnd()); +} + +void tst_QVariant::iterateSequentialContainerElements_data() +{ + QTest::addColumn<QFunctionPointer>("testFunction"); +#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> + ADD(QQueue<int>); + ADD(QQueue<QVariant>); + ADD(QQueue<QString>); + ADD(QList<int>); + ADD(QList<QVariant>); + ADD(QList<QString>); + ADD(QList<QByteArray>); + ADD(QStack<int>); + ADD(QStack<QVariant>); + ADD(QStack<QString>); + ADD(std::vector<int>); + ADD(std::vector<QVariant>); + ADD(std::vector<QString>); + ADD(std::list<int>); + ADD(std::list<QVariant>); + ADD(std::list<QString>); + ADD(QStringList); + ADD(QByteArrayList); + ADD(QString); + ADD(QByteArray); #ifdef TEST_FORWARD_LIST - TEST_SEQUENTIAL_ITERATION(std::forward_list, int) - TEST_SEQUENTIAL_ITERATION(std::forward_list, QVariant) - TEST_SEQUENTIAL_ITERATION(std::forward_list, QString) + ADD(std::forward_list<int>); + ADD(std::forward_list<QVariant>); + ADD(std::forward_list<QString>); #endif +#undef ADD +} + +void tst_QVariant::iterateAssociativeContainerElements_data() +{ + QTest::addColumn<QFunctionPointer>("testFunction"); +#define ADD(C, K, V) QTest::newRow(#C #K #V) << &testAssociativeIteration<C<K, V>>; + ADD(QHash, int, bool); + ADD(QHash, int, int); + ADD(QMap, int, bool); + ADD(std::map, int, bool); + ADD(std::unordered_map, int, bool); +#undef ADD +} +void tst_QVariant::iterateContainerElements() +{ { QVariantList ints; ints << 1 << 2 << 3; @@ -4239,44 +5126,6 @@ void tst_QVariant::iterateContainerElements() QCOMPARE(ints, intsCopy); } -#define TEST_ASSOCIATIVE_ITERATION(CONTAINER, KEY_TYPE, MAPPED_TYPE) \ - { \ - int numSeen = 0; \ - CONTAINER<KEY_TYPE, MAPPED_TYPE> mapping; \ - mapping[5] = true; \ - mapping[15] = false; \ - \ - QVariant mappingVariant = QVariant::fromValue(mapping); \ - QVariantMap varMap = mappingVariant.value<QVariantMap>(); \ - QVariantMap varHash = mappingVariant.value<QVariantMap>(); \ - QAssociativeIterable mappingIter = mappingVariant.value<QAssociativeIterable>(); \ - \ - CONTAINER<KEY_TYPE, MAPPED_TYPE>::const_iterator containerIter = mapping.begin(); \ - const CONTAINER<KEY_TYPE, MAPPED_TYPE>::const_iterator containerEnd = mapping.end(); \ - for ( ; containerIter != containerEnd; ++containerIter, ++numSeen) \ - { \ - MAPPED_TYPE expected = KeyGetter<CONTAINER<KEY_TYPE, MAPPED_TYPE> >::value(containerIter); \ - KEY_TYPE key = KeyGetter<CONTAINER<KEY_TYPE, MAPPED_TYPE> >::get(containerIter); \ - MAPPED_TYPE actual = mappingIter.value(key).value<MAPPED_TYPE >(); \ - QCOMPARE(varMap.value(QString::number(key)).value<MAPPED_TYPE>(), expected); \ - QCOMPARE(varHash.value(QString::number(key)).value<MAPPED_TYPE>(), expected); \ - QCOMPARE(actual, expected); \ - const QAssociativeIterable::const_iterator it = mappingIter.find(key); \ - QVERIFY(it != mappingIter.end()); \ - QCOMPARE(it.value().value<MAPPED_TYPE>(), expected); \ - } \ - QCOMPARE(numSeen, (int)std::distance(mapping.begin(), mapping.end())); \ - QCOMPARE(containerIter, containerEnd); \ - QVERIFY(mappingIter.find(10) == mappingIter.end()); \ - } - - TEST_ASSOCIATIVE_ITERATION(QHash, int, bool) - TEST_ASSOCIATIVE_ITERATION(QMap, int, bool) - TEST_ASSOCIATIVE_ITERATION(std::map, int, bool) -#ifdef TEST_UNORDERED_MAP - TEST_ASSOCIATIVE_ITERATION(std::unordered_map, int, bool) -#endif - { QMap<int, QString> mapping; mapping.insert(1, "one"); @@ -4303,58 +5152,77 @@ void tst_QVariant::iterateContainerElements() numsCopy << *(it++); QCOMPARE(nums, numsCopy); } + + { + auto container = QVariantMap(); + + container["one"] = 1; + + auto containerVariant = QVariant::fromValue(container); + auto iter = containerVariant.value<QAssociativeIterable>(); + auto value = iter.value("one"); + QCOMPARE(value, QVariant(1)); + + auto f = iter.constFind("one"); + QCOMPARE(*f, QVariant(1)); + } } -void tst_QVariant::pairElements() +template <typename Pair> static void testVariantPairElements() { - typedef QPair<QVariant, QVariant> QVariantPair; + QFETCH(std::function<void(void *)>, makeValue); + Pair p; + makeValue(&p); + QVariant v = QVariant::fromValue(p); -#define TEST_PAIR_ELEMENT_ACCESS(PAIR, T1, T2, VALUE1, VALUE2) \ - { \ - PAIR<T1, T2> p(VALUE1, VALUE2); \ - QVariant v = QVariant::fromValue(p); \ - \ - QVERIFY(v.canConvert<QVariantPair>()); \ - QVariantPair pi = v.value<QVariantPair>(); \ - QCOMPARE(pi.first, QVariant::fromValue(VALUE1)); \ - QCOMPARE(pi.second, QVariant::fromValue(VALUE2)); \ - } - - TEST_PAIR_ELEMENT_ACCESS(QPair, int, int, 4, 5) - TEST_PAIR_ELEMENT_ACCESS(std::pair, int, int, 4, 5) - TEST_PAIR_ELEMENT_ACCESS(QPair, QString, QString, QStringLiteral("one"), QStringLiteral("two")) - TEST_PAIR_ELEMENT_ACCESS(std::pair, QString, QString, QStringLiteral("one"), QStringLiteral("two")) - TEST_PAIR_ELEMENT_ACCESS(QPair, QVariant, QVariant, 4, 5) - TEST_PAIR_ELEMENT_ACCESS(std::pair, QVariant, QVariant, 4, 5) - TEST_PAIR_ELEMENT_ACCESS(QPair, QVariant, int, 41, 15) - TEST_PAIR_ELEMENT_ACCESS(std::pair, QVariant, int, 34, 65) - TEST_PAIR_ELEMENT_ACCESS(QPair, int, QVariant, 24, 25) - TEST_PAIR_ELEMENT_ACCESS(std::pair, int, QVariant, 44, 15) + QVERIFY(v.canConvert<QVariantPair>()); + QVariantPair pi = v.value<QVariantPair>(); + QCOMPARE(pi.first, QVariant::fromValue(p.first)); + QCOMPARE(pi.second, QVariant::fromValue(p.second)); } -enum EnumTest_Enum0 { EnumTest_Enum0_value = 42, EnumTest_Enum0_negValue = -8 }; -Q_DECLARE_METATYPE(EnumTest_Enum0) -enum EnumTest_Enum1 : qint64 { EnumTest_Enum1_value = 42, EnumTest_Enum1_bigValue = (Q_INT64_C(1) << 33) + 50 }; -Q_DECLARE_METATYPE(EnumTest_Enum1) +void tst_QVariant::pairElements_data() +{ + QTest::addColumn<QFunctionPointer>("testFunction"); + QTest::addColumn<std::function<void(void *)>>("makeValue"); -enum EnumTest_Enum3 : qint64 { EnumTest_Enum3_value = -47, EnumTest_Enum3_bigValue = (Q_INT64_C(1) << 56) + 5 }; -Q_DECLARE_METATYPE(EnumTest_Enum3) -enum EnumTest_Enum4 : quint64 { EnumTest_Enum4_value = 47, EnumTest_Enum4_bigValue = (Q_INT64_C(1) << 52) + 45 }; -Q_DECLARE_METATYPE(EnumTest_Enum4) -enum EnumTest_Enum5 : uint { EnumTest_Enum5_value = 47 }; -Q_DECLARE_METATYPE(EnumTest_Enum5) -enum EnumTest_Enum6 : uchar { EnumTest_Enum6_value = 47 }; -Q_DECLARE_METATYPE(EnumTest_Enum6) -enum class EnumTest_Enum7 { EnumTest_Enum7_value = 47, ensureSignedEnum7 = -1 }; -Q_DECLARE_METATYPE(EnumTest_Enum7) -enum EnumTest_Enum8 : short { EnumTest_Enum8_value = 47 }; -Q_DECLARE_METATYPE(EnumTest_Enum8) + static auto makeString = [](auto &&value) -> QString { + using T = std::decay_t<decltype(value)>; + if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) { + return QString::number(value); + } else if constexpr (std::is_same_v<T, QVariant>) { + return value.toString(); + } else { + return value; + } + }; + auto addRow = [](auto &&first, auto &&second) { + using Pair = std::pair<std::decay_t<decltype(first)>, std::decay_t<decltype(second)>>; + std::function<void(void *)> makeValue = [=](void *pair) { + *static_cast<Pair *>(pair) = Pair{first, second}; + }; + + QTest::addRow("%s", qPrintable(makeString(first) + u',' + makeString(second))) + << &testVariantPairElements<Pair> << makeValue; + }; -template<typename Enum> void testVariant(Enum value, bool *ok) + addRow(4, 5); + addRow(QStringLiteral("one"), QStringLiteral("two")); + addRow(QVariant(4), QVariant(5)); + addRow(QVariant(41), 65); + addRow(41, QVariant(15)); +} + +template <auto value> static void testVariantEnum() { - *ok = false; - QVariant var = QVariant::fromValue(value); + using Enum = decltype(value); + auto canLosslesslyConvert = [=](auto zero) { + return sizeof(value) <= sizeof(zero) || + value == Enum(decltype(zero)(qToUnderlying(value))); + }; + bool losslessConvertToInt = canLosslesslyConvert(int{}); + QVariant var = QVariant::fromValue(value); QCOMPARE(var.userType(), qMetaTypeId<Enum>()); QVERIFY(var.canConvert<Enum>()); @@ -4365,25 +5233,27 @@ template<typename Enum> void testVariant(Enum value, bool *ok) QVERIFY(var.canConvert<qint64>()); QVERIFY(var.canConvert<quint64>()); - QCOMPARE(var.value<Enum>(), value); QCOMPARE(var.value<int>(), static_cast<int>(value)); QCOMPARE(var.value<uint>(), static_cast<uint>(value)); QCOMPARE(var.value<short>(), static_cast<short>(value)); QCOMPARE(var.value<unsigned short>(), static_cast<unsigned short>(value)); QCOMPARE(var.value<qint64>(), static_cast<qint64>(value)); - if (sizeof(value) < 8 && static_cast<qint64>(value) < 0) { - QEXPECT_FAIL("", "The metatype system don't store the sign of enums", Continue); - // The value is stored internaly with 32 bit. When asked to convert it to 64 bit unsigned, - // we consider that the value was unsigned, so we don't extent the bit signs - } QCOMPARE(var.value<quint64>(), static_cast<quint64>(value)); QVariant var2 = var; - QVERIFY(var2.convert(QMetaType::Int)); + QVERIFY(var2.convert(QMetaType::fromType<int>())); QCOMPARE(var2.value<int>(), static_cast<int>(value)); - if ((static_cast<qint64>(value) <= INT_MAX) && (static_cast<qint64>(value) >= INT_MIN)) { + QVariant strVar = QString::number(qToUnderlying(value)); + QVariant baVar = QByteArray::number(qToUnderlying(value)); + QCOMPARE(strVar.value<Enum>(), value); + QCOMPARE(baVar.value<Enum>(), value); + QCOMPARE(var.value<QString>(), strVar); + QCOMPARE(var.value<QByteArray>(), baVar); + + // unary + to silence gcc warning + if (losslessConvertToInt) { int intValue = static_cast<int>(value); QVariant intVar = intValue; QVERIFY(intVar.canConvert<Enum>()); @@ -4393,71 +5263,126 @@ template<typename Enum> void testVariant(Enum value, bool *ok) QVERIFY(QVariant(longValue).canConvert<Enum>()); QCOMPARE(QVariant(longValue).value<Enum>(), value); - *ok = true; -} - -void tst_QVariant::enums() -{ - bool ok = false; - testVariant(EnumTest_Enum0_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum0_negValue, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum1_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum1_bigValue, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum3::EnumTest_Enum3_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum3::EnumTest_Enum3_bigValue, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum4::EnumTest_Enum4_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum4::EnumTest_Enum4_bigValue, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum5::EnumTest_Enum5_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum6::EnumTest_Enum6_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum7::EnumTest_Enum7_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum8::EnumTest_Enum8_value, &ok); - QVERIFY(ok); - testVariant(EnumTest_Enum3::EnumTest_Enum3_value, &ok); - QVERIFY(ok); -} - -template<typename Enum> void testVariantMeta(Enum value, bool *ok, const char *string) -{ - testVariant<Enum>(value, ok); - QVERIFY(ok); - *ok = false; + auto value2 = Enum(qToUnderlying(value) + 1); + var2 = QVariant::fromValue(value2); + QCOMPARE_EQ(var, var); + QCOMPARE_NE(var, var2); + QCOMPARE(QVariant::compare(var, var), QPartialOrdering::Equivalent); + QCOMPARE(QVariant::compare(var, var2), QPartialOrdering::Less); + QCOMPARE(QVariant::compare(var2, var), QPartialOrdering::Greater); + + QCOMPARE_EQ(var, static_cast<qint64>(value)); + QCOMPARE_EQ(var, static_cast<quint64>(value)); + QCOMPARE_EQ(static_cast<qint64>(value), var); + QCOMPARE_EQ(static_cast<quint64>(value), var); + QCOMPARE_NE(var2, static_cast<qint64>(value)); + QCOMPARE_NE(var2, static_cast<quint64>(value)); + QCOMPARE_NE(static_cast<qint64>(value), var2); + QCOMPARE_NE(static_cast<quint64>(value), var2); + + if (losslessConvertToInt) { + QCOMPARE_EQ(var, int(value)); + QCOMPARE_EQ(int(value), var); + QCOMPARE_NE(var2, int(value)); + QCOMPARE_NE(int(value), var2); + } + if (canLosslesslyConvert(uint{})) { + QCOMPARE_EQ(var, uint(value)); + QCOMPARE_EQ(uint(value), var); + QCOMPARE_NE(var2, uint(value)); + QCOMPARE_NE(uint(value), var2); + } + if (canLosslesslyConvert(short{})) { + QCOMPARE_EQ(var, short(value)); + QCOMPARE_EQ(short(value), var); + QCOMPARE_NE(var2, short(value)); + QCOMPARE_NE(short(value), var2); + } + if (canLosslesslyConvert(ushort{})) { + QCOMPARE_EQ(var, ushort(value)); + QCOMPARE_EQ(ushort(value), var); + QCOMPARE_NE(var2, ushort(value)); + QCOMPARE_NE(ushort(value), var2); + } + if (canLosslesslyConvert(char{})) { + QCOMPARE_EQ(var, char(value)); + QCOMPARE_EQ(char(value), var); + QCOMPARE_NE(var2, char(value)); + QCOMPARE_NE(char(value), var2); + } + if (canLosslesslyConvert(uchar{})) { + QCOMPARE_EQ(var, uchar(value)); + QCOMPARE_EQ(uchar(value), var); + QCOMPARE_NE(var2, uchar(value)); + QCOMPARE_NE(uchar(value), var2); + } + if (canLosslesslyConvert(qint8{})) { + QCOMPARE_EQ(var, qint8(value)); + QCOMPARE_EQ(qint8(value), var); + QCOMPARE_NE(var2, qint8(value)); + QCOMPARE_NE(qint8(value), var2); + } + + // string compares too (of the values in decimal) + QCOMPARE_EQ(var, QString::number(qToUnderlying(value))); + QCOMPARE_EQ(QString::number(qToUnderlying(value)), var); + QCOMPARE_NE(var, QString::number(qToUnderlying(value2))); + QCOMPARE_NE(QString::number(qToUnderlying(value2)), var); +} + +void tst_QVariant::enums_data() +{ + QTest::addColumn<QFunctionPointer>("testFunction"); + +#define ADD(V) QTest::newRow(#V) << &testVariantEnum<V> + ADD(EnumTest_Enum0_value); + ADD(EnumTest_Enum0_negValue); + ADD(EnumTest_Enum1_value); + ADD(EnumTest_Enum1_bigValue); + ADD(EnumTest_Enum3::EnumTest_Enum3_value); + ADD(EnumTest_Enum3::EnumTest_Enum3_bigValue); + ADD(EnumTest_Enum4::EnumTest_Enum4_value); + ADD(EnumTest_Enum4::EnumTest_Enum4_bigValue); + ADD(EnumTest_Enum5::EnumTest_Enum5_value); + ADD(EnumTest_Enum6::EnumTest_Enum6_value); + ADD(EnumTest_Enum7::EnumTest_Enum7_value); + ADD(EnumTest_Enum8::EnumTest_Enum8_value); + ADD(EnumTest_Enum3::EnumTest_Enum3_value); +#undef ADD +} + +// ### C++20: this would be easier if QFlags were a structural type +template <typename Enum, auto Value> static void testVariantMetaEnum() +{ + Enum value(Value); + QFETCH(QString, string); QVariant var = QVariant::fromValue(value); QVERIFY(var.canConvert<QString>()); QVERIFY(var.canConvert<QByteArray>()); - QCOMPARE(var.value<QString>(), QString::fromLatin1(string)); - QCOMPARE(var.value<QByteArray>(), QByteArray(string)); + QCOMPARE(var.value<QString>(), string); + QCOMPARE(var.value<QByteArray>(), string.toLatin1()); - QVariant strVar = QString::fromLatin1(string); + QVariant strVar = string; QVERIFY(strVar.canConvert<Enum>()); - if ((static_cast<qint64>(value) > INT_MAX) || (static_cast<qint64>(value) < INT_MIN)) { + // unary + to silence gcc warning + if ((+static_cast<qint64>(value) > INT_MAX) || (+static_cast<qint64>(value) < INT_MIN)) { QEXPECT_FAIL("", "QMetaEnum api uses 'int' as return type QTBUG-27451", Abort); - *ok = true; } QCOMPARE(strVar.value<Enum>(), value); - strVar = QByteArray(string); + strVar = string.toLatin1(); QVERIFY(strVar.canConvert<Enum>()); QCOMPARE(strVar.value<Enum>(), value); - *ok = true; } -void tst_QVariant::metaEnums() +void tst_QVariant::metaEnums_data() { - bool ok = false; + QTest::addColumn<QFunctionPointer>("testFunction"); + QTest::addColumn<QString>("string"); + #define METAENUMS_TEST(Value) \ - testVariantMeta(Value, &ok, #Value); QVERIFY(ok) + QTest::newRow(#Value) << &testVariantMetaEnum<decltype(Value), Value> << #Value; METAENUMS_TEST(MetaEnumTest_Enum0_value); METAENUMS_TEST(MetaEnumTest_Enum1_value); @@ -4470,17 +5395,26 @@ void tst_QVariant::metaEnums() METAENUMS_TEST(MetaEnumTest_Enum5_value); METAENUMS_TEST(MetaEnumTest_Enum6_value); METAENUMS_TEST(MetaEnumTest_Enum8_value); + { using namespace Qt; METAENUMS_TEST(RichText); } +#undef METAENUMS_TEST + + QTest::newRow("AlignBottom") + << &testVariantMetaEnum<Qt::Alignment, Qt::AlignBottom> << "AlignBottom"; + + constexpr auto AlignHCenterBottom = Qt::AlignmentFlag((Qt::AlignHCenter | Qt::AlignBottom).toInt()); + QTest::newRow("AlignHCenter|AlignBottom") + << &testVariantMetaEnum<Qt::Alignment, AlignHCenterBottom> << "AlignHCenter|AlignBottom"; } void tst_QVariant::nullConvert() { // null variant with no initialized value - QVariant nullVar(QVariant::String); + QVariant nullVar {QMetaType::fromType<QString>()}; QVERIFY(nullVar.isValid()); QVERIFY(nullVar.isNull()); // We can not convert a variant with no value - QVERIFY(!nullVar.convert(QVariant::Url)); - QCOMPARE(nullVar.type(), QVariant::Url); + QVERIFY(!nullVar.convert(QMetaType::fromType<QUrl>())); + QCOMPARE(nullVar.typeId(), QMetaType::QUrl); QVERIFY(nullVar.isNull()); } @@ -4512,29 +5446,39 @@ void tst_QVariant::shouldDeleteVariantDataWorksForSequential() { QCOMPARE(instanceCount, 0); { - QtMetaTypePrivate::QSequentialIterableImpl iterator {}; - iterator._iteratorCapabilities = QtMetaTypePrivate::RandomAccessCapability | - QtMetaTypePrivate::BiDirectionalCapability | - QtMetaTypePrivate::ForwardCapability; - iterator._metaType_flags = QVariantConstructionFlags::ShouldDeleteVariantData; - - iterator._size = [](const void *) {return 1;}; - iterator._metaType_id = qMetaTypeId<MyType>(); - iterator._moveToBegin = [](const void *, void **) {}; - iterator._moveToEnd = [](const void *, void **) {}; - iterator._advance = [](void **, int) {}; - iterator._destroyIter = [](void **){}; - iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/}; - iterator._destroyIter = [](void **){}; - iterator._at = [](const void *, int ) -> void const * { + QtMetaContainerPrivate::QMetaSequenceInterface metaSequence {}; + metaSequence.iteratorCapabilities = QtMetaContainerPrivate::RandomAccessCapability + | QtMetaContainerPrivate::BiDirectionalCapability + | QtMetaContainerPrivate::ForwardCapability + | QtMetaContainerPrivate::InputCapability; + + metaSequence.sizeFn = [](const void *) { return qsizetype(1); }; + metaSequence.createConstIteratorFn = + [](const void *, QtMetaContainerPrivate::QMetaSequenceInterface::Position) -> void* { + return nullptr; + }; + metaSequence.addValueFn = [](void *, const void *, + QtMetaContainerPrivate::QMetaSequenceInterface::Position) {}; + metaSequence.advanceConstIteratorFn = [](void *, qsizetype) {}; + metaSequence.destroyConstIteratorFn = [](const void *){}; + metaSequence.compareConstIteratorFn = [](const void *, const void *) { + return true; // all iterators are nullptr + }; + metaSequence.copyConstIteratorFn = [](void *, const void *){}; + metaSequence.diffConstIteratorFn = [](const void *, const void *) -> qsizetype { + return 0; + }; + metaSequence.valueAtIndexFn = [](const void *, qsizetype, void *dataPtr) -> void { MyType mytype {1, "eins"}; - return QMetaType::create(qMetaTypeId<MyType>(), &mytype); + *static_cast<MyType *>(dataPtr) = mytype; }; - iterator._get = [](void * const *, int, uint) -> QtMetaTypePrivate::VariantData { + metaSequence.valueAtConstIteratorFn = [](const void *, void *dataPtr) -> void { MyType mytype {2, "zwei"}; - return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData}; + *static_cast<MyType *>(dataPtr) = mytype; }; - QSequentialIterable iterable {iterator}; + metaSequence.valueMetaType = QtPrivate::qMetaTypeInterfaceForType<MyType>(); + + QSequentialIterable iterable(QMetaSequence(&metaSequence), nullptr); QVariant value1 = iterable.at(0); QVERIFY(value1.canConvert<MyType>()); QCOMPARE(value1.value<MyType>().number, 1); @@ -4549,39 +5493,42 @@ void tst_QVariant::shouldDeleteVariantDataWorksForAssociative() { QCOMPARE(instanceCount, 0); { - QtMetaTypePrivate::QAssociativeIterableImpl iterator {}; - iterator._metaType_flags_key = QVariantConstructionFlags::ShouldDeleteVariantData; - iterator._metaType_flags_value = QVariantConstructionFlags::ShouldDeleteVariantData; - - iterator._size = [](const void *) {return 1;}; - iterator._metaType_id_value = qMetaTypeId<MyType>(); - iterator._metaType_id_key = qMetaTypeId<MyType>(); - iterator._begin = [](const void *, void **) {}; - iterator._end = [](const void *, void **) {}; - iterator._advance = [](void **, int) {}; - iterator._destroyIter = [](void **){}; - iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/}; - iterator._destroyIter = [](void **){}; - iterator._find = [](const void *, const void *, void **iterator ) -> void { - (*iterator) = reinterpret_cast<void *>(quintptr(42)); + QtMetaContainerPrivate::QMetaAssociationInterface iterator {}; + + iterator.sizeFn = [](const void *) -> qsizetype {return 1;}; + iterator.mappedMetaType = QtPrivate::qMetaTypeInterfaceForType<MyType>(); + iterator.keyMetaType = QtPrivate::qMetaTypeInterfaceForType<MyType>(); + iterator.createConstIteratorFn = []( + const void *, QtMetaContainerPrivate::QMetaContainerInterface::Position) -> void * { + return nullptr; + }; + iterator.advanceConstIteratorFn = [](void *, qsizetype) {}; + iterator.destroyConstIteratorFn = [](const void *){}; + iterator.compareConstIteratorFn = [](const void *, const void *) { + return true; /*all iterators are nullptr*/ }; - iterator._getKey = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData { + iterator.createConstIteratorAtKeyFn = [](const void *, const void *) -> void * { + return reinterpret_cast<void *>(quintptr(42)); + }; + iterator.copyConstIteratorFn = [](void *, const void *) {}; + iterator.diffConstIteratorFn = [](const void *, const void *) -> qsizetype { return 0; }; + iterator.keyAtConstIteratorFn = [](const void *iterator, void *dataPtr) -> void { MyType mytype {1, "key"}; - if (reinterpret_cast<quintptr>(*iterator) == 42) { + if (reinterpret_cast<quintptr>(iterator) == 42) { mytype.number = 42; mytype.text = "find_key"; } - return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData}; + *static_cast<MyType *>(dataPtr) = mytype; }; - iterator._getValue = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData { + iterator.mappedAtConstIteratorFn = [](const void *iterator, void *dataPtr) -> void { MyType mytype {2, "value"}; - if (reinterpret_cast<quintptr>(*iterator) == 42) { + if (reinterpret_cast<quintptr>(iterator) == 42) { mytype.number = 42; mytype.text = "find_value"; } - return {qMetaTypeId<MyType>(), QMetaType::create(qMetaTypeId<MyType>(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData}; + *static_cast<MyType *>(dataPtr) = mytype; }; - QAssociativeIterable iterable {iterator}; + QAssociativeIterable iterable(QMetaAssociation(&iterator), nullptr); auto it = iterable.begin(); QVariant value1 = it.key(); QVERIFY(value1.canConvert<MyType>()); @@ -4603,44 +5550,85 @@ void tst_QVariant::shouldDeleteVariantDataWorksForAssociative() void tst_QVariant::fromStdVariant() { -#if __has_include(<variant>) && __cplusplus >= 201703L +#define CHECK_EQUAL(lhs, rhs, type) do { \ + QCOMPARE(lhs.typeId(), rhs.typeId()); \ + if (lhs.isNull()) { \ + QVERIFY(rhs.isNull()); \ + } else { \ + QVERIFY(!rhs.isNull()); \ + QCOMPARE(get< type >(lhs), get< type >(rhs)); \ + } \ + } while (false) + { typedef std::variant<int, bool> intorbool_t; intorbool_t stdvar = 5; QVariant qvar = QVariant::fromStdVariant(stdvar); QVERIFY(!qvar.isNull()); - QCOMPARE(qvar.type(), QVariant::Int); + QCOMPARE(qvar.typeId(), QMetaType::Int); QCOMPARE(qvar.value<int>(), std::get<int>(stdvar)); + { + const auto qv2 = QVariant::fromStdVariant(std::move(stdvar)); + CHECK_EQUAL(qv2, qvar, int); + } + stdvar = true; qvar = QVariant::fromStdVariant(stdvar); QVERIFY(!qvar.isNull()); - QCOMPARE(qvar.type(), QVariant::Bool); + QCOMPARE(qvar.typeId(), QMetaType::Bool); QCOMPARE(qvar.value<bool>(), std::get<bool>(stdvar)); + { + const auto qv2 = QVariant::fromStdVariant(std::move(stdvar)); + CHECK_EQUAL(qv2, qvar, bool); + } } { std::variant<std::monostate, int> stdvar; QVariant qvar = QVariant::fromStdVariant(stdvar); QVERIFY(!qvar.isValid()); + { + const auto qv2 = QVariant::fromStdVariant(std::move(stdvar)); + CHECK_EQUAL(qv2, qvar, int); // fake type, they're empty + } stdvar = -4; qvar = QVariant::fromStdVariant(stdvar); QVERIFY(!qvar.isNull()); - QCOMPARE(qvar.type(), QVariant::Int); + QCOMPARE(qvar.typeId(), QMetaType::Int); QCOMPARE(qvar.value<int>(), std::get<int>(stdvar)); + { + const auto qv2 = QVariant::fromStdVariant(std::move(stdvar)); + CHECK_EQUAL(qv2, qvar, int); + } } { std::variant<int, bool, QChar> stdvar = QChar::fromLatin1(' '); QVariant qvar = QVariant::fromStdVariant(stdvar); QVERIFY(!qvar.isNull()); - QCOMPARE(qvar.type(), QVariant::Char); + QCOMPARE(qvar.typeId(), QMetaType::QChar); QCOMPARE(qvar.value<QChar>(), std::get<QChar>(stdvar)); + { + const auto qv2 = QVariant::fromStdVariant(std::move(stdvar)); + CHECK_EQUAL(qv2, qvar, QChar); + } } -#endif + // rvalue fromStdVariant() actually moves: + { + const auto foo = u"foo"_s; + std::variant<QString, QByteArray> stdvar = foo; + QVariant qvar = QVariant::fromStdVariant(std::move(stdvar)); + const auto ps = get_if<QString>(&stdvar); + QVERIFY(ps); + QVERIFY(ps->isNull()); // QString was moved from + QVERIFY(!qvar.isNull()); + QCOMPARE(qvar.typeId(), QMetaType::QString); + QCOMPARE(get<QString>(qvar), foo); + } + +#undef CHECK_EQUAL } void tst_QVariant::qt4UuidDataStream() { - qRegisterMetaTypeStreamOperators<QUuid>(); - QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_4_8); @@ -4658,12 +5646,14 @@ void tst_QVariant::qt4UuidDataStream() void tst_QVariant::sequentialIterableEndianessSanityCheck() { - namespace QMTP = QtMetaTypePrivate; - uint oldIteratorCaps = QMTP::ForwardCapability | QMTP::BiDirectionalCapability | QMTP::RandomAccessCapability; - QMTP::QSequentialIterableImpl seqImpl {}; - QCOMPARE(seqImpl.revision(), 1u); - memcpy(&seqImpl._iteratorCapabilities, &oldIteratorCaps, sizeof(oldIteratorCaps)); - QCOMPARE(seqImpl.revision(), 0u); + namespace QMTP = QtMetaContainerPrivate; + QMTP::IteratorCapabilities oldIteratorCaps + = QMTP::InputCapability | QMTP::ForwardCapability + | QMTP::BiDirectionalCapability | QMTP::RandomAccessCapability; + QMTP::QMetaSequenceInterface seqImpl {}; + QCOMPARE(seqImpl.revision, 0u); + memcpy(&seqImpl.iteratorCapabilities, &oldIteratorCaps, sizeof(oldIteratorCaps)); + QCOMPARE(seqImpl.revision, 0u); } void tst_QVariant::sequentialIterableAppend() @@ -4671,24 +5661,38 @@ void tst_QVariant::sequentialIterableAppend() { QList<int> container { 1, 2 }; auto variant = QVariant::fromValue(container); - QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>()); - auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>(); + QVERIFY(variant.canConvert<QSequentialIterable>()); + QSequentialIterable asIterable = variant.view<QSequentialIterable>(); const int i = 3, j = 4; - asIterable.append(&i); - asIterable.append(&j); + void *mutableIterable = asIterable.mutableIterable(); + asIterable.metaContainer().addValueAtEnd(mutableIterable, &i); + asIterable.metaContainer().addValueAtEnd(mutableIterable, &j); QCOMPARE(variant.value<QList<int>>(), QList<int> ({ 1, 2, 3, 4 })); + + asIterable.metaContainer().addValueAtBegin(mutableIterable, &i); + asIterable.metaContainer().addValueAtBegin(mutableIterable, &j); + QCOMPARE(variant.value<QList<int>>(), QList<int> ({ 4, 3, 1, 2, 3, 4 })); + + asIterable.metaContainer().removeValueAtBegin(mutableIterable); + QCOMPARE(variant.value<QList<int>>(), QList<int> ({ 3, 1, 2, 3, 4 })); + asIterable.metaContainer().removeValueAtEnd(mutableIterable); + QCOMPARE(variant.value<QList<int>>(), QList<int> ({ 3, 1, 2, 3 })); } { QSet<QByteArray> container { QByteArray{"hello"}, QByteArray{"world"} }; auto variant = QVariant::fromValue(std::move(container)); - QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>()); - auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>(); + QVERIFY(variant.canConvert<QSequentialIterable>()); + QSequentialIterable asIterable = variant.view<QSequentialIterable>(); QByteArray qba1 {"goodbye"}; QByteArray qba2 { "moon" }; - asIterable.append( &qba1 ); - asIterable.append( &qba2); + void *mutableIterable = asIterable.mutableIterable(); + asIterable.metaContainer().addValue(mutableIterable, &qba1); + asIterable.metaContainer().addValue(mutableIterable, &qba2); QSet<QByteArray> reference { "hello", "world", "goodbye", "moon" }; QCOMPARE(variant.value<QSet<QByteArray>>(), reference); + asIterable.metaContainer().addValue(mutableIterable, &qba1); + asIterable.metaContainer().addValue(mutableIterable, &qba2); + QCOMPARE(variant.value<QSet<QByteArray>>(), reference); } } @@ -4696,15 +5700,15 @@ void tst_QVariant::preferDirectConversionOverInterfaces() { using namespace QtMetaTypePrivate; bool calledCorrectConverter = false; - QMetaType::registerConverter<MyType, QSequentialIterableImpl>([](const MyType &) { - return QSequentialIterableImpl {}; + QMetaType::registerConverter<MyType, QSequentialIterable>([](const MyType &) { + return QSequentialIterable {}; }); QMetaType::registerConverter<MyType, QVariantList>([&calledCorrectConverter](const MyType &) { calledCorrectConverter = true; return QVariantList {}; }); - QMetaType::registerConverter<MyType, QAssociativeIterableImpl>([](const MyType &) { - return QAssociativeIterableImpl {}; + QMetaType::registerConverter<MyType, QAssociativeIterable>([](const MyType &) { + return QAssociativeIterable {}; }); QMetaType::registerConverter<MyType, QVariantHash>([&calledCorrectConverter](const MyType &) { calledCorrectConverter = true; @@ -4716,9 +5720,9 @@ void tst_QVariant::preferDirectConversionOverInterfaces() }); auto holder = QVariant::fromValue(MyType {}); - QVERIFY(holder.canConvert<QSequentialIterableImpl>()); + QVERIFY(holder.canConvert<QSequentialIterable>()); QVERIFY(holder.canConvert<QVariantList>()); - QVERIFY(holder.canConvert<QAssociativeIterableImpl>()); + QVERIFY(holder.canConvert<QAssociativeIterable>()); QVERIFY(holder.canConvert<QVariantHash>()); QVERIFY(holder.canConvert<QVariantMap>()); @@ -4734,5 +5738,596 @@ void tst_QVariant::preferDirectConversionOverInterfaces() QVERIFY(calledCorrectConverter); } +struct MyTypeView +{ + MyType *data; +}; + +void tst_QVariant::mutableView() +{ + bool calledView = false; + const bool success = QMetaType::registerMutableView<MyType, MyTypeView>([&](MyType &data) { + calledView = true; + return MyTypeView { &data }; + }); + QVERIFY(success); + + QTest::ignoreMessage( + QtWarningMsg, + "Mutable view on type already registered from type MyType to type MyTypeView"); + const bool shouldFail = QMetaType::registerMutableView<MyType, MyTypeView>([&](MyType &) { + return MyTypeView { nullptr }; + }); + QVERIFY(!shouldFail); + + auto original = QVariant::fromValue(MyType {}); + + QVERIFY(original.canView<MyTypeView>()); + QVERIFY(!original.canConvert<MyTypeView>()); + + MyTypeView view = original.view<MyTypeView>(); + QVERIFY(calledView); + const char *txt = "lll"; + view.data->number = 113; + view.data->text = txt; + + MyType extracted = original.view<MyType>(); + QCOMPARE(extracted.number, 0); + QCOMPARE(extracted.text, nullptr); +} + +template<typename T> +void tst_QVariant::canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl( + const QByteArray &typeName) +{ + T instance{}; + + // Value -> Pointer + QVariant value = QVariant::fromValue(instance); + QVERIFY2(!value.canView<T *>(), typeName); + QCOMPARE(value.view<T *>(), nullptr); // Expect default constructed pointer + + // Pointer -> Value + QVariant pointer = QVariant::fromValue(&instance); + QVERIFY2(!pointer.canView<T>(), typeName); + QCOMPARE(pointer.view<T>(), T{}); // Expect default constructed. Note: Weak test since instance + // is default constructed, but we detect data corruption +} + +void tst_QVariant::canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue() +{ +#define ADD_TEST_IMPL(typeName, typeNameId, realType) \ + canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl<realType>( \ + #typeName); + + // Add tests for static primitive types + QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ADD_TEST_IMPL) + + // Add tests for static core types + QT_FOR_EACH_STATIC_CORE_CLASS(ADD_TEST_IMPL) +#undef ADD_TEST_IMPL +} + +struct MoveTester +{ + bool wasMoved = false; + MoveTester() = default; + MoveTester(const MoveTester &) {}; // non-trivial on purpose + MoveTester(MoveTester &&other) { other.wasMoved = true; } + MoveTester& operator=(const MoveTester &) = default; + MoveTester& operator=(MoveTester &&other) {other.wasMoved = true; return *this;} +}; + +void tst_QVariant::moveOperations() +{ + { + QString str = "Hello"; + auto v = QVariant::fromValue(str); + QVariant v2(std::move(v)); + QCOMPARE(v2.toString(), str); + + v = QVariant::fromValue(str); + v2 = std::move(v); + QCOMPARE(v2.toString(), str); + } + + std::list<int> list; + auto v = QVariant::fromValue(list); + QVariant v2(std::move(v)); + QVERIFY(v2.value<std::list<int>>() == list); + + v = QVariant::fromValue(list); + v2 = std::move(v); + QVERIFY(v2.value<std::list<int>>() == list); + + { + MoveTester tester; + QVariant::fromValue(tester); + QVERIFY(!tester.wasMoved); + QVariant::fromValue(std::move(tester)); + QVERIFY(tester.wasMoved); + } + { + const MoveTester tester; + QVariant::fromValue(std::move(tester)); + QVERIFY(!tester.wasMoved); // we don't want to move from const variables + } + { + QVariant var(std::in_place_type<MoveTester>); + const auto p = get_if<MoveTester>(&var); + QVERIFY(p); + auto &tester = *p; + QVERIFY(!tester.wasMoved); + [[maybe_unused]] auto copy = var.value<MoveTester>(); + QVERIFY(!tester.wasMoved); + [[maybe_unused]] auto moved = std::move(var).value<MoveTester>(); + QVERIFY(tester.wasMoved); + } +} + +class NoMetaObject : public QObject {}; +void tst_QVariant::equalsWithoutMetaObject() +{ + using T = NoMetaObject*; + QtPrivate::QMetaTypeInterface d = { + /*.revision=*/ 0, + /*.alignment=*/ alignof(T), + /*.size=*/ sizeof(T), + /*.flags=*/ QtPrivate::QMetaTypeTypeFlags<T>::Flags, + /*.typeId=*/ 0, + /*.metaObject=*/ nullptr, // on purpose. + /*.name=*/ "NoMetaObject*", + /*.defaultCtr=*/ [](const QtPrivate::QMetaTypeInterface *, void *addr) { + new (addr) T(); + }, + /*.copyCtr=*/ [](const QtPrivate::QMetaTypeInterface *, void *addr, const void *other) { + new (addr) T(*reinterpret_cast<const T *>(other)); + }, + /*.moveCtr=*/ [](const QtPrivate::QMetaTypeInterface *, void *addr, void *other) { + new (addr) T(std::move(*reinterpret_cast<T *>(other))); + }, + /*.dtor=*/ [](const QtPrivate::QMetaTypeInterface *, void *addr) { + reinterpret_cast<T *>(addr)->~T(); + }, + /*.equals*/ nullptr, + /*.lessThan*/ nullptr, + /*.debugStream=*/ nullptr, + /*.dataStreamOut=*/ nullptr, + /*.dataStreamIn=*/ nullptr, + /*.legacyRegisterOp=*/ nullptr + }; + + QMetaType noMetaObjectMetaType(&d); + QMetaType qobjectMetaType = QMetaType::fromType<tst_QVariant*>(); + + QVERIFY(noMetaObjectMetaType.flags() & QMetaType::PointerToQObject); + QVERIFY(qobjectMetaType.flags() & QMetaType::PointerToQObject); + + QVariant noMetaObjectVariant(noMetaObjectMetaType, nullptr); + QVariant qobjectVariant(qobjectMetaType, nullptr); + + // Shouldn't crash + QVERIFY(noMetaObjectVariant != qobjectVariant); + QVERIFY(qobjectVariant != noMetaObjectVariant); +} + +struct NonDefaultConstructible +{ + NonDefaultConstructible(int i) :i(i) {} + int i; + friend bool operator==(NonDefaultConstructible l, NonDefaultConstructible r) + { return l.i == r.i; } +}; + +template <> char *QTest::toString<NonDefaultConstructible>(const NonDefaultConstructible &ndc) +{ + return qstrdup('{' + QByteArray::number(ndc.i) + '}'); +} + +struct Indestructible +{ + Indestructible() {} + Indestructible(const Indestructible &) {} + Indestructible &operator=(const Indestructible &) { return *this; } +private: + ~Indestructible() {} +}; + +struct NotCopyable +{ + NotCopyable() = default; + NotCopyable(const NotCopyable&) = delete; + NotCopyable &operator=(const NotCopyable &) = delete; +}; + +void tst_QVariant::constructFromIncompatibleMetaType_data() +{ + QTest::addColumn<QMetaType>("type"); + auto addRow = [](QMetaType meta) { + QTest::newRow(meta.name()) << meta; + }; + addRow(QMetaType::fromType<void>()); + addRow(QMetaType::fromType<NonDefaultConstructible>()); + addRow(QMetaType::fromType<QObject>()); + addRow(QMetaType::fromType<Indestructible>()); + addRow(QMetaType::fromType<NotCopyable>()); +} + +void tst_QVariant::constructFromIncompatibleMetaType() +{ + QFETCH(QMetaType, type); + const auto anticipate = [type]() { + // In that case, we run into a different condition (size == 0), and do not warn + if (type == QMetaType::fromType<NonDefaultConstructible>()) { + QTest::ignoreMessage(QtWarningMsg, + "QVariant: Cannot create type 'NonDefaultConstructible' without a " + "default constructor"); + } else if (type != QMetaType::fromType<void>()) { + QTest::ignoreMessage( + QtWarningMsg, + "QVariant: Provided metatype for '" + QByteArray(type.name()) + + "' does not support destruction and copy construction"); + } + }; + anticipate(); + QVariant var(type, nullptr); + QVERIFY(!var.isValid()); + QVERIFY(!var.metaType().isValid()); + + anticipate(); + QVariant regular(1.0); + QVERIFY(!var.canView(type)); + QVERIFY(!var.canConvert(type)); + QVERIFY(!QVariant(regular).convert(type)); +} + +void tst_QVariant::constructFromQtLT65MetaType() +{ + auto qsizeIface = QtPrivate::qMetaTypeInterfaceForType<QSize>(); + + QtPrivate::QMetaTypeInterface qsize64Iface = { + /*revision*/0, + 8, + 8, + QMetaType::NeedsConstruction | QMetaType::NeedsDestruction, + 0, + qsizeIface->metaObjectFn, + "FakeQSize", + qsizeIface->defaultCtr, + qsizeIface->copyCtr, + qsizeIface->moveCtr, + /*dtor =*/ nullptr, + qsizeIface->equals, + qsizeIface->lessThan, + qsizeIface->debugStream, + qsizeIface->dataStreamOut, + qsizeIface->dataStreamIn, + /*legacyregop =*/ nullptr + }; + QVariant var{ QMetaType(&qsize64Iface) }; + QVERIFY(var.isValid()); +} + +void tst_QVariant::copyNonDefaultConstructible() +{ + NonDefaultConstructible ndc(42); + QVariant var = QVariant::fromValue(ndc); + QVERIFY(var.isDetached()); + QCOMPARE(var.metaType(), QMetaType::fromType<NonDefaultConstructible>()); + QVERIFY(var.constData() != &ndc); + + // qvariant_cast<T> and QVariant::value<T> don't compile + QCOMPARE(get<NonDefaultConstructible>(std::as_const(var)), ndc); + + QVariant var2 = var; + var2.detach(); // force another copy + QVERIFY(var2.isDetached()); + QVERIFY(var2.constData() != var.constData()); + QCOMPARE(get<NonDefaultConstructible>(std::as_const(var2)), + get<NonDefaultConstructible>(std::as_const(var))); + QCOMPARE(var2, var); +} + +void tst_QVariant::inplaceConstruct() +{ + { + NonDefaultConstructible ndc(42); + QVariant var(std::in_place_type<NonDefaultConstructible>, 42); + QVERIFY(get_if<NonDefaultConstructible>(&var)); + QCOMPARE(get<NonDefaultConstructible>(var), ndc); + } + + { + std::vector<int> vec {1, 2, 3, 4}; + QVariant var(std::in_place_type<std::vector<int>>, {1, 2, 3, 4}); + QVERIFY(get_if<std::vector<int>>(&var)); + QCOMPARE(get<std::vector<int>>(var), vec); + } +} + +struct LargerThanInternalQVariantStorage { + char data[6 * sizeof(void *)]; +}; + +struct alignas(256) LargerThanInternalQVariantStorageOveraligned { + char data[6 * sizeof(void *)]; +}; + +struct alignas(128) SmallerAlignmentEvenLargerSize { + char data[17 * sizeof(void *)]; +}; + +void tst_QVariant::emplace() +{ + { + // can emplace non default constructible + can emplace on null variant + NonDefaultConstructible ndc(42); + QVariant var; + var.emplace<NonDefaultConstructible>(42); + QVERIFY(get_if<NonDefaultConstructible>(&var)); + QCOMPARE(get<NonDefaultConstructible>(var), ndc); + } + { + // can emplace using ctor taking initializer_list + QVariant var; + var.emplace<std::vector<int>>({0, 1, 2, 3, 4}); + auto vecPtr = get_if<std::vector<int>>(&var); + QVERIFY(vecPtr); + QCOMPARE(vecPtr->size(), 5U); + for (int i = 0; i < 5; ++i) + QCOMPARE(vecPtr->at(size_t(i)), i); + } + // prequisites for the test + QCOMPARE_LE(sizeof(std::vector<int>), sizeof(std::string)); + QCOMPARE(alignof(std::vector<int>), alignof(std::string)); + { + // emplace can reuse storage + auto var = QVariant::fromValue(std::string{}); + QVERIFY(var.data_ptr().is_shared); + auto data = var.constData(); + std::vector<int> &vec = var.emplace<std::vector<int>>(3, 42); + /* alignment is the same, so the pointer is exactly the same; + no offset change */ + auto expected = std::vector<int>{42, 42, 42}; + QCOMPARE(get_if<std::vector<int>>(&var), &vec); + QCOMPARE(get<std::vector<int>>(var), expected); + QCOMPARE(var.constData(), data); + } + { + // emplace can't reuse storage if the variant is shared + auto var = QVariant::fromValue(std::string{}); + [[maybe_unused]] QVariant causesSharing = var; + QVERIFY(var.data_ptr().is_shared); + auto data = var.constData(); + var.emplace<std::vector<int>>(3, 42); + auto expected = std::vector<int>{42, 42, 42}; + QVERIFY(get_if<std::vector<int>>(&var)); + QCOMPARE(get<std::vector<int>>(var), expected); + QCOMPARE_NE(var.constData(), data); + } + { + // emplace puts element into the correct place - non-shared + QVERIFY(QVariant::Private::canUseInternalSpace(QMetaType::fromType<QString>().iface())); + QVariant var; + var.emplace<QString>(QChar('x')); + QVERIFY(!var.data_ptr().is_shared); + } + { + // emplace puts element into the correct place - shared + QVERIFY(!QVariant::Private::canUseInternalSpace(QMetaType::fromType<std::string>().iface())); + QVariant var; + var.emplace<std::string>(42, 'x'); + QVERIFY(var.data_ptr().is_shared); + } + { + // emplace does not reuse the storage if alignment is too large + auto iface = QMetaType::fromType<LargerThanInternalQVariantStorage>().iface(); + QVERIFY(!QVariant::Private::canUseInternalSpace(iface)); + auto var = QVariant::fromValue(LargerThanInternalQVariantStorage{}); + auto data = var.constData(); + var.emplace<LargerThanInternalQVariantStorageOveraligned>(); + QCOMPARE_NE(var.constData(), data); + } + { + // emplace does reuse the storage if new alignment and size are together small enough + auto iface = QMetaType::fromType<LargerThanInternalQVariantStorageOveraligned>().iface(); + QVERIFY(!QVariant::Private::canUseInternalSpace(iface)); + auto var = QVariant::fromValue(LargerThanInternalQVariantStorageOveraligned{}); + auto data = var.constData(); + var.emplace<SmallerAlignmentEvenLargerSize>(); + // no exact match below - the alignment is after all different + QCOMPARE_LE(quintptr(var.constData()), quintptr(data)); + QCOMPARE_LE(quintptr(var.constData()), + quintptr(data) + sizeof(LargerThanInternalQVariantStorageOveraligned)); + } +} + +void tst_QVariant::getIf_NonDefaultConstructible() +{ + getIf_impl(NonDefaultConstructible{42}); +} + +void tst_QVariant::getIfSpecial() +{ + QVariant v{QString{}}; // used to be a null QVariant in Qt 5 + QCOMPARE_NE(get_if<QString>(&v), nullptr); // not anymore... +} + +void tst_QVariant::get_NonDefaultConstructible() +{ + get_impl(NonDefaultConstructible{42}); +} + +template <typename T> +T mutate(const T &t) { return t + t; } +template <> +NonDefaultConstructible mutate(const NonDefaultConstructible &t) +{ + return NonDefaultConstructible{t.i + t.i}; +} + +template <typename T> +QVariant make_null_QVariant_of_type() +{ + return QVariant(QMetaType::fromType<T>()); +} + +template <typename T> +void tst_QVariant::getIf_impl(T t) const +{ + QVariant v = QVariant::fromValue(t); + + QVariant null; + QVERIFY(null.isNull()); + + [[maybe_unused]] + QVariant nulT; + if constexpr (std::is_default_constructible_v<T>) { + // typed null QVariants don't work with non-default-constuctable types + nulT = make_null_QVariant_of_type<T>(); + QVERIFY(nulT.isNull()); + } + + QVariant date = QDate(2023, 3, 3); + static_assert(!std::is_same_v<T, QDate>); + + // for behavioral comparison: + StdVariant stdn = {}, stdv = t; + + // returns nullptr on type mismatch: + { + // const + QCOMPARE_EQ(get_if<T>(&std::as_const(stdn)), nullptr); + QCOMPARE_EQ(get_if<T>(&std::as_const(date)), nullptr); + // mutable + QCOMPARE_EQ(get_if<T>(&stdn), nullptr); + QCOMPARE_EQ(get_if<T>(&date), nullptr); + } + + // returns nullptr on null variant (QVariant only): + { + QCOMPARE_EQ(get_if<T>(&std::as_const(null)), nullptr); + QCOMPARE_EQ(get_if<T>(&null), nullptr); + if constexpr (std::is_default_constructible_v<T>) { + // const access return nullptr + QCOMPARE_EQ(get_if<T>(&std::as_const(nulT)), nullptr); + // but mutable access makes typed null QVariants non-null (like data()) + QCOMPARE_NE(get_if<T>(&nulT), nullptr); + QVERIFY(!nulT.isNull()); + nulT = make_null_QVariant_of_type<T>(); // reset to null state + } + } + + // const access: + { + auto ps = get_if<T>(&std::as_const(stdv)); + static_assert(std::is_same_v<decltype(ps), const T*>); + QCOMPARE_NE(ps, nullptr); + QCOMPARE_EQ(*ps, t); + + auto pv = get_if<T>(&std::as_const(v)); + static_assert(std::is_same_v<decltype(ps), const T*>); + QCOMPARE_NE(pv, nullptr); + QCOMPARE_EQ(*pv, t); + } + + // mutable access: + { + T t2 = mutate(t); + + auto ps = get_if<T>(&stdv); + static_assert(std::is_same_v<decltype(ps), T*>); + QCOMPARE_NE(ps, nullptr); + QCOMPARE_EQ(*ps, t); + *ps = t2; + auto ps2 = get_if<T>(&stdv); + QCOMPARE_NE(ps2, nullptr); + QCOMPARE_EQ(*ps2, t2); + + auto pv = get_if<T>(&v); + static_assert(std::is_same_v<decltype(pv), T*>); + QCOMPARE_NE(pv, nullptr); + QCOMPARE_EQ(*pv, t); + *pv = t2; + auto pv2 = get_if<T>(&v); + QCOMPARE_NE(pv2, nullptr); + QCOMPARE_EQ(*pv2, t2); + + // typed null QVariants become non-null (data() behavior): + if constexpr (std::is_default_constructible_v<T>) { + QVERIFY(nulT.isNull()); + auto pn = get_if<T>(&nulT); + QVERIFY(!nulT.isNull()); + static_assert(std::is_same_v<decltype(pn), T*>); + QCOMPARE_NE(pn, nullptr); + QCOMPARE_EQ(*pn, T{}); + *pn = t2; + auto pn2 = get_if<T>(&nulT); + QCOMPARE_NE(pn2, nullptr); + QCOMPARE_EQ(*pn2, t2); + } + } +} + +template <typename T> +void tst_QVariant::get_impl(T t) const +{ + QVariant v = QVariant::fromValue(t); + + // for behavioral comparison: + StdVariant stdv = t; + + #define FOR_EACH_CVREF(op) \ + op(/*unadorned*/, &&) \ + op(&, &) \ + op(&&, &&) \ + op(const, const &&) \ + op(const &, const &) \ + op(const &&, const &&) \ + /* end */ + + + #define CHECK_RETURN_TYPE_OF(Variant, cvref_in, cvref_out) \ + static_assert(std::is_same_v< \ + decltype(get<T>(std::declval<Variant cvref_in >())), \ + T cvref_out \ + >); \ + /* end */ + #define CHECK_RETURN_TYPE(cvref_in, cvref_out) \ + CHECK_RETURN_TYPE_OF(StdVariant, cvref_in, cvref_out) \ + CHECK_RETURN_TYPE_OF(QVariant, cvref_in, cvref_out) \ + /* end */ + FOR_EACH_CVREF(CHECK_RETURN_TYPE) + #undef CHECK_RETURN_TYPE + + #undef FOR_EACH_CVREF + + // const access: + { + auto &&rs = get<T>(std::as_const(stdv)); + QCOMPARE_EQ(rs, t); + + auto &&rv = get<T>(std::as_const(v)); + QCOMPARE_EQ(rv, t); + } + + // mutable access: + { + T t2 = mutate(t); + + auto &&rs = get<T>(stdv); + QCOMPARE_EQ(rs, t); + rs = t2; + auto &&rs2 = get<T>(stdv); + QCOMPARE_EQ(rs2, t2); + + auto &&rv = get<T>(v); + QCOMPARE_EQ(rv, t); + rv = t2; + auto &&rv2 = get<T>(v); + QCOMPARE_EQ(rv2, t2); + } +} + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc" |