summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp')
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp1576
1 files changed, 1339 insertions, 237 deletions
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index 5f2ce68918..23d41cafb2 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -1,48 +1,96 @@
// 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 WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qvariant.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>
-#include <QQueue>
-#include <QStack>
-#include <QSet>
-#include <qvariant.h>
-#include <qbitarray.h>
-#include <qbytearraylist.h>
-#include <qdatetime.h>
-#include <qmap.h>
-#include <QHash>
-#include <qiodevice.h>
-#include <qurl.h>
-#include <qlocale.h>
-#include <qdebug.h>
-#include <qjsondocument.h>
-#include <quuid.h>
-
-#include <limits.h>
-#include <float.h>
-#include <cmath>
-#include <variant>
-#include <QRegularExpression>
-#include <QDir>
+// 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 <QEasingCurve>
-#include <QSequentialIterable>
-#include <QAssociativeIterable>
+#include <QLocale>
+#include <QQueue>
+#include <QRegularExpression>
#include <QScopeGuard>
-#include "qnumeric.h"
+#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>
+#include <variant>
#include <unordered_map>
+using namespace Qt::StringLiterals;
+
class CustomNonQObject;
+struct NonDefaultConstructible;
template<typename T, typename = void>
struct QVariantFromValueCompiles
@@ -59,15 +107,43 @@ struct QVariantFromValueCompiles<T, std::void_t<decltype (QVariant::fromValue(st
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 = nullptr)
: QObject(parent), customNonQObjectPointer(0)
{
-
}
@@ -99,6 +175,12 @@ 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();
@@ -209,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;
@@ -222,6 +307,7 @@ private slots:
void loadBrokenUserType();
void invalidDate() const;
+ void compareCustomTypes_data() const;
void compareCustomTypes() const;
void timeToDateTime() const;
void copyingUserTypes() const;
@@ -268,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();
@@ -287,14 +380,41 @@ private slots:
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);
@@ -325,11 +445,21 @@ void tst_QVariant::constructor()
QVERIFY(var3.isNull());
QVERIFY(var3.isValid());
+ 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.typeId(), QMetaType::QString);
QCOMPARE(var5.typeName(), "QString");
@@ -372,6 +502,14 @@ void tst_QVariant::constructor_invalid()
}
{
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));
+ }
+ {
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
QVariant variant(QMetaType(typeId), /* copy */ nullptr);
QVERIFY(!variant.isValid());
QVERIFY(variant.isNull());
@@ -526,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;
@@ -562,19 +697,124 @@ 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
@@ -584,7 +824,6 @@ void tst_QVariant::convert()
QCOMPARE(var.toInt(), 0);
}
-
void tst_QVariant::toInt_data()
{
QTest::addColumn<QVariant>("value");
@@ -1234,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 ) );
}
@@ -1520,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"));
@@ -1560,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;
@@ -1567,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;
@@ -1577,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;
@@ -1743,7 +2010,7 @@ 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)
@@ -1820,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 );
@@ -1914,6 +2187,8 @@ void tst_QVariant::userType()
QVariant userVar;
userVar.setValue(data);
+ 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");
@@ -2037,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);
@@ -2700,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();
+ };
+
+ 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{});
+
+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;
};
- MyClass myClass;
+ 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]);
- QVariant v = QVariant::fromValue<void *>(&myClass);
- QVariant v2 = QVariant::fromValue<void *>(&myClass);
+ Qt::partial_ordering expectedOrdering = Qt::partial_ordering::equivalent;
+ QCOMPARE(QVariant::compare(nonObjV1, nonObjV1), expectedOrdering);
+ QT_TEST_EQUALITY_OPS(nonObjV1, nonObjV1, is_eq(expectedOrdering));
- QCOMPARE(v, v2);
+ 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 {};
@@ -2910,35 +3464,49 @@ 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
{
@@ -3108,57 +3676,57 @@ 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());
}
}
@@ -3859,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;
}
@@ -3932,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) {
+ 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)
- continue;
- if (id != QMetaType::Void) {
+ 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>();
@@ -4297,7 +4867,7 @@ void sortIterable(QSequentialIterable *iterable)
}
template<typename Container>
-void testSequentialIteration()
+static void testSequentialIteration()
{
int numSeen = 0;
Container sequence;
@@ -4336,7 +4906,7 @@ void testSequentialIteration()
int numSeen = 0;
auto varList = listVariant.value<QVariantList>();
auto varIter = varList.begin();
- for (const QVariant &v : qAsConst(listIter)) {
+ for (const QVariant &v : std::as_const(listIter)) {
QVERIFY(ContainerAPI<Container>::compare(v, *varIter));
++varIter;
++numSeen;
@@ -4416,7 +4986,7 @@ void testSequentialIteration()
}
template<typename Container>
-void testAssociativeIteration()
+static void testAssociativeIteration()
{
using Key = typename Container::key_type;
using Mapped = typename Container::mapped_type;
@@ -4487,35 +5057,53 @@ void testAssociativeIteration()
QCOMPARE(f, iter.constEnd());
}
-void tst_QVariant::iterateContainerElements()
-{
- testSequentialIteration<QQueue<int>>();
- testSequentialIteration<QQueue<QVariant>>();
- testSequentialIteration<QQueue<QString>>();
- testSequentialIteration<QList<int>>();
- testSequentialIteration<QList<QVariant>>();
- testSequentialIteration<QList<QString>>();
- testSequentialIteration<QList<QByteArray>>();
- testSequentialIteration<QStack<int>>();
- testSequentialIteration<QStack<QVariant>>();
- testSequentialIteration<QStack<QString>>();
- testSequentialIteration<std::vector<int>>();
- testSequentialIteration<std::vector<QVariant>>();
- testSequentialIteration<std::vector<QString>>();
- testSequentialIteration<std::list<int>>();
- testSequentialIteration<std::list<QVariant>>();
- testSequentialIteration<std::list<QString>>();
- testSequentialIteration<QStringList>();
- testSequentialIteration<QByteArrayList>();
- testSequentialIteration<QString>();
- testSequentialIteration<QByteArray>();
+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
- testSequentialIteration<std::forward_list<int>>();
- testSequentialIteration<std::forward_list<QVariant>>();
- testSequentialIteration<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;
@@ -4538,12 +5126,6 @@ void tst_QVariant::iterateContainerElements()
QCOMPARE(ints, intsCopy);
}
- testAssociativeIteration<QHash<int, bool>>();
- testAssociativeIteration<QHash<int, int>>();
- testAssociativeIteration<QMap<int, bool>>();
- testAssociativeIteration<std::map<int, bool>>();
- testAssociativeIteration<std::unordered_map<int, bool>>();
-
{
QMap<int, QString> mapping;
mapping.insert(1, "one");
@@ -4586,56 +5168,61 @@ void tst_QVariant::iterateContainerElements()
}
}
-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>());
@@ -4646,7 +5233,6 @@ 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));
@@ -4659,8 +5245,15 @@ template<typename Enum> void testVariant(Enum value, bool *ok)
QVERIFY(var2.convert(QMetaType::fromType<int>()));
QCOMPARE(var2.value<int>(), static_cast<int>(value));
+ 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 ((+static_cast<qint64>(value) <= INT_MAX) && (+static_cast<qint64>(value) >= INT_MIN)) {
+ if (losslessConvertToInt) {
int intValue = static_cast<int>(value);
QVariant intVar = intValue;
QVERIFY(intVar.canConvert<Enum>());
@@ -4670,72 +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>());
// 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);
@@ -4748,13 +5395,15 @@ 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
- testVariantMeta(Qt::RichText, &ok, "RichText");
- testVariantMeta(Qt::Alignment(Qt::AlignBottom), &ok, "AlignBottom");
- testVariantMeta(Qt::Alignment(Qt::AlignHCenter | Qt::AlignBottom), &ok,
- "AlignHCenter|AlignBottom");
+ 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()
@@ -4901,6 +5550,16 @@ void tst_QVariant::shouldDeleteVariantDataWorksForAssociative()
void tst_QVariant::fromStdVariant()
{
+#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;
@@ -4908,21 +5567,38 @@ void tst_QVariant::fromStdVariant()
QVERIFY(!qvar.isNull());
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.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.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(' ');
@@ -4930,7 +5606,25 @@ void tst_QVariant::fromStdVariant()
QVERIFY(!qvar.isNull());
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);
+ }
}
+ // 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()
@@ -5082,6 +5776,48 @@ void tst_QVariant::mutableView()
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()
{
{
@@ -5103,6 +5839,30 @@ void tst_QVariant::moveOperations()
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 {};
@@ -5173,6 +5933,13 @@ private:
~Indestructible() {}
};
+struct NotCopyable
+{
+ NotCopyable() = default;
+ NotCopyable(const NotCopyable&) = delete;
+ NotCopyable &operator=(const NotCopyable &) = delete;
+};
+
void tst_QVariant::constructFromIncompatibleMetaType_data()
{
QTest::addColumn<QMetaType>("type");
@@ -5183,49 +5950,384 @@ void tst_QVariant::constructFromIncompatibleMetaType_data()
addRow(QMetaType::fromType<NonDefaultConstructible>());
addRow(QMetaType::fromType<QObject>());
addRow(QMetaType::fromType<Indestructible>());
+ addRow(QMetaType::fromType<NotCopyable>());
}
void tst_QVariant::constructFromIncompatibleMetaType()
{
QFETCH(QMetaType, 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");
- }
+ 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(QMetaType::fromType<NonDefaultConstructible>(), &ndc);
+ 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(*static_cast<const NonDefaultConstructible *>(var.constData()), ndc);
+ 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"