diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp | 326 |
1 files changed, 281 insertions, 45 deletions
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp index 856b56941a..68bcb53056 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_qmetatype.h" -#include "tst_qvariant_common.h" +#include "tst_qmetatype_libs.h" #include <QtCore/private/qmetaobjectbuilder_p.h> @@ -55,6 +30,30 @@ struct ConvertFunctor } }; +template<typename T> +struct OptionalWrapper +{ + std::optional<T> operator()(const T& t) const + { + if (!CustomConvertibleType::s_ok) + return std::nullopt; + + return t; + } +}; + +template<typename From> +struct ConvertFunctorWithOptional +{ + std::optional<CustomConvertibleType> operator()(const From& f) const + { + if (!CustomConvertibleType::s_ok) + return std::nullopt; + + return CustomConvertibleType(QVariant::fromValue(f)); + } +}; + template<typename From, typename To> bool hasRegisteredConverterFunction() { @@ -106,6 +105,7 @@ void customTypeNotYetConvertible() testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>(); testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>(); testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, std::optional<CustomConvertibleType>>(); } void registerCustomTypeConversions() @@ -124,20 +124,22 @@ void registerCustomTypeConversions() QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>))); QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>))); QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QChar>(&CustomConvertibleType::convert<QChar>))); - QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctor<QString>()))); + QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctorWithOptional<QString>()))); QVERIFY((QMetaType::registerConverter<bool, CustomConvertibleType>(ConvertFunctor<bool>()))); - QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctor<int>()))); + QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctorWithOptional<int>()))); QVERIFY((QMetaType::registerConverter<double, CustomConvertibleType>(ConvertFunctor<double>()))); - QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctor<float>()))); + QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctorWithOptional<float>()))); QVERIFY((QMetaType::registerConverter<QRect, CustomConvertibleType>(ConvertFunctor<QRect>()))); - QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctor<QRectF>()))); + QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctorWithOptional<QRectF>()))); QVERIFY((QMetaType::registerConverter<QPoint, CustomConvertibleType>(ConvertFunctor<QPoint>()))); - QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctor<QPointF>()))); + QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctorWithOptional<QPointF>()))); QVERIFY((QMetaType::registerConverter<QSize, CustomConvertibleType>(ConvertFunctor<QSize>()))); - QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctor<QSizeF>()))); + QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctorWithOptional<QSizeF>()))); QVERIFY((QMetaType::registerConverter<QLine, CustomConvertibleType>(ConvertFunctor<QLine>()))); - QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctor<QLineF>()))); + QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctorWithOptional<QLineF>()))); QVERIFY((QMetaType::registerConverter<QChar, CustomConvertibleType>(ConvertFunctor<QChar>()))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, std::optional<CustomConvertibleType>>(OptionalWrapper<CustomConvertibleType>()))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2"); QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); @@ -164,6 +166,7 @@ void tst_QMetaType::convertCustomType_data() QTest::addColumn<QLineF>("testQLineF"); QTest::addColumn<QChar>("testQChar"); QTest::addColumn<CustomConvertibleType>("testCustom"); + QTest::addColumn<DerivedGadgetType>("testDerived"); QTest::newRow("default") << true << QString::fromLatin1("string") << true << 15 @@ -171,14 +174,16 @@ void tst_QMetaType::convertCustomType_data() << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) << QLine(3, 9, 29, 4) << QLineF(38.9, 28.9, 102.3, 0.0) - << QChar('Q') << CustomConvertibleType(QString::fromLatin1("test")); + << QChar('Q') << CustomConvertibleType(QString::fromLatin1("test")) + << DerivedGadgetType(QString::fromLatin1("test")); QTest::newRow("not ok") << false << QString::fromLatin1("string") << true << 15 << double(3.14) << float(3.6) << QRect(1, 2, 3, 4) << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) << QLine(3, 9, 29, 4) << QLineF() - << QChar('Q') << CustomConvertibleType(42); + << QChar('Q') << CustomConvertibleType(42) + << DerivedGadgetType(42); } void tst_QMetaType::convertCustomType() @@ -193,7 +198,7 @@ void tst_QMetaType::convertCustomType() QCOMPARE(v.toString(), ok ? testQString : QString()); QCOMPARE(v.value<QString>(), ok ? testQString : QString()); QVERIFY(CustomConvertibleType::s_value.canConvert<CustomConvertibleType>()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toString()), testQString); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toString()), ok ? testQString : QString()); QFETCH(bool, testBool); CustomConvertibleType::s_value = testBool; @@ -205,7 +210,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testInt; QCOMPARE(v.toInt(), ok ? testInt : 0); QCOMPARE(v.value<int>(), ok ? testInt : 0); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toInt()), testInt); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toInt()), ok ? testInt : 0); QFETCH(double, testDouble); CustomConvertibleType::s_value = testDouble; @@ -217,7 +222,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testFloat; QCOMPARE(v.toFloat(), ok ? testFloat : 0.0); QCOMPARE(v.value<float>(), ok ? testFloat : 0.0); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toFloat()), testFloat); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toFloat()), ok ? testFloat : 0); QFETCH(QRect, testQRect); CustomConvertibleType::s_value = testQRect; @@ -229,7 +234,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQRectF; QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF()); QCOMPARE(v.value<QRectF>(), ok ? testQRectF : QRectF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRectF()), testQRectF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRectF()), ok ? testQRectF : QRectF()); QFETCH(QPoint, testQPoint); CustomConvertibleType::s_value = testQPoint; @@ -241,7 +246,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQPointF; QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF()); QCOMPARE(v.value<QPointF>(), ok ? testQPointF : QPointF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPointF()), testQPointF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPointF()), ok ? testQPointF : QPointF()); QFETCH(QSize, testQSize); CustomConvertibleType::s_value = testQSize; @@ -253,7 +258,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQSizeF; QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF()); QCOMPARE(v.value<QSizeF>(), ok ? testQSizeF : QSizeF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSizeF()), testQSizeF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSizeF()), ok ? testQSizeF : QSizeF()); QFETCH(QLine, testQLine); CustomConvertibleType::s_value = testQLine; @@ -265,7 +270,7 @@ void tst_QMetaType::convertCustomType() CustomConvertibleType::s_value = testQLineF; QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF()); QCOMPARE(v.value<QLineF>(), ok ? testQLineF : QLineF()); - QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLineF()), testQLineF); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLineF()), ok ? testQLineF : QLineF()); QFETCH(QChar, testQChar); CustomConvertibleType::s_value = testQChar; @@ -274,8 +279,29 @@ void tst_QMetaType::convertCustomType() QFETCH(CustomConvertibleType, testCustom); v = QVariant::fromValue(testCustom); - QVERIFY(v.canConvert(::qMetaTypeId<CustomConvertibleType2>())); + QVERIFY(v.canConvert(QMetaType(::qMetaTypeId<CustomConvertibleType2>()))); QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo); + + // Check that converters that actually convert to std::optional<T> are not + // taken to indicate success or failure of the conversion. In these cases, + // the conversion must always succeed, even if the converter has returned a + // nullopt. + v = QVariant::fromValue(testCustom); + QVERIFY(v.canConvert(QMetaType::fromType<std::optional<CustomConvertibleType>>())); + QVERIFY(v.convert(QMetaType::fromType<std::optional<CustomConvertibleType>>())); + QCOMPARE(v.value<std::optional<CustomConvertibleType>>().has_value(), ok); + if (ok) { + QCOMPARE(v.value<std::optional<CustomConvertibleType>>().value().m_foo, testCustom.m_foo); + } + + QFETCH(DerivedGadgetType, testDerived); + v = QVariant::fromValue(testDerived); + QCOMPARE(v.metaType(), QMetaType::fromType<DerivedGadgetType>()); + QCOMPARE(v.value<DerivedGadgetType>().m_foo, testDerived.m_foo); + QVERIFY(v.canConvert(QMetaType::fromType<BaseGadgetType>())); + QVERIFY(v.convert(QMetaType::fromType<BaseGadgetType>())); + QCOMPARE(v.metaType(), QMetaType::fromType<BaseGadgetType>()); + QCOMPARE(v.value<BaseGadgetType>().m_foo, testDerived.m_foo); } void tst_QMetaType::convertConstNonConst() @@ -385,6 +411,11 @@ struct CharTemplate { int a; } x; + + union + { + int a; + } y; }; void tst_QMetaType::operatorEq_data() @@ -420,6 +451,124 @@ void tst_QMetaType::operatorEq() QCOMPARE(typeB == typeA, eq); QCOMPARE(typeA != typeB, !eq); QCOMPARE(typeB != typeA, !eq); + +#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) + // for built-in types or locally-defined types, this must also hold true + if (eq) + QCOMPARE(typeA.iface(), typeB.iface()); +#endif +} + +void tst_QMetaType::operatorEq2_data() +{ + create_data(); +} + +void tst_QMetaType::operatorEq2() +{ + QFETCH(int, type); + QMetaType fromType1, fromType2; + QMetaType fromId1(type), fromId2(type); + + switch (type) { + case QMetaType::UnknownType: + break; +#define GET_METATYPE_FROM_TYPE(MetaTypeName, MetaTypeId, RealType) \ + case QMetaType::MetaTypeName: \ + fromType1 = QMetaType::fromType<RealType>(); \ + fromType2 = QMetaType::fromType<RealType>(); \ + break; +FOR_EACH_CORE_METATYPE(GET_METATYPE_FROM_TYPE) +#undef GET_METATYPE_FROM_TYPE + } + + // sanity check + QCOMPARE(fromId1.id(), type); + QCOMPARE(fromId2.id(), type); + + // confirm that they're all equal + QCOMPARE(fromId1, fromId2); + QCOMPARE(fromType1, fromType2); + QCOMPARE(fromType1, fromId1); + QCOMPARE(fromType2, fromId2); + +#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) + // for built-in types (other than void), this must be true + if (type != QMetaType::Void) { + QCOMPARE(fromType1.iface(), fromId1.iface()); + QCOMPARE(fromType2.iface(), fromId1.iface()); + } +#endif +} + +#define DECLARE_LIB_FUNCTION(TYPE, ID) \ + Q_DECL_IMPORT QMetaType metatype_ ## TYPE(); +namespace Lib1 { FOR_EACH_METATYPE_LIBS(DECLARE_LIB_FUNCTION) } +namespace Lib2 { FOR_EACH_METATYPE_LIBS(DECLARE_LIB_FUNCTION) } +#undef DECLARE_LIB_FUNCTION + +using LibMetatypeFunction = QMetaType (*)(); +void tst_QMetaType::operatorEqAcrossLibs_data() +{ + QTest::addColumn<int>("builtinTypeId"); + QTest::addColumn<QMetaType>("localType"); + QTest::addColumn<LibMetatypeFunction>("lib1Function"); + QTest::addColumn<LibMetatypeFunction>("lib2Function"); + +#define ADD_ROW(TYPE, ID) \ + QTest::addRow(QT_STRINGIFY(TYPE)) << int(ID) \ + << QMetaType::fromType<TYPE>() \ + << &Lib1::metatype_ ## TYPE \ + << &Lib2::metatype_ ## TYPE; +FOR_EACH_METATYPE_LIBS(ADD_ROW) +#undef ADD_ROW +} + +void tst_QMetaType::operatorEqAcrossLibs() +{ + QFETCH(int, builtinTypeId); + QFETCH(QMetaType, localType); + QFETCH(LibMetatypeFunction, lib1Function); + QFETCH(LibMetatypeFunction, lib2Function); + + QMetaType lib1Type = lib1Function(); + QMetaType lib2Type = lib2Function(); + + const QtPrivate::QMetaTypeInterface *localIface = localType.iface(); + const QtPrivate::QMetaTypeInterface *lib1Iface = lib1Type.iface(); + const QtPrivate::QMetaTypeInterface *lib2Iface = lib2Type.iface(); + + // DO THIS FIRST: + // if this isn't a built-in type, then the QMetaTypeInterface::typeId is + // initially set to 0 + QCOMPARE(lib1Type, lib2Type); + + int actualTypeId = localType.id(); + bool builtinTypeExpected = builtinTypeId != QMetaType::UnknownType; + bool builtinTypeActually = actualTypeId < QMetaType::User; + + qDebug() << "QMetaType for type" << QByteArray(localType.name()) + << "(type ID" << (actualTypeId >= 0x1000 ? Qt::hex : Qt::dec) << actualTypeId << ')' + << (builtinTypeActually ? "IS" : "is NOT") << "a built-in type;" + << "local interface:" << static_cast<const void *>(localIface) + << "lib1 interface:" << static_cast<const void *>(lib1Iface) + << "lib2 interface:" << static_cast<const void *>(lib2Iface); + + QCOMPARE(builtinTypeActually, builtinTypeExpected); + QCOMPARE(lib1Type.id(), actualTypeId); + QCOMPARE(lib2Type.id(), actualTypeId); + QCOMPARE(QByteArray(lib1Type.name()), QByteArray(localType.name())); + QCOMPARE(QByteArray(lib2Type.name()), QByteArray(localType.name())); + QCOMPARE(lib1Type, localType); + QCOMPARE(lib2Type, localType); + +#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) + if (actualTypeId < QMetaType::FirstGuiType && actualTypeId != QMetaType::Void) { + // for built-in QtCore types, we expect the interfaces to be the same too + QCOMPARE(lib1Iface, localIface); + QCOMPARE(lib2Iface, localIface); + } +#endif } class WithPrivateDTor { @@ -479,7 +628,94 @@ void tst_QMetaType::typeNameNormalization() // The string based normalization doesn't handle aliases, QMetaType::fromType() does // CHECK_TYPE_NORMALIZATION("qulonglong", quint64); QCOMPARE(QMetaType::fromType<quint64>().name(), "qulonglong"); + + // noramlizedType and metatype name agree + { + auto type = QMetaType::fromType<decltype(CharTemplate<'<'>::x)>(); + QCOMPARE(type.name(), QMetaObject::normalizedType(type.name())); + } + { + auto type = QMetaType::fromType<decltype(CharTemplate<'<'>::y)>(); + QCOMPARE(type.name(), QMetaObject::normalizedType(type.name())); + } +} + +QT_BEGIN_NAMESPACE +namespace QtPrivate { struct tst_QMetaType_TestType {}; } +QT_END_NAMESPACE + +void tst_QMetaType::typeNameInQtPrivate() +{ + using T = QT_PREPEND_NAMESPACE(QtPrivate::tst_QMetaType_TestType); + + // some compilers (GCC) are known to suppress the namespace prefix if the + // type and the function where it is expanded on are on the same namespace + static constexpr char expectedName[] = "QtPrivate::tst_QMetaType_TestType"; + QMetaType mt = QMetaType::fromType<T>(); + QCOMPARE(mt.name(), expectedName); +} + +#if QT_DEPRECATED_SINCE(6, 0) +void tst_QMetaType::testDeprecatedGetters() +{ + QFETCH(int, aType); + QFETCH(QByteArray, aTypeName); + + if (aType >= QMetaType::FirstWidgetsType) + QSKIP("The test doesn't link against QtWidgets."); + + // QMetaType::type("name") -> QMetaType::fromName("name").id() + QT_IGNORE_DEPRECATIONS(QCOMPARE(QMetaType::type(aTypeName), + QMetaType::fromName(aTypeName).id());) + // QMetaType::typeName(int) -> QMetaType(int).name() + QT_IGNORE_DEPRECATIONS(QCOMPARE(QLatin1String(QMetaType::typeName(aType)), + QLatin1String(QMetaType(aType).name()));) + // QMetaType::typeFlags(int) -> QMetaType(int).flags() + QT_IGNORE_DEPRECATIONS(QCOMPARE(QMetaType::typeFlags(aType), + QMetaType(aType).flags());) + // QMetaType::metaObjectForType(int) -> QMetaType(int).metaObject() + QT_IGNORE_DEPRECATIONS(QCOMPARE(QMetaType::metaObjectForType(aType), + QMetaType(aType).metaObject());) +} + +void tst_QMetaType::testDeprecatedLoadSave() +{ + QFETCH(int, type); + QFETCH(bool, isStreamable); + + if (!isStreamable) + return; + + QMetaType metaType(type); + void *value = metaType.create(); + auto cleanup = qScopeGuard([&metaType, value]() { + metaType.destroy(value); + }); + + QByteArray ba; + QDataStream stream(&ba, QIODevice::ReadWrite); + + // Write using deprecated API + QT_IGNORE_DEPRECATIONS(QVERIFY(QMetaType::save(stream, type, value));) + QCOMPARE(stream.status(), QDataStream::Ok); + + // Read using non-deprecated API + stream.device()->seek(0); + QVERIFY(metaType.load(stream, value)); + QCOMPARE(stream.status(), QDataStream::Ok); + + // Write using non-deprecated API + stream.device()->seek(0); + ba.clear(); + QVERIFY(metaType.save(stream, value)); + QCOMPARE(stream.status(), QDataStream::Ok); + + // Read using deprecated API + stream.device()->seek(0); + QT_IGNORE_DEPRECATIONS(QVERIFY(QMetaType::load(stream, type, value));) + QCOMPARE(stream.status(), QDataStream::Ok); } +#endif // QT_DEPRECATED_SINCE(6, 0) // Compile-time test, it should be possible to register function pointer types class Undefined; |