diff options
author | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2023-06-09 17:08:47 +0300 |
---|---|---|
committer | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2023-06-09 17:08:47 +0300 |
commit | 960a980dc885622cb84990c4da75d5060318302d (patch) | |
tree | b02009bb0e08ec4f94f2ef1d4318679700347d9a /tests/auto | |
parent | 540c4e4a5def8c350a49bb68380b787ae62490c6 (diff) | |
parent | cecf9b52904ab790e1a531698e9c5e33585227f0 (diff) |
Merge remote-tracking branch 'origin/tqtc/lts-5.15.11' into tqtc/lts-5.15-opensourcev5.15.11-lts-lgpl
Change-Id: I7b6e0ef657d1278405738f682b2795f8c345c3d4
Diffstat (limited to 'tests/auto')
25 files changed, 580 insertions, 29 deletions
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST new file mode 100644 index 0000000000..3793debebb --- /dev/null +++ b/tests/auto/qml/animation/qparallelanimationgroupjob/BLACKLIST @@ -0,0 +1,3 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[deleteChildrenWithRunningGroup] +ci macos # QTBUG-106356 diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 3c59f7bd56..773b826e62 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -346,7 +346,7 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(1)); - QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script)); + QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Script); QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(0)); QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); @@ -374,7 +374,7 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(2)); - QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); + QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Number); QCOMPARE(reinterpret_cast<const QV4::Value *>(testUnit->constants()) [obj->bindingTable()->value.constantValueIndex].doubleValue(), @@ -419,7 +419,7 @@ void tst_qmldiskcache::registerImportForImplicitComponent() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(1)); - QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object)); + QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Object); const QV4::CompiledData::Object *implicitComponent = qmlUnit->objectAt(obj->bindingTable()->value.objectIndex); QCOMPARE(testUnit->stringAtInternal(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType.elementName()); diff --git a/tests/auto/qml/qqmlecmascript/BLACKLIST b/tests/auto/qml/qqmlecmascript/BLACKLIST deleted file mode 100644 index bd25566eef..0000000000 --- a/tests/auto/qml/qqmlecmascript/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[gcCrashRegressionTest] -macos arm diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js new file mode 100644 index 0000000000..f51ab662ab --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.js @@ -0,0 +1,29 @@ +function init() { + Array.prototype.doPush = Array.prototype.push +} + +function nasty() { + var sc_Vector = Array; + var push = sc_Vector.prototype.doPush; + + // Change the memberData to hold something nasty on the current internalClass + sc_Vector.prototype.doPush = 5; + + // Trigger a re-allocation of memberData + for (var i = 0; i < 256; ++i) + sc_Vector.prototype[i + "string"] = function() { return 98; } + + // Change the (new) memberData back, to hold our doPush function again. + // This should propagate a protoId change all the way up to the lookup. + sc_Vector.prototype.doPush = push; +} + +function func() { + var b = []; + + // This becomes a lookup internally, which stores protoId and a pointer + // into the memberData. It should get invalidated when memberData is re-allocated. + b.doPush(3); + + return b; +} diff --git a/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml new file mode 100644 index 0000000000..460c40a750 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/internalClassParentGc.qml @@ -0,0 +1,13 @@ +import QtQml 2.15 + +import "internalClassParentGc.js" as Foo + +QtObject { + Component.onCompleted: { + gc(); + Foo.init(); + Foo.func(); + Foo.nasty(); + objectName = Foo.func()[0]; + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 2291c31895..9d5ffda180 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -390,6 +390,8 @@ private slots: void gcCrashRegressionTest(); void functionAsDefaultArgument(); + void internalClassParentGc(); + private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); static void verifyContextLifetime(QQmlContextData *ctxt); @@ -9395,6 +9397,15 @@ void tst_qqmlecmascript::functionAsDefaultArgument() QCOMPARE(root->objectName(), "didRun"); } +void tst_qqmlecmascript::internalClassParentGc() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("internalClassParentGc.qml")); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QCOMPARE(root->objectName(), "3"); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmllanguage/data/ComponentType.qml b/tests/auto/qml/qqmllanguage/data/ComponentType.qml new file mode 100644 index 0000000000..ad0dad455c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/ComponentType.qml @@ -0,0 +1,8 @@ +import QtQml 2 + +Component { + id: componentRoot + QtObject { + objectName: "enclosed" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.15a.qml b/tests/auto/qml/qqmllanguage/data/alias.15a.qml new file mode 100644 index 0000000000..ba8097c997 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.15a.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 + +Item { + id: root + + property alias symbol: symbol + symbol.layer.enabled: true + + Item { + id: symbol + } +} diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml new file mode 100644 index 0000000000..7e10553ae7 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl.qml @@ -0,0 +1,11 @@ +import QtQuick 2 + +Item { + id: root + Component { + id: accessibleNormal + Item {} + } + property alias accessibleNormalUrl: accessibleNormal.url + property url urlClone: root.accessibleNormalUrl // crashes qml utility +} diff --git a/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml new file mode 100644 index 0000000000..899b7aca37 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bindingAliasToComponentUrl2.qml @@ -0,0 +1,11 @@ +import QtQuick 2 +Item { + id: root + Component { + id: accessibleNormal + ComponentType { + id: inaccessibleNormal + } + } + property alias accessibleNormalProgress: accessibleNormal.progress +} diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt index e399799fe9..758be7feae 100644 --- a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt @@ -1,2 +1 @@ -2:8:Cannot assign to non-existent property "_G" - +2:11:Non-existent attached object diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 31a4135d89..df9d1401a9 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -174,7 +174,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::Execut return; } - if (binding->type != QV4::CompiledData::Binding::Type_Script) { + if (binding->type() != QV4::CompiledData::Binding::Type_Script) { error(binding, QStringLiteral("Custom parser invoked with the wrong property value. Expected script that evaluates to enum")); return; } diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index bffb62c59e..1be1533b63 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlprivate.h> #include <QtQml/qqmlincubator.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qfile.h> @@ -338,6 +339,8 @@ private slots: void hangOnWarning(); void ambiguousContainingType(); + void staticConstexprMembers(); + void bindingAliasToComponentUrl(); private: QQmlEngine engine; @@ -2146,6 +2149,22 @@ void tst_qqmllanguage::aliasProperties() QCOMPARE(subItem->property("y").toInt(), 1); } + // Nested property bindings on group properties that are actually aliases (QTBUG-94983) + { + QQmlComponent component(&engine, testFileUrl("alias.15a.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QObject> subItem = qvariant_cast<QObject*>(object->property("symbol")); + QVERIFY(!subItem.isNull()); + + QPointer<QObject> subSubItem = qvariant_cast<QObject*>(subItem->property("layer")); + + QCOMPARE(subSubItem->property("enabled").value<bool>(), true); + } + // Alias to sub-object with binding (QTBUG-57041) { // This is shold *not* crash. @@ -5875,6 +5894,50 @@ void tst_qqmllanguage::ambiguousContainingType() QVERIFY(!o.isNull()); } } + void tst_qqmllanguage::staticConstexprMembers() { + // this tests if the symbols are correclty defined for c++11 (gcc 11 and 12), and should + // not have any linker errors + using T = QObject; + using T2 = QObject; + + auto f1 = QQmlPrivate::Constructors<T, true>::createSingletonInstance; + auto f2 = QQmlPrivate::Constructors<T, false>::createSingletonInstance; + auto f3 = QQmlPrivate::Constructors<T, true>::createInto; + + auto f4 = QQmlPrivate::ExtendedType<T, true>::createParent; + auto f5 = QQmlPrivate::ExtendedType<T, false>::createParent; + auto f6 = QQmlPrivate::ExtendedType<T, true>::staticMetaObject; + + auto f7 = QQmlPrivate::QmlSingleton<T, T2>::Value; + + Q_UNUSED(f1); + Q_UNUSED(f2); + Q_UNUSED(f3); + Q_UNUSED(f3); + Q_UNUSED(f4); + Q_UNUSED(f5); + Q_UNUSED(f6); + Q_UNUSED(f7); + } + +void tst_qqmllanguage::bindingAliasToComponentUrl() +{ + QQmlEngine engine; + { + QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object); + QCOMPARE(object->property("accessibleNormalUrl"), object->property("urlClone")); + } + { + QQmlComponent component(&engine, testFileUrl("bindingAliasToComponentUrl2.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object); + QCOMPARE(object->property("accessibleNormalProgress"), QVariant(1.0)); + } +} QTEST_MAIN(tst_qqmllanguage) diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index a75a00bd01..b6e0b00cc4 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -96,13 +96,13 @@ void tst_qqmltranslation::translation() const bool expectCompiledTranslation = compiledTranslations.contains(propertyName); if (expectCompiledTranslation) { - if (binding->type != QV4::CompiledData::Binding::Type_Translation) + if (binding->type() != QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation)); + QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_Translation); } else { - if (binding->type == QV4::CompiledData::Binding::Type_Translation) + if (binding->type() == QV4::CompiledData::Binding::Type_Translation) qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation"; - QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation); + QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation); } } } @@ -148,11 +148,11 @@ void tst_qqmltranslation::idTranslation() for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); if (propertyName == "idTranslation") { - if (binding->type != QV4::CompiledData::Binding::Type_TranslationById) + if (binding->type() != QV4::CompiledData::Binding::Type_TranslationById) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; - QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById)); + QCOMPARE(binding->type(), QV4::CompiledData::Binding::Type_TranslationById); } else { - QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation); + QVERIFY(binding->type() != QV4::CompiledData::Binding::Type_Translation); } } } diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index 824fd89e5b..34da3a7c50 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -47,7 +47,7 @@ private slots: void gcStats(); void multiWrappedQObjects(); void accessParentOnDestruction(); - void clearICParent(); + void cleanInternalClasses(); }; void tst_qv4mm::gcStats() @@ -110,16 +110,41 @@ void tst_qv4mm::accessParentOnDestruction() QCOMPARE(obj->property("destructions").toInt(), 100); } -void tst_qv4mm::clearICParent() +void tst_qv4mm::cleanInternalClasses() { QV4::ExecutionEngine engine; QV4::Scope scope(engine.rootContext()); QV4::ScopedObject object(scope, engine.newObject()); + QV4::ScopedObject prototype(scope, engine.newObject()); + + // Set a prototype so that we get a unique IC. + object->setPrototypeOf(prototype); + + QV4::Scoped<QV4::InternalClass> prevIC(scope, object->internalClass()); + QVERIFY(prevIC->d()->transitions.empty()); + + uint prevIcChainLength = 0; + for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent) + ++prevIcChainLength; + + const auto checkICCHainLength = [&]() { + uint icChainLength = 0; + for (QV4::Heap::InternalClass *ic = object->internalClass(); ic; ic = ic->parent) + ++icChainLength; + + const uint redundant = object->internalClass()->numRedundantTransitions; + QVERIFY(redundant <= QV4::Heap::InternalClass::MaxRedundantTransitions); + + // A removal makes two transitions redundant. + QVERIFY(icChainLength <= prevIcChainLength + 2 * redundant); + }; + + const uint numTransitions = 16 * 1024; // Keep identifiers in a separate array so that we don't have to allocate them in the loop that // should test the GC on InternalClass allocations. QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject()); - for (uint i = 0; i < 16 * 1024; ++i) { + for (uint i = 0; i < numTransitions; ++i) { QV4::Scope scope(&engine); QV4::ScopedString s(scope); s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i)); @@ -130,22 +155,60 @@ void tst_qv4mm::clearICParent() object->insertMember(s, v); } - // When allocating the InternalClass objects required for deleting properties, the GC should - // eventually run and remove all but the last two. - // If we ever manage to avoid allocating the InternalClasses in the first place we will need - // to change this test. - for (uint i = 0; i < 16 * 1024; ++i) { + // There is a chain of ICs originating from the original class. + QCOMPARE(prevIC->d()->transitions.size(), 1u); + QVERIFY(prevIC->d()->transitions.front().lookup != nullptr); + + // When allocating the InternalClass objects required for deleting properties, eventually + // the IC chain gets truncated, dropping all the removed properties. + for (uint i = 0; i < numTransitions; ++i) { QV4::Scope scope(&engine); QV4::ScopedString s(scope, identifiers->get(i)); QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass()); QVERIFY(ic->d()->parent != nullptr); - object->deleteProperty(s->toPropertyKey()); + QV4::ScopedValue val(scope, object->get(s->toPropertyKey())); + QCOMPARE(val->toNumber(), double(i)); + QVERIFY(object->deleteProperty(s->toPropertyKey())); + QVERIFY(!object->hasProperty(s->toPropertyKey())); QVERIFY(object->internalClass() != ic->d()); - QCOMPARE(object->internalClass()->parent, ic->d()); - if (ic->d()->parent == nullptr) - return; } - QFAIL("Garbage collector was not triggered by large amount of InternalClasses"); + + // None of the properties we've added are left + for (uint i = 0; i < numTransitions; ++i) { + QV4::ScopedString s(scope, identifiers->get(i)); + QVERIFY(!object->hasProperty(s->toPropertyKey())); + } + + // Also no other properties have appeared + QScopedPointer<QV4::OwnPropertyKeyIterator> iterator(object->ownPropertyKeys(object)); + QVERIFY(!iterator->next(object).isValid()); + + checkICCHainLength(); + + // Add and remove properties until it clears all remaining redundant ones + uint i = 0; + while (object->internalClass()->numRedundantTransitions > 0) { + i = (i + 1) % numTransitions; + QV4::ScopedString s(scope, identifiers->get(i)); + QV4::ScopedValue v(scope); + v->setDouble(i); + object->insertMember(s, v); + QVERIFY(object->deleteProperty(s->toPropertyKey())); + } + + // Make sure that all dangling ICs are actually gone. + scope.engine->memoryManager->runGC(); + + // Now the GC has removed the ICs we originally added by adding properties. + QVERIFY(prevIC->d()->transitions.empty() || prevIC->d()->transitions.front().lookup == nullptr); + + // Same thing with redundant prototypes + for (uint i = 0; i < numTransitions; ++i) { + QV4::ScopedObject prototype(scope, engine.newObject()); + object->setPrototypeOf(prototype); // Makes previous prototype redundant + } + + checkICCHainLength(); } QTEST_MAIN(tst_qv4mm) diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST new file mode 100644 index 0000000000..636aef4904 --- /dev/null +++ b/tests/auto/quick/qquickflickable/BLACKLIST @@ -0,0 +1,3 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[setContentPositionWhileDragging] +ci macos # QTBUG-106278 diff --git a/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml new file mode 100644 index 0000000000..57a4273257 --- /dev/null +++ b/tests/auto/quick/qquickflickable/data/contentPosWhileDragging.qml @@ -0,0 +1,22 @@ +import QtQuick 2.14 + +Item { + id: root + width: 500 + height: 500 + Flickable { + anchors.centerIn: parent + width: 100 + height: 100 + clip: true + contentWidth: content.width + contentHeight: content.height + Rectangle { + id: content + width: 320 + height: width + color: "#41cd52" + radius: width/2 + } + } +} diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 9fa51da6f8..d092cd0170 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -27,10 +27,10 @@ ****************************************************************************/ #include <qtest.h> #include <QtTest/QSignalSpy> +#include <QtQuick/qquickview.h> #include <QtGui/QStyleHints> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> -#include <QtQuick/qquickview.h> #include <private/qquickflickable_p.h> #include <private/qquickflickable_p_p.h> #include <private/qquickmousearea_p.h> @@ -206,6 +206,8 @@ private slots: void synchronousDrag_data(); void synchronousDrag(); void visibleAreaBinding(); + void setContentPositionWhileDragging_data(); + void setContentPositionWhileDragging(); private: void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); @@ -2558,6 +2560,109 @@ void tst_qquickflickable::visibleAreaBinding() // Shouldn't crash. } +void tst_qquickflickable::setContentPositionWhileDragging_data() +{ + QTest::addColumn<bool>("isHorizontal"); + QTest::addColumn<int>("newPos"); + QTest::addColumn<int>("newExtent"); + QTest::newRow("horizontal, setContentX") << true << 0 << -1; + QTest::newRow("vertical, setContentY") << false << 0 << -1; + QTest::newRow("horizontal, setContentWidth") << true << -1 << 200; + QTest::newRow("vertical, setContentHeight") << false << -1 << 200; +} + +void tst_qquickflickable::setContentPositionWhileDragging() // QTBUG-104966 +{ + QFETCH(bool, isHorizontal); + QFETCH(int, newPos); + QFETCH(int, newExtent); + + QScopedPointer<QQuickView> window(new QQuickView); + window->setSource(testFileUrl("contentPosWhileDragging.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QQuickItem *rootItem = window->rootObject(); + QVERIFY(rootItem); + + QVERIFY(window->isVisible()); + QQuickFlickable *flickable = rootItem->findChild<QQuickFlickable *>(); + QVERIFY(flickable); + + const auto contentPos = [flickable]() -> QPoint { + return QPoint(flickable->contentX(), flickable->contentY()); + }; + const qreal threshold = + qApp->styleHints()->startDragDistance() * flickable->parentItem()->scale(); + const QPoint thresholdPnt(qRound(threshold), qRound(threshold)); + const auto flickableCenterPos = flickable->mapToScene({flickable->width() / 2, flickable->height() / 2}).toPoint(); + + // Drag the mouse until we have surpassed the mouse drag threshold and a drag is initiated + // by checking for flickable->isDragging() + QPoint pos = flickableCenterPos; + moveAndPress(window.data(), pos); + int j = 1; + QVERIFY(!flickable->isDragging()); + while (!flickable->isDragging()) { + pos = flickableCenterPos - QPoint(j, j); + QTest::mouseMove(window.data(), pos); + j++; + } + + // Now we have entered the drag state + QVERIFY(flickable->isDragging()); + QCOMPARE(flickable->contentX(), 0); + QCOMPARE(flickable->contentY(), 0); + QVERIFY(flickable->width() > 0); + QVERIFY(flickable->height() > 0); + + + const int moveLength = 50; + const QPoint unitDelta(isHorizontal ? 1 : 0, isHorizontal ? 0 : 1); + const QPoint moveDelta = unitDelta * moveLength; + + pos -= 3*moveDelta; + QTest::mouseMove(window.data(), pos); + // Should be positive because we drag in the opposite direction + QCOMPARE(contentPos(), 3 * moveDelta); + QPoint expectedContentPos; + + // Set the content item position back to zero *while dragging* (!!) + if (newPos >= 0) { + if (isHorizontal) { + flickable->setContentX(newPos); + } else { + flickable->setContentY(newPos); + } + // Continue dragging + pos -= moveDelta; + expectedContentPos = moveDelta; + } else if (newExtent >= 0) { + // ...or reduce the content size be be less than current (contentX, contentY) position + // This forces the content item to move. + expectedContentPos = moveDelta; + if (isHorizontal) { + flickable->setContentWidth(newExtent); + } else { + flickable->setContentHeight(newExtent); + } + // Assumption is that the contentItem is aligned to the bottom of the flickable + // We therefore cannot scroll/flick it further down. Drag it up towards the top instead + // (by moving mouse down). + pos += moveDelta; + } + + QTest::mouseMove(window.data(), pos); + + // Make sure that the contentItem was only dragged the delta in mouse movement since the last + // setContentX/Y() call. + QCOMPARE(contentPos(), expectedContentPos); + QTest::mouseRelease(window.data(), Qt::LeftButton, Qt::NoModifier, pos); + QVERIFY(!flickable->isDragging()); +} + QTEST_MAIN(tst_qquickflickable) #include "tst_qquickflickable.moc" diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml index 3a41bdb3e0..de335386cf 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml @@ -103,5 +103,55 @@ Item { layout.destroy() } + + Component { + id: layout_setCurrentIndex_Component + + StackLayout { + width: 200 + height: 200 + + property alias firstItem : rect + property alias secondItem: rowLayout + + Rectangle { + id: rect + color: "red" + implicitWidth: 10 + implicitHeight: 10 + } + RowLayout { + id: rowLayout + spacing: 0 + Rectangle { + color: "green" + implicitWidth: 10 + implicitHeight: 10 + Layout.fillWidth: true + Layout.fillHeight: true + } + Rectangle { + color: "blue" + implicitWidth: 10 + implicitHeight: 10 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + function test_setCurrentIndex() + { + var layout = layout_setCurrentIndex_Component.createObject(container) + compare(layout.firstItem.width, 200) + + // Invalidate the StackLayout (and its cached size hints) + layout.firstItem.implicitWidth = 42 + + layout.currentIndex = 1 + compare(layout.secondItem.width, 200) // width should not be -1 + layout.destroy() + } } } diff --git a/tests/auto/quick/qquickloader/BLACKLIST b/tests/auto/quick/qquickloader/BLACKLIST index a45a300607..aeb3674482 100644 --- a/tests/auto/quick/qquickloader/BLACKLIST +++ b/tests/auto/quick/qquickloader/BLACKLIST @@ -1,4 +1,5 @@ # Test fails on qemu when bound to one core, passes on real ARM # QTBUG-63049 [asyncToSync1] +ci ubuntu-20.04 # QTBUG-106424 b2qt diff --git a/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml b/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml new file mode 100644 index 0000000000..3833c5a8a9 --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/preventStealingListViewChild.qml @@ -0,0 +1,31 @@ +import QtQuick 2.15 + +ListView { + id: flick + width: 640 + height: 480 + model: 100 + + delegate: Rectangle { + border.color: "#81e889" + width: 640; height: 100 + Text { text: "Row " + index } + } + + Rectangle { + anchors.right: parent.right + anchors.margins: 2 + color: ma.pressed ? "#81e889" : "#c2f4c6" + width: 50; height: 50 + radius: 5 + MouseArea { + id: ma + anchors.fill: parent + preventStealing: true + drag { + target: parent + axis: Drag.YAxis + } + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 27fee2aab3..941d6dc47b 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -129,6 +129,7 @@ private slots: void invalidClick(); void pressedOrdering(); void preventStealing(); + void preventStealingListViewChild(); void clickThrough(); void hoverPosition(); void hoverPropagation(); @@ -1212,6 +1213,39 @@ void tst_QQuickMouseArea::preventStealing() QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p); } +// QTBUG-103522 +void tst_QQuickMouseArea::preventStealingListViewChild() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("preventStealingListViewChild.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window.rootObject()); + QVERIFY(flickable); + QQuickMouseArea *mouseArea = flickable->findChild<QQuickMouseArea*>(); + QVERIFY(mouseArea); + QPoint p = mouseArea->mapToScene(mouseArea->boundingRect().center()).toPoint(); + const int threshold = qApp->styleHints()->startDragDistance(); + + flickable->flick(0, -10000); + for (int i = 0; i < 2; ++i) { + QVERIFY(flickable->isMovingVertically()); + QTest::touchEvent(&window, device).press(0, p); + QQuickTouchUtils::flush(&window); + for (int j = 0; j < 4 && !mouseArea->drag()->active(); ++j) { + p += QPoint(0, threshold); + QTest::touchEvent(&window, device).move(0, p); + QQuickTouchUtils::flush(&window); + } + // MouseArea should be dragged because of preventStealing; ListView does not steal the grab. + QVERIFY(mouseArea->drag()->active()); + QCOMPARE(flickable->isDragging(), false); + QTest::touchEvent(&window, device).release(0, p); + QCOMPARE(mouseArea->drag()->active(), false); + } +} + void tst_QQuickMouseArea::clickThrough() { //With no handlers defined click, doubleClick and PressAndHold should propagate to those with handlers diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml new file mode 100644 index 0000000000..7ca3f187d6 --- /dev/null +++ b/tests/auto/quick/qquickmultipointtoucharea/data/nestedMouseArea.qml @@ -0,0 +1,26 @@ +import QtQuick 2.12 + +Item { + id: root + width: 300; height: 300 + property point mptaPoint + property point maPoint + MultiPointTouchArea { + anchors.fill : parent + onPressed: function(touchPoints) { + root.mptaPoint = Qt.point(touchPoints[0].x, touchPoints[0].y) + } + MouseArea { + id: ma + width: 100; height: 100 + anchors.centerIn: parent + onPressed: function(mouse) { + root.maPoint = Qt.point(mouse.x, mouse.y) + } + } + Rectangle { + anchors.fill: ma + border.color: "grey" + } + } +} diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index a9d557915a..3aff9a293a 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -78,6 +78,7 @@ private slots: void cancel(); void stationaryTouchWithChangingPressure(); void nestedPinchAreaMouse(); + void touchFiltering(); private: QQuickView *createAndShowView(const QString &file); @@ -1456,6 +1457,23 @@ void tst_QQuickMultiPointTouchArea::nestedPinchAreaMouse() QCOMPARE(mpta->property("releasedCount").toInt(), 1); } +void tst_QQuickMultiPointTouchArea::touchFiltering() // QTBUG-74028 +{ + QScopedPointer<QQuickView> window(createAndShowView("nestedMouseArea.qml")); + QVERIFY(window->rootObject() != nullptr); + QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>(); + QVERIFY(mpta); + QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>(); + QVERIFY(ma); + + QSignalSpy mptaSpy(mpta, &QQuickMultiPointTouchArea::pressed); + const QPoint pt = window->rootObject()->boundingRect().center().toPoint(); + QTest::touchEvent(window.data(), device).press(1, pt); + QQuickTouchUtils::flush(window.data()); + QTRY_COMPARE(mpta->parentItem()->property("mptaPoint").toPoint(), pt); + QCOMPARE(mpta->parentItem()->property("maPoint").toPoint(), ma->boundingRect().center().toPoint()); + QCOMPARE(mptaSpy.count(), 1); +} QTEST_MAIN(tst_QQuickMultiPointTouchArea) diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index ff191bbc7f..03570b43ce 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -3478,6 +3478,46 @@ void tst_qquicktext::fontSizeMode() myText->setElideMode(QQuickText::ElideNone); QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + + // Growing height needs to update the baselineOffset when AlignBottom is used + // and text is NOT wrapped + myText->setVAlign(QQuickText::AlignBottom); + myText->setFontSizeMode(QQuickText::Fit); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + + int baselineOffset = myText->baselineOffset(); + myText->setHeight(myText->height() * 2); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QVERIFY(myText->baselineOffset() > baselineOffset); + + // Growing height needs to update the baselineOffset when AlignBottom is used + // and the text is wrapped + myText->setVAlign(QQuickText::AlignBottom); + myText->setFontSizeMode(QQuickText::Fit); + myText->setWrapMode(QQuickText::NoWrap); + myText->resetMaximumLineCount(); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + + baselineOffset = myText->baselineOffset(); + myText->setHeight(myText->height() * 2); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QVERIFY(myText->baselineOffset() > baselineOffset); + + // Check baselineOffset for the HorizontalFit case + myText->setVAlign(QQuickText::AlignBottom); + myText->setFontSizeMode(QQuickText::HorizontalFit); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QSignalSpy baselineOffsetSpy(myText, SIGNAL(baselineOffsetChanged(qreal))); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + const qreal oldBaselineOffset = myText->baselineOffset(); + myText->setHeight(myText->height() + 42); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QCOMPARE(baselineOffsetSpy.count(), 1); + QCOMPARE(myText->baselineOffset(), oldBaselineOffset + 42); + myText->setHeight(myText->height() - 42); + QVERIFY(QQuickTest::qWaitForItemPolished(myText)); + QCOMPARE(baselineOffsetSpy.count(), 2); + QCOMPARE(myText->baselineOffset(), oldBaselineOffset); } void tst_qquicktext::fontSizeModeMultiline_data() |