Restrict QVariant::isNull() behavior

isNull() would forward to the contained type and check that type's
isNull() method for some of the builtin types. Remove that behavior
and only return true in isNull(), if the variant is invalid, doesn't
contain data or contains a null pointer.

In addition, implement more consistent behavior when constructing
a QVariant using the internal API taking a copy from a void *.
isNull() should return true in both cases. This mainly changes behavior
for some corner cases and when using our internal API.

[ChangeLog][Important Behavior Changes] QVariant::isNull()
no longer returns true when the variant contains an object of some
type with an isNull() method, that returns true for the object;
QVariant::isNull() now only returns true when the variant contains
no object or a null pointer.

Change-Id: I3125041c4f8f8618a04aa375aa0a56b19c02dcf5
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Lars Knoll 2020-06-10 11:26:31 +02:00
parent 4a69cd7f72
commit 048debe8f9
9 changed files with 70 additions and 235 deletions

View File

@ -117,12 +117,6 @@ struct CoreTypesFilter {
namespace { // annonymous used to hide QVariant handlers namespace { // annonymous used to hide QVariant handlers
static bool isNull(const QVariant::Private *d)
{
QVariantIsNull<CoreTypesFilter> isNull(d);
return QMetaTypeSwitcher::switcher<bool>(isNull, d->type().id());
}
/*! /*!
\internal \internal
*/ */
@ -1394,7 +1388,6 @@ static void streamDebug(QDebug dbg, const QVariant &v)
#endif #endif
const QVariant::Handler qt_kernel_variant_handler = { const QVariant::Handler qt_kernel_variant_handler = {
isNull,
convert, convert,
#if !defined(QT_NO_DEBUG_STREAM) #if !defined(QT_NO_DEBUG_STREAM)
streamDebug streamDebug
@ -1403,13 +1396,11 @@ const QVariant::Handler qt_kernel_variant_handler = {
#endif #endif
}; };
static bool dummyIsNull(const QVariant::Private *d) { Q_ASSERT_X(false, "QVariant::isNull", "Trying to call isNull on an unknown type"); return d->is_null; }
static bool dummyConvert(const QVariant::Private *, int, void *, bool *) { Q_ASSERT_X(false, "QVariant", "Trying to convert an unknown type"); return false; } static bool dummyConvert(const QVariant::Private *, int, void *, bool *) { Q_ASSERT_X(false, "QVariant", "Trying to convert an unknown type"); return false; }
#if !defined(QT_NO_DEBUG_STREAM) #if !defined(QT_NO_DEBUG_STREAM)
static void dummyStreamDebug(QDebug, const QVariant &) { Q_ASSERT_X(false, "QVariant", "Trying to convert an unknown type"); } static void dummyStreamDebug(QDebug, const QVariant &) { Q_ASSERT_X(false, "QVariant", "Trying to convert an unknown type"); }
#endif #endif
const QVariant::Handler qt_dummy_variant_handler = { const QVariant::Handler qt_dummy_variant_handler = {
dummyIsNull,
dummyConvert, dummyConvert,
#if !defined(QT_NO_DEBUG_STREAM) #if !defined(QT_NO_DEBUG_STREAM)
dummyStreamDebug dummyStreamDebug
@ -1445,7 +1436,9 @@ static void customConstruct(QVariant::Private *d, const void *copy)
d->is_shared = true; d->is_shared = true;
d->data.shared = new (data) QVariant::PrivateShared(ptr); d->data.shared = new (data) QVariant::PrivateShared(ptr);
} }
d->is_null = !copy; // need to check for nullptr_t here, as this can get called by fromValue(nullptr). fromValue() uses
// std::addressof(value) which in this case returns the address of the nullptr object.
d->is_null = !copy || type == QMetaType::fromType<std::nullptr_t>();
} }
static void customClear(QVariant::Private *d) static void customClear(QVariant::Private *d)
@ -1459,17 +1452,6 @@ static void customClear(QVariant::Private *d)
} }
} }
static bool customIsNull(const QVariant::Private *d)
{
if (d->is_null)
return true;
if (d->type().flags() & QMetaType::IsPointer) {
const void *d_ptr = d->is_shared ? d->data.shared->ptr : &(d->data);
return *static_cast<void *const *>(d_ptr) == nullptr;
}
return false;
}
static bool customConvert(const QVariant::Private *d, int t, void *result, bool *ok) static bool customConvert(const QVariant::Private *d, int t, void *result, bool *ok)
{ {
if (d->type().id() >= QMetaType::User || t >= QMetaType::User) { if (d->type().id() >= QMetaType::User || t >= QMetaType::User) {
@ -1496,7 +1478,6 @@ static void customStreamDebug(QDebug dbg, const QVariant &variant) {
#endif #endif
const QVariant::Handler qt_custom_variant_handler = { const QVariant::Handler qt_custom_variant_handler = {
customIsNull,
customConvert, customConvert,
#if !defined(QT_NO_DEBUG_STREAM) #if !defined(QT_NO_DEBUG_STREAM)
customStreamDebug customStreamDebug
@ -2061,7 +2042,6 @@ QVariant::QVariant(int typeId, const void *copy, uint flags)
} else { } else {
create(typeId, copy); create(typeId, copy);
} }
d.is_null = false;
} }
/*! /*!
@ -2071,7 +2051,6 @@ QVariant::QVariant(int typeId, const void *copy, uint flags)
QVariant::QVariant(QMetaType type, const void *copy) : d(type) QVariant::QVariant(QMetaType type, const void *copy) : d(type)
{ {
customConstruct(&d, copy); customConstruct(&d, copy);
d.is_null = false;
} }
QVariant::QVariant(int val) QVariant::QVariant(int val)
@ -3972,25 +3951,32 @@ const void *QVariant::constData() const
void* QVariant::data() void* QVariant::data()
{ {
detach(); detach();
// set is_null to false, as the caller is likely to write some data into this variant
d.is_null = false;
return const_cast<void *>(constData()); return const_cast<void *>(constData());
} }
/*! /*!
Returns \c true if this is a null variant, false otherwise. A variant is Returns \c true if this is a null variant, false otherwise.
considered null if it contains no initialized value, or the contained value
is \nullptr or is an instance of a built-in type that has an isNull
method, in which case the result would be the same as calling isNull on the
wrapped object.
\warning Null variants is not a single state and two null variants may easily A variant is considered null if it contains no initialized value or a null pointer.
return \c false on the == operator if they do not contain similar null values.
\note This behavior has been changed from Qt 5, where isNull() would also
return true if the variant contained an object of a builtin type with an isNull()
method that returned true for that object.
\sa convert(int) \sa convert(int)
*/ */
bool QVariant::isNull() const bool QVariant::isNull() const
{ {
return handlerManager[d.type().id()]->isNull(&d); if (d.is_null || !metaType().isValid())
return true;
if (metaType().flags() & QMetaType::IsPointer) {
const void *d_ptr = d.is_shared ? d.data.shared->ptr : &(d.data);
return *static_cast<void *const *>(d_ptr) == nullptr;
}
return false;
} }
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM

View File

@ -469,11 +469,9 @@ class Q_CORE_EXPORT QVariant
} }
}; };
public: public:
typedef bool (*f_null)(const Private *);
typedef bool (*f_convert)(const QVariant::Private *d, int t, void *, bool *); typedef bool (*f_convert)(const QVariant::Private *d, int t, void *, bool *);
typedef void (*f_debugStream)(QDebug, const QVariant &); typedef void (*f_debugStream)(QDebug, const QVariant &);
struct Handler { struct Handler {
f_null isNull;
f_convert convert; f_convert convert;
f_debugStream debugStream; f_debugStream debugStream;
}; };

View File

@ -182,127 +182,8 @@ inline void v_clear(QVariant::Private *d, T* = nullptr)
} }
template <typename T>
struct PrimitiveIsNull
{
public:
static bool isNull(const QVariant::Private *d)
{
return d->is_null;
}
};
template <typename T>
struct PrimitiveIsNull<T*>
{
public:
static bool isNull(const QVariant::Private *d)
{
return d->is_null || d->data.ptr == nullptr;
}
};
template <>
struct PrimitiveIsNull<std::nullptr_t>
{
public:
static bool isNull(const QVariant::Private *)
{
return true;
}
};
Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler();
template<class Filter>
class QVariantIsNull
{
/// \internal
/// This class checks if a type T has method called isNull. Result is kept in the Value property
/// TODO Can we somehow generalize it? A macro version?
template<typename T>
class HasIsNullMethod {
struct Yes { char unused[1]; };
struct No { char unused[2]; };
static_assert(sizeof(Yes) != sizeof(No));
template<class C> static decltype(static_cast<const C*>(nullptr)->isNull(), Yes()) test(int);
template<class C> static No test(...);
public:
static const bool Value = (sizeof(test<T>(0)) == sizeof(Yes));
};
// TODO This part should go to autotests during HasIsNullMethod generalization.
static_assert(!HasIsNullMethod<bool>::Value);
struct SelfTest1 { bool isNull() const; };
static_assert(HasIsNullMethod<SelfTest1>::Value);
struct SelfTest2 {};
static_assert(!HasIsNullMethod<SelfTest2>::Value);
struct SelfTest3 : public SelfTest1 {};
static_assert(HasIsNullMethod<SelfTest3>::Value);
struct SelfTestFinal1 final { bool isNull() const; };
static_assert(HasIsNullMethod<SelfTestFinal1>::Value);
struct SelfTestFinal2 final {};
static_assert(!HasIsNullMethod<SelfTestFinal2>::Value);
struct SelfTestFinal3 final : public SelfTest1 {};
static_assert(HasIsNullMethod<SelfTestFinal3>::Value);
template<typename T, bool HasIsNull = HasIsNullMethod<T>::Value>
struct CallFilteredIsNull
{
static bool isNull(const QVariant::Private *d)
{
return v_cast<T>(d)->isNull();
}
};
template<typename T>
struct CallFilteredIsNull<T, /* HasIsNull = */ false>
{
static bool isNull(const QVariant::Private *d)
{
return PrimitiveIsNull<T>::isNull(d);
}
};
template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
struct CallIsNull
{
static bool isNull(const QVariant::Private *d)
{
return CallFilteredIsNull<T>::isNull(d);
}
};
template<typename T>
struct CallIsNull<T, /* IsAcceptedType = */ false>
{
static bool isNull(const QVariant::Private *d)
{
return CallFilteredIsNull<T, false>::isNull(d);
}
};
public:
QVariantIsNull(const QVariant::Private *d)
: m_d(d)
{}
template<typename T>
bool delegate(const T*)
{
return CallIsNull<T>::isNull(m_d);
}
// we need that as sizof(void) is undefined and it is needed in HasIsNullMethod
bool delegate(const void *) { Q_ASSERT(false); return m_d->is_null; }
bool delegate(const QMetaTypeSwitcher::UnknownType *) { return m_d->is_null; }
bool delegate(const QMetaTypeSwitcher::NotBuiltinType *)
{
// QVariantIsNull is used only for built-in types
Q_ASSERT(false);
return m_d->is_null;
}
protected:
const QVariant::Private *m_d;
};
namespace QVariantPrivate { namespace QVariantPrivate {
Q_CORE_EXPORT void registerHandler(const int /* Modules::Names */ name, const QVariant::Handler *handler); Q_CORE_EXPORT void registerHandler(const int /* Modules::Names */ name, const QVariant::Handler *handler);
} }

View File

@ -101,26 +101,6 @@ struct GuiTypesFilter {
}; };
}; };
// This class is a hack that customizes access to QPolygon and QPolygonF
template<class Filter>
class QGuiVariantIsNull : public QVariantIsNull<Filter> {
typedef QVariantIsNull<Filter> Base;
public:
QGuiVariantIsNull(const QVariant::Private *d)
: QVariantIsNull<Filter>(d)
{}
template<typename T>
bool delegate(const T *p) { return Base::delegate(p); }
bool delegate(const QPolygon*) { return v_cast<QPolygon>(Base::m_d)->isEmpty(); }
bool delegate(const QPolygonF*) { return v_cast<QPolygonF>(Base::m_d)->isEmpty(); }
bool delegate(const void *p) { return Base::delegate(p); }
};
static bool isNull(const QVariant::Private *d)
{
QGuiVariantIsNull<GuiTypesFilter> isNull(d);
return QMetaTypeSwitcher::switcher<bool>(isNull, d->type().id(), nullptr);
}
static bool convert(const QVariant::Private *d, int t, static bool convert(const QVariant::Private *d, int t,
void *result, bool *ok) void *result, bool *ok)
{ {
@ -263,7 +243,6 @@ static void streamDebug(QDebug dbg, const QVariant &v)
#endif #endif
const QVariant::Handler qt_gui_variant_handler = { const QVariant::Handler qt_gui_variant_handler = {
isNull,
convert, convert,
#if !defined(QT_NO_DEBUG_STREAM) #if !defined(QT_NO_DEBUG_STREAM)
streamDebug streamDebug

View File

@ -49,11 +49,6 @@ QT_BEGIN_NAMESPACE
namespace { namespace {
static bool isNull(const QVariant::Private *)
{
return false;
}
static bool convert(const QVariant::Private *d, int type, void *result, bool *ok) static bool convert(const QVariant::Private *d, int type, void *result, bool *ok)
{ {
Q_UNUSED(d); Q_UNUSED(d);
@ -79,7 +74,6 @@ static void streamDebug(QDebug dbg, const QVariant &v)
#endif #endif
static const QVariant::Handler widgets_handler = { static const QVariant::Handler widgets_handler = {
isNull,
convert, convert,
#if !defined(QT_NO_DEBUG_STREAM) #if !defined(QT_NO_DEBUG_STREAM)
streamDebug streamDebug

View File

@ -442,7 +442,8 @@ void tst_QTransposeProxyModel::insertRowBase()
const int oldColCount = proxy.columnCount(proxy.mapFromSource(parent)); const int oldColCount = proxy.columnCount(proxy.mapFromSource(parent));
QVERIFY(model->insertRow(1, parent)); QVERIFY(model->insertRow(1, parent));
QCOMPARE(proxy.columnCount(proxy.mapFromSource(parent)), oldColCount + 1); QCOMPARE(proxy.columnCount(proxy.mapFromSource(parent)), oldColCount + 1);
QVERIFY(proxy.index(0, 1, proxy.mapFromSource(parent)).data().isNull()); QVariant result = proxy.index(0, 1, proxy.mapFromSource(parent)).data();
QVERIFY(result.isNull() || (result.metaType().id() == QMetaType::QString && result.toString().isNull()));
QCOMPARE(columnsInsertSpy.count(), 1); QCOMPARE(columnsInsertSpy.count(), 1);
QCOMPARE(columnsAboutToBeInsertSpy.count(), 1); QCOMPARE(columnsAboutToBeInsertSpy.count(), 1);
for (const auto &spyArgs : {columnsInsertSpy.takeFirst(), for (const auto &spyArgs : {columnsInsertSpy.takeFirst(),
@ -540,8 +541,10 @@ void tst_QTransposeProxyModel::insertColumnProxy()
QVERIFY(proxy.insertColumn(1, proxyParent)); QVERIFY(proxy.insertColumn(1, proxyParent));
QCOMPARE(proxy.columnCount(proxyParent), oldColCount + 1); QCOMPARE(proxy.columnCount(proxyParent), oldColCount + 1);
QCOMPARE(model->rowCount(sourceParent), oldRowCount + 1); QCOMPARE(model->rowCount(sourceParent), oldRowCount + 1);
QVERIFY(proxy.index(0, 1, proxyParent).data().isNull()); QVariant result = proxy.index(0, 1, proxyParent).data();
QVERIFY(model->index(1, 0, sourceParent).data().isNull()); QVERIFY(result.isNull() || (result.metaType().id() == QMetaType::QString && result.toString().isNull()));
result = model->index(1, 0, sourceParent).data();
QVERIFY(result.isNull() || (result.metaType().id() == QMetaType::QString && result.toString().isNull()));
QCOMPARE(columnsInsertSpy.count(), 1); QCOMPARE(columnsInsertSpy.count(), 1);
QCOMPARE(columnsAboutToBeInsertSpy.count(), 1); QCOMPARE(columnsAboutToBeInsertSpy.count(), 1);
QCOMPARE(rowsInsertSpy.count(), 1); QCOMPARE(rowsInsertSpy.count(), 1);

View File

@ -350,12 +350,15 @@ void tst_QVariant::constructor_invalid()
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type")); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
QVariant variant(static_cast<QVariant::Type>(typeId)); QVariant variant(static_cast<QVariant::Type>(typeId));
QVERIFY(!variant.isValid()); QVERIFY(!variant.isValid());
QVERIFY(variant.isNull());
QCOMPARE(variant.type(), int(QMetaType::UnknownType));
QCOMPARE(variant.userType(), int(QMetaType::UnknownType)); QCOMPARE(variant.userType(), int(QMetaType::UnknownType));
} }
{ {
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type")); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
QVariant variant(typeId, /* copy */ 0); QVariant variant(typeId, /* copy */ 0);
QVERIFY(!variant.isValid()); QVERIFY(!variant.isValid());
QVERIFY(variant.isNull());
QCOMPARE(variant.userType(), int(QMetaType::UnknownType)); QCOMPARE(variant.userType(), int(QMetaType::UnknownType));
} }
} }
@ -377,16 +380,19 @@ void tst_QVariant::isNull()
QString str1; QString str1;
QVariant var1( str1 ); QVariant var1( str1 );
QVERIFY( var1.isNull() ); QVERIFY( !var1.isNull() );
QVariant var3( QString( "blah" ) ); QVariant var3( QString( "blah" ) );
QVERIFY( !var3.isNull() ); QVERIFY( !var3.isNull() );
var3 = QVariant(QVariant::String);
QVERIFY( var3.isNull() );
QVariant var4( 0 ); QVariant var4( 0 );
QVERIFY( !var4.isNull() ); QVERIFY( !var4.isNull() );
QVariant var5 = QString(); QVariant var5 = QString();
QVERIFY( var5.isNull() ); QVERIFY( !var5.isNull() );
QVariant var6( QString( "blah" ) ); QVariant var6( QString( "blah" ) );
QVERIFY( !var6.isNull() ); QVERIFY( !var6.isNull() );
@ -402,9 +408,9 @@ void tst_QVariant::isNull()
var8 = QVariant::fromValue<std::nullptr_t>(nullptr); var8 = QVariant::fromValue<std::nullptr_t>(nullptr);
QVERIFY(var8.isNull()); QVERIFY(var8.isNull());
QVariant var9 = QVariant(QJsonValue(QJsonValue::Null)); QVariant var9 = QVariant(QJsonValue(QJsonValue::Null));
QVERIFY(var9.isNull()); QVERIFY(!var9.isNull());
var9 = QVariant::fromValue<QJsonValue>(QJsonValue(QJsonValue::Null)); var9 = QVariant::fromValue<QJsonValue>(QJsonValue(QJsonValue::Null));
QVERIFY(var9.isNull()); QVERIFY(!var9.isNull());
QVariant var10(QMetaType::VoidStar, nullptr); QVariant var10(QMetaType::VoidStar, nullptr);
QVERIFY(var10.isNull()); QVERIFY(var10.isNull());
@ -419,7 +425,7 @@ void tst_QVariant::isNull()
QVERIFY(QVariant::fromValue<int*>(nullptr).isNull()); QVERIFY(QVariant::fromValue<int*>(nullptr).isNull());
QVariant var12(QVariant::fromValue<QString>(QString())); QVariant var12(QVariant::fromValue<QString>(QString()));
QVERIFY(var12.isNull()); QVERIFY(!var12.isNull());
} }
void tst_QVariant::swap() void tst_QVariant::swap()
@ -1077,8 +1083,6 @@ void tst_QVariant::toByteArray()
QCOMPARE( ba, result ); QCOMPARE( ba, result );
QVERIFY( value.convert( QVariant::ByteArray ) ); QVERIFY( value.convert( QVariant::ByteArray ) );
QCOMPARE( value.isNull(), result.isNull() );
QCOMPARE( value.toByteArray().isNull(), result.isNull() );
QCOMPARE( value.toByteArray(), result ); QCOMPARE( value.toByteArray(), result );
} }
@ -1121,8 +1125,6 @@ void tst_QVariant::toString()
QCOMPARE( str, result ); QCOMPARE( str, result );
QVERIFY( value.convert( QVariant::String ) ); QVERIFY( value.convert( QVariant::String ) );
QCOMPARE( value.isNull(), result.isNull() );
QCOMPARE( value.toString().isNull(), result.isNull() );
QCOMPARE( value.toString(), result ); QCOMPARE( value.toString(), result );
} }
@ -1246,13 +1248,15 @@ void tst_QVariant::writeToReadFromDataStream_data()
} }
QTest::newRow( "invalid" ) << QVariant() << true; QTest::newRow( "invalid" ) << QVariant() << true;
QTest::newRow( "bitarray_invalid" ) << QVariant( QBitArray() ) << true; QTest::newRow( "bitarray_invalid" ) << QVariant(QVariant::BitArray) << true;
QTest::newRow( "bitarray_empty" ) << QVariant( QBitArray() ) << false;
QBitArray bitarray( 3 ); QBitArray bitarray( 3 );
bitarray[0] = 0; bitarray[0] = 0;
bitarray[1] = 1; bitarray[1] = 1;
bitarray[2] = 0; bitarray[2] = 0;
QTest::newRow( "bitarray_valid" ) << QVariant( bitarray ) << false; QTest::newRow( "bitarray_valid" ) << QVariant( bitarray ) << false;
QTest::newRow( "bytearray_invalid" ) << QVariant( QByteArray() ) << true; QTest::newRow( "bytearray_invalid" ) << QVariant(QVariant::ByteArray) << true;
QTest::newRow( "bytearray_empty" ) << QVariant( QByteArray() ) << false;
QTest::newRow( "int_invalid") << QVariant(QVariant::Int) << true; QTest::newRow( "int_invalid") << QVariant(QVariant::Int) << true;
QByteArray bytearray(5, ' '); QByteArray bytearray(5, ' ');
bytearray[0] = 'T'; bytearray[0] = 'T';
@ -1261,9 +1265,11 @@ void tst_QVariant::writeToReadFromDataStream_data()
bytearray[3] = 't'; bytearray[3] = 't';
bytearray[4] = '\0'; bytearray[4] = '\0';
QTest::newRow( "bytearray_valid" ) << QVariant( bytearray ) << false; QTest::newRow( "bytearray_valid" ) << QVariant( bytearray ) << false;
QTest::newRow( "date_invalid" ) << QVariant( QDate() ) << true; QTest::newRow( "date_invalid" ) << QVariant(QVariant::Date) << true;
QTest::newRow( "date_empty" ) << QVariant( QDate() ) << false;
QTest::newRow( "date_valid" ) << QVariant( QDate( 2002, 07, 06 ) ) << false; QTest::newRow( "date_valid" ) << QVariant( QDate( 2002, 07, 06 ) ) << false;
QTest::newRow( "datetime_invalid" ) << QVariant( QDateTime() ) << true; QTest::newRow( "datetime_invalid" ) << QVariant(QVariant::DateTime) << 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( "datetime_valid" ) << QVariant( QDateTime( QDate( 2002, 07, 06 ), QTime( 14, 0, 0 ) ) ) << false;
QTest::newRow( "double_valid" ) << QVariant( 123.456 ) << false; QTest::newRow( "double_valid" ) << QVariant( 123.456 ) << false;
QTest::newRow( "float_valid" ) << QVariant( 123.456f ) << false; QTest::newRow( "float_valid" ) << QVariant( 123.456f ) << false;
@ -1274,45 +1280,50 @@ void tst_QVariant::writeToReadFromDataStream_data()
vMap.insert( "double", QVariant( 3.45 ) ); vMap.insert( "double", QVariant( 3.45 ) );
vMap.insert( "float", QVariant( 3.45f ) ); vMap.insert( "float", QVariant( 3.45f ) );
QTest::newRow( "map_valid" ) << QVariant( vMap ) << false; QTest::newRow( "map_valid" ) << QVariant( vMap ) << false;
QTest::newRow( "point_invalid" ) << QVariant::fromValue( QPoint() ) << true; QTest::newRow( "point_invalid" ) << QVariant(QVariant::Point) << true;
QTest::newRow( "point_empty" ) << QVariant::fromValue( QPoint() ) << false;
QTest::newRow( "point_valid" ) << QVariant::fromValue( QPoint( 10, 10 ) ) << false; QTest::newRow( "point_valid" ) << QVariant::fromValue( QPoint( 10, 10 ) ) << false;
QTest::newRow( "rect_invalid" ) << QVariant( QRect() ) << true; QTest::newRow( "rect_invalid" ) << QVariant(QVariant::Rect) << true;
QTest::newRow( "rect_empty" ) << QVariant( QRect() ) << false;
QTest::newRow( "rect_valid" ) << QVariant( QRect( 10, 10, 20, 20 ) ) << false; QTest::newRow( "rect_valid" ) << QVariant( QRect( 10, 10, 20, 20 ) ) << false;
QTest::newRow( "size_invalid" ) << QVariant( QSize( 0, 0 ) ) << true; QTest::newRow( "size_invalid" ) << QVariant(QVariant::Size) << true;
QTest::newRow( "size_empty" ) << QVariant( QSize( 0, 0 ) ) << false;
QTest::newRow( "size_valid" ) << QVariant( QSize( 10, 10 ) ) << false; QTest::newRow( "size_valid" ) << QVariant( QSize( 10, 10 ) ) << false;
QTest::newRow( "string_invalid" ) << QVariant( QString() ) << true; QTest::newRow( "string_invalid" ) << QVariant(QVariant::String) << true;
QTest::newRow( "string_empty" ) << QVariant( QString() ) << false;
QTest::newRow( "string_valid" ) << QVariant( QString( "Test" ) ) << false; QTest::newRow( "string_valid" ) << QVariant( QString( "Test" ) ) << false;
QStringList stringlist; QStringList stringlist;
stringlist << "One" << "Two" << "Three"; stringlist << "One" << "Two" << "Three";
QTest::newRow( "stringlist_valid" ) << QVariant( stringlist ) << false; QTest::newRow( "stringlist_valid" ) << QVariant( stringlist ) << false;
QTest::newRow( "time_invalid" ) << QVariant( QTime() ) << true; QTest::newRow( "time_invalid" ) << QVariant(QVariant::Time) << true;
QTest::newRow( "time_empty" ) << QVariant( QTime() ) << false;
QTest::newRow( "time_valid" ) << QVariant( QTime( 14, 0, 0 ) ) << false; QTest::newRow( "time_valid" ) << QVariant( QTime( 14, 0, 0 ) ) << false;
QTest::newRow( "uint_valid" ) << QVariant( (uint)123 ) << false; QTest::newRow( "uint_valid" ) << QVariant( (uint)123 ) << false;
QTest::newRow( "qchar" ) << QVariant(QChar('a')) << false; QTest::newRow( "qchar" ) << QVariant(QChar('a')) << false;
QTest::newRow( "qchar_null" ) << QVariant(QChar(0)) << true; QTest::newRow( "qchar_null" ) << QVariant(QChar(0)) << false;
QTest::newRow( "regularexpression" ) << QVariant(QRegularExpression("abc.*def")) << false; QTest::newRow( "regularexpression" ) << QVariant(QRegularExpression("abc.*def")) << false;
QTest::newRow( "regularexpression_empty" ) << QVariant(QRegularExpression()) << false; QTest::newRow( "regularexpression_empty" ) << QVariant(QRegularExpression()) << false;
// types known to QMetaType, but not part of QVariant::Type // types known to QMetaType, but not part of QVariant::Type
QTest::newRow("QMetaType::Long invalid") << QVariant(QMetaType::Long, (void *) 0) << false; QTest::newRow("QMetaType::Long invalid") << QVariant(QMetaType::Long, (void *) 0) << true;
long longInt = -1l; long longInt = -1l;
QTest::newRow("QMetaType::Long") << QVariant(QMetaType::Long, &longInt) << false; QTest::newRow("QMetaType::Long") << QVariant(QMetaType::Long, &longInt) << false;
QTest::newRow("QMetaType::Short invalid") << QVariant(QMetaType::Short, (void *) 0) << false; QTest::newRow("QMetaType::Short invalid") << QVariant(QMetaType::Short, (void *) 0) << true;
short shortInt = 1; short shortInt = 1;
QTest::newRow("QMetaType::Short") << QVariant(QMetaType::Short, &shortInt) << false; QTest::newRow("QMetaType::Short") << QVariant(QMetaType::Short, &shortInt) << false;
QTest::newRow("QMetaType::Char invalid") << QVariant(QMetaType::Char, (void *) 0) << false; QTest::newRow("QMetaType::Char invalid") << QVariant(QMetaType::Char, (void *) 0) << true;
char ch = 'c'; char ch = 'c';
QTest::newRow("QMetaType::Char") << QVariant(QMetaType::Char, &ch) << false; QTest::newRow("QMetaType::Char") << QVariant(QMetaType::Char, &ch) << false;
QTest::newRow("QMetaType::ULong invalid") << QVariant(QMetaType::ULong, (void *) 0) << false; QTest::newRow("QMetaType::ULong invalid") << QVariant(QMetaType::ULong, (void *) 0) << true;
ulong ulongInt = 1ul; ulong ulongInt = 1ul;
QTest::newRow("QMetaType::ULong") << QVariant(QMetaType::ULong, &ulongInt) << false; QTest::newRow("QMetaType::ULong") << QVariant(QMetaType::ULong, &ulongInt) << false;
QTest::newRow("QMetaType::UShort invalid") << QVariant(QMetaType::UShort, (void *) 0) << false; QTest::newRow("QMetaType::UShort invalid") << QVariant(QMetaType::UShort, (void *) 0) << true;
ushort ushortInt = 1u; ushort ushortInt = 1u;
QTest::newRow("QMetaType::UShort") << QVariant(QMetaType::UShort, &ushortInt) << false; QTest::newRow("QMetaType::UShort") << QVariant(QMetaType::UShort, &ushortInt) << false;
QTest::newRow("QMetaType::UChar invalid") << QVariant(QMetaType::UChar, (void *) 0) << false; QTest::newRow("QMetaType::UChar invalid") << QVariant(QMetaType::UChar, (void *) 0) << true;
uchar uch = 0xf0; uchar uch = 0xf0;
QTest::newRow("QMetaType::UChar") << QVariant(QMetaType::UChar, &uch) << false; QTest::newRow("QMetaType::UChar") << QVariant(QMetaType::UChar, &uch) << false;
QTest::newRow("QMetaType::Float invalid") << QVariant(QMetaType::Float, (void *) 0) << false; QTest::newRow("QMetaType::Float invalid") << QVariant(QMetaType::Float, (void *) 0) << true;
float f = 1.234f; float f = 1.234f;
QTest::newRow("QMetaType::Float") << QVariant(QMetaType::Float, &f) << false; QTest::newRow("QMetaType::Float") << QVariant(QMetaType::Float, &f) << false;
CustomStreamableClass custom = {123}; CustomStreamableClass custom = {123};
@ -2318,7 +2329,7 @@ void tst_QVariant::qvariant_cast_QObject()
QFETCH(bool, isNull); QFETCH(bool, isNull);
QObject *o = qvariant_cast<QObject *>(data); QObject *o = qvariant_cast<QObject *>(data);
QCOMPARE(o != 0, success && !isNull); QCOMPARE(o != nullptr, success && !isNull);
if (success) { if (success) {
if (!isNull) if (!isNull)
QCOMPARE(o->objectName(), QString::fromLatin1("Hello")); QCOMPARE(o->objectName(), QString::fromLatin1("Hello"));
@ -2326,14 +2337,12 @@ void tst_QVariant::qvariant_cast_QObject()
QVERIFY(data.canConvert(QMetaType::QObjectStar)); QVERIFY(data.canConvert(QMetaType::QObjectStar));
QVERIFY(data.canConvert(::qMetaTypeId<QObject*>())); QVERIFY(data.canConvert(::qMetaTypeId<QObject*>()));
QCOMPARE(data.value<QObject*>() == 0, isNull); QCOMPARE(data.value<QObject*>() == 0, isNull);
QCOMPARE(data.isNull(), isNull);
QVERIFY(data.convert(QMetaType::QObjectStar)); QVERIFY(data.convert(QMetaType::QObjectStar));
QCOMPARE(data.userType(), int(QMetaType::QObjectStar)); QCOMPARE(data.userType(), int(QMetaType::QObjectStar));
} else { } else {
QVERIFY(!data.canConvert<QObject*>()); QVERIFY(!data.canConvert<QObject*>());
QVERIFY(!data.canConvert(QMetaType::QObjectStar)); QVERIFY(!data.canConvert(QMetaType::QObjectStar));
QVERIFY(!data.canConvert(::qMetaTypeId<QObject*>())); QVERIFY(!data.canConvert(::qMetaTypeId<QObject*>()));
QCOMPARE(data.isNull(), isNull);
QVERIFY(!data.value<QObject*>()); QVERIFY(!data.value<QObject*>());
QVERIFY(!data.convert(QMetaType::QObjectStar)); QVERIFY(!data.convert(QMetaType::QObjectStar));
QVERIFY(data.userType() != QMetaType::QObjectStar); QVERIFY(data.userType() != QMetaType::QObjectStar);
@ -3358,7 +3367,7 @@ void tst_QVariant::moreCustomTypes()
{ {
QString str; QString str;
PLAY_WITH_VARIANT(str, true, QString(), 0, false); PLAY_WITH_VARIANT(str, false, QString(), 0, false);
str = QString::fromLatin1("123456789.123"); str = QString::fromLatin1("123456789.123");
PLAY_WITH_VARIANT(str, false, str, 123456789.123, true); PLAY_WITH_VARIANT(str, false, str, 123456789.123, true);
} }
@ -4465,8 +4474,6 @@ void tst_QVariant::metaEnums()
void tst_QVariant::nullConvert() void tst_QVariant::nullConvert()
{ {
// Test quirks with QVariants different types of null states.
// null variant with no initialized value // null variant with no initialized value
QVariant nullVar(QVariant::String); QVariant nullVar(QVariant::String);
QVERIFY(nullVar.isValid()); QVERIFY(nullVar.isValid());
@ -4475,19 +4482,6 @@ void tst_QVariant::nullConvert()
QVERIFY(!nullVar.convert(QVariant::Url)); QVERIFY(!nullVar.convert(QVariant::Url));
QCOMPARE(nullVar.type(), QVariant::Url); QCOMPARE(nullVar.type(), QVariant::Url);
QVERIFY(nullVar.isNull()); QVERIFY(nullVar.isNull());
// variant initialized with null value
QVariant nullStr = QVariant::fromValue(QString());
QVERIFY(nullStr.isValid());
QVERIFY(nullStr.isNull());
// We can convert an initialized null value however
QVERIFY(nullStr.convert(QVariant::Url));
QCOMPARE(nullStr.type(), QVariant::Url);
QVERIFY(nullStr.isValid());
// QUrl does not have an isNull method
QVERIFY(!nullStr.isNull());
// The URL is not valid however
QVERIFY(!nullStr.toUrl().isValid());
} }
void tst_QVariant::accessSequentialContainerKey() void tst_QVariant::accessSequentialContainerKey()

View File

@ -484,7 +484,7 @@ void tst_QGuiVariant::writeToReadFromDataStream_data()
QTest::addColumn<QVariant>("writeVariant"); QTest::addColumn<QVariant>("writeVariant");
QTest::addColumn<bool>("isNull"); QTest::addColumn<bool>("isNull");
QTest::newRow( "bitmap_invalid" ) << QVariant::fromValue( QBitmap() ) << true; QTest::newRow( "bitmap_invalid" ) << QVariant::fromValue( QBitmap() ) << false;
QBitmap bitmap( 10, 10 ); QBitmap bitmap( 10, 10 );
bitmap.fill( Qt::red ); bitmap.fill( Qt::red );
QTest::newRow( "bitmap_valid" ) << QVariant::fromValue( bitmap ) << false; QTest::newRow( "bitmap_valid" ) << QVariant::fromValue( bitmap ) << false;
@ -494,19 +494,19 @@ void tst_QGuiVariant::writeToReadFromDataStream_data()
QTest::newRow( "cursor_valid" ) << QVariant::fromValue( QCursor( Qt::PointingHandCursor ) ) << false; QTest::newRow( "cursor_valid" ) << QVariant::fromValue( QCursor( Qt::PointingHandCursor ) ) << false;
#endif #endif
QTest::newRow( "font_valid" ) << QVariant::fromValue( QFont( "times", 12 ) ) << false; QTest::newRow( "font_valid" ) << QVariant::fromValue( QFont( "times", 12 ) ) << false;
QTest::newRow( "pixmap_invalid" ) << QVariant::fromValue( QPixmap() ) << true; QTest::newRow( "pixmap_invalid" ) << QVariant::fromValue( QPixmap() ) << false;
QPixmap pixmap( 10, 10 ); QPixmap pixmap( 10, 10 );
pixmap.fill( Qt::red ); pixmap.fill( Qt::red );
QTest::newRow( "pixmap_valid" ) << QVariant::fromValue( pixmap ) << false; QTest::newRow( "pixmap_valid" ) << QVariant::fromValue( pixmap ) << false;
QTest::newRow( "image_invalid" ) << QVariant::fromValue( QImage() ) << true; QTest::newRow( "image_invalid" ) << QVariant::fromValue( QImage() ) << false;
QTest::newRow( "keysequence_valid" ) << QVariant::fromValue( QKeySequence( Qt::CTRL + Qt::Key_A ) ) << false; QTest::newRow( "keysequence_valid" ) << QVariant::fromValue( QKeySequence( Qt::CTRL + Qt::Key_A ) ) << false;
QTest::newRow( "palette_valid" ) << QVariant::fromValue(QPalette(QColor("turquoise"))) << false; QTest::newRow( "palette_valid" ) << QVariant::fromValue(QPalette(QColor("turquoise"))) << false;
QTest::newRow( "pen_valid" ) << QVariant::fromValue( QPen( Qt::red ) ) << false; QTest::newRow( "pen_valid" ) << QVariant::fromValue( QPen( Qt::red ) ) << false;
QTest::newRow( "pointarray_invalid" ) << QVariant::fromValue( QPolygon() ) << true; QTest::newRow( "pointarray_invalid" ) << QVariant::fromValue( QPolygon() ) << false;
QTest::newRow( "pointarray_valid" ) << QVariant::fromValue( QPolygon( QRect( 10, 10, 20, 20 ) ) ) << false; QTest::newRow( "pointarray_valid" ) << QVariant::fromValue( QPolygon( QRect( 10, 10, 20, 20 ) ) ) << false;
QTest::newRow( "region_invalid" ) << QVariant::fromValue( QRegion() ) << true; QTest::newRow( "region_invalid" ) << QVariant::fromValue( QRegion() ) << false;
QTest::newRow( "region_valid" ) << QVariant::fromValue( QRegion( 10, 10, 20, 20 ) ) << false; QTest::newRow( "region_valid" ) << QVariant::fromValue( QRegion( 10, 10, 20, 20 ) ) << false;
QTest::newRow("polygonf_invalid") << QVariant::fromValue(QPolygonF()) << true; QTest::newRow("polygonf_invalid") << QVariant::fromValue(QPolygonF()) << false;
QTest::newRow("polygonf_valid") << QVariant::fromValue(QPolygonF(QRectF(10, 10, 20, 20))) << false; QTest::newRow("polygonf_valid") << QVariant::fromValue(QPolygonF(QRectF(10, 10, 20, 20))) << false;
} }

View File

@ -2265,7 +2265,7 @@ void tst_QSqlQuery::prepare_bind_exec()
QVERIFY( q.next() ); QVERIFY( q.next() );
QCOMPARE( q.value( 0 ).toInt(), 6 ); QCOMPARE( q.value( 0 ).toInt(), 6 );
QVERIFY( q.isNull( 1 ) ); QVERIFY( q.value( 1 ).toString().isEmpty() );
if ( useUnicode ) { if ( useUnicode ) {
QVERIFY( q.next() ); QVERIFY( q.next() );