diff options
Diffstat (limited to 'tests')
20 files changed, 845 insertions, 14 deletions
diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp index 26919af1b6..f91d0135a9 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp +++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp @@ -84,10 +84,15 @@ void tst_QPacketProtocol::init() QVERIFY(m_server->listen(QHostAddress("127.0.0.1"))); m_client = new QTcpSocket(this); + + QSignalSpy serverSpy(m_server, SIGNAL(newConnection())); + QSignalSpy clientSpy(m_client, SIGNAL(connected())); + m_client->connectToHost(m_server->serverAddress(), m_server->serverPort()); - QVERIFY(m_client->waitForConnected()); - QVERIFY(m_server->waitForNewConnection(10000)); + QVERIFY(clientSpy.count() > 0 || clientSpy.wait()); + QVERIFY(serverSpy.count() > 0 || serverSpy.wait()); + m_serverConn = m_server->nextPendingConnection(); } diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml index 8847055a70..5103168fd3 100644 --- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml @@ -165,6 +165,43 @@ Item { } } } + + // unshift + msco.stringListProperty = [ "one", "two" ] + var unshiftedVal = msco.stringListProperty.unshift("zero") + expected = [ "zero", "one", "two" ] + if (msco.stringListProperty.toString() != expected.toString()) success = false; + expected = 3 + if (msco.stringListProperty.length != expected) success = false + msco.stringListProperty = [ ] + msco.stringListProperty.unshift("zero", "one") + expected = [ "zero", "one" ] + if (msco.stringListProperty.toString() != expected.toString()) success = false; + expected = 2 + if (msco.stringListProperty.length != expected) success = false + + // shift + msco.stringListProperty = [ "one", "two", "three" ] + var shiftVal = msco.stringListProperty.shift() + expected = [ "two", "three" ] + if (msco.stringListProperty.toString() != expected.toString()) success = false; + expected = "one" + if (shiftVal != expected) success = false + shiftVal = msco.stringListProperty.shift() + expected = [ "three" ] + if (msco.stringListProperty.toString() != expected.toString()) success = false; + expected = "two" + if (shiftVal != expected) success = false + shiftVal = msco.stringListProperty.shift() + expected = 0 + if (msco.stringListProperty.length != expected) success = false; + expected = "three" + if (shiftVal != expected) success = false + shiftVal = msco.stringListProperty.shift() + expected = 0 + if (msco.stringListProperty.length != expected) success = false; + expected = undefined + if (shiftVal != expected) success = false } property variant variantList: [ 1, 2, 3, 4, 5 ]; diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index eb06b9e57d..560d86005c 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -300,6 +300,62 @@ static QObject *create_singletonWithEnum(QQmlEngine *, QJSEngine *) return new SingletonWithEnum; } +QObjectContainer::QObjectContainer() + : widgetParent(0) + , gcOnAppend(false) +{} + +QQmlListProperty<QObject> QObjectContainer::data() +{ + return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear); +} + +void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject *o) +{ + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + that->dataChildren.append(o); + QObject::connect(o, SIGNAL(destroyed(QObject*)), prop->object, SLOT(childDestroyed(QObject*))); + + if (that->gcOnAppend) { + QQmlEngine *engine = qmlEngine(that); + engine->collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } +} + +int QObjectContainer::children_count(QQmlListProperty<QObject> *prop) +{ + return static_cast<QObjectContainer*>(prop->object)->dataChildren.count(); +} + +QObject *QObjectContainer::children_at(QQmlListProperty<QObject> *prop, int index) +{ + return static_cast<QObjectContainer*>(prop->object)->dataChildren.at(index); +} + +void QObjectContainer::children_clear(QQmlListProperty<QObject> *prop) +{ + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + foreach (QObject *c, that->dataChildren) + QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); + that->dataChildren.clear(); +} + +void QObjectContainer::childDestroyed(QObject *child) { + dataChildren.removeAll(child); +} + +void FloatingQObject::classBegin() +{ + setParent(0); +} + +void FloatingQObject::componentComplete() +{ + Q_ASSERT(!parent()); +} + void registerTypes() { qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObjectAlias"); @@ -381,6 +437,10 @@ void registerTypes() qmlRegisterSingletonType<testImportOrderApi>("Qt.test.importOrderApi2",1,0,"Data",testImportOrder_api2); qmlRegisterSingletonType<SingletonWithEnum>("Qt.test.singletonWithEnum", 1, 0, "SingletonWithEnum", create_singletonWithEnum); + + qmlRegisterType<QObjectContainer>("Qt.test", 1, 0, "QObjectContainer"); + qmlRegisterType<QObjectContainerWithGCOnAppend>("Qt.test", 1, 0, "QObjectContainerWithGCOnAppend"); + qmlRegisterType<FloatingQObject>("Qt.test", 1, 0, "FloatingQObject"); } #include "testtypes.moc" diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index 928d594f62..d5a1220f23 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1661,6 +1661,51 @@ public: }; }; +// Like QtObject, but with default property +class QObjectContainer : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false) +public: + QObjectContainer(); + + QQmlListProperty<QObject> data(); + + static void children_append(QQmlListProperty<QObject> *prop, QObject *o); + static int children_count(QQmlListProperty<QObject> *prop); + static QObject *children_at(QQmlListProperty<QObject> *prop, int index); + static void children_clear(QQmlListProperty<QObject> *prop); + + QList<QObject*> dataChildren; + QWidget *widgetParent; + bool gcOnAppend; + +protected slots: + void childDestroyed(QObject *child); +}; + +class QObjectContainerWithGCOnAppend : public QObjectContainer +{ + Q_OBJECT +public: + QObjectContainerWithGCOnAppend() + { + gcOnAppend = true; + } +}; + +class FloatingQObject : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) +public: + FloatingQObject() {} + + virtual void classBegin(); + virtual void componentComplete(); +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a1e36b42e6..a9486a8e63 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -322,6 +322,7 @@ private slots: void varPropertyAccessOnObjectWithInvalidContext(); void importedScriptsAccessOnObjectWithInvalidContext(); void contextObjectOnLazyBindings(); + void garbageCollectionDuringCreation(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -7603,6 +7604,33 @@ void tst_qqmlecmascript::contextObjectOnLazyBindings() QCOMPARE(subObject->property("testValue").toInt(), int(42)); } +void tst_qqmlecmascript::garbageCollectionDuringCreation() +{ + QQmlComponent component(&engine); + component.setData("import Qt.test 1.0\n" + "QObjectContainerWithGCOnAppend {\n" + " objectName: \"root\"\n" + " FloatingQObject {\n" + " objectName: \"parentLessChild\"\n" + " property var blah;\n" // Ensure we have JS wrapper + " }\n" + "}\n", + QUrl()); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast<QObjectContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QObject *child = container->dataChildren.first(); + QQmlData *ddata = QQmlData::get(child); + QVERIFY(!ddata->jsWrapper.isNullOrUndefined()); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 0); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml index 03be57909f..069b62a726 100644 --- a/tests/auto/qmltest/listview/tst_listview.qml +++ b/tests/auto/qmltest/listview/tst_listview.qml @@ -108,6 +108,33 @@ Item { property int createdDelegates: 0 } + ListView + { + id: listInteractiveCurrentIndexEnforce + width: 600 + height: 600 + + snapMode: ListView.SnapOneItem + orientation: ListView.Horizontal + interactive: !currentItem.moving + highlightRangeMode: ListView.StrictlyEnforceRange + + model: 4 + + focus: true + Keys.onPressed: if (event.key == Qt.Key_K) currentIndex = currentIndex + 1; + + delegate: Flickable { + width: 600 + height: 600 + contentWidth: 600 + contentHeight: 1200 + + MouseArea { anchors.fill: parent } + Rectangle { anchors.fill: parent; color: index == 0 ? "red" : index == 1 ? "green" : index == 2 ? "blue" : "white" } + } + } + Component { id: delegateModelAfterCreateComponent Rectangle { @@ -272,5 +299,19 @@ Item { listViewDelegateModelAfterCreate.model = 40; verify(listViewDelegateModelAfterCreate.createdDelegates > 0); } + + function test_listInteractiveCurrentIndexEnforce() { + mousePress(listInteractiveCurrentIndexEnforce, 10, 50); + mouseMove(listInteractiveCurrentIndexEnforce, 10, 40); + mouseMove(listInteractiveCurrentIndexEnforce, 10, 30); + mouseMove(listInteractiveCurrentIndexEnforce, 10, 20); + mouseMove(listInteractiveCurrentIndexEnforce, 10, 10); + compare(listInteractiveCurrentIndexEnforce.interactive, false); + mouseRelease(listInteractiveCurrentIndexEnforce, 10, 10); + tryCompare(listInteractiveCurrentIndexEnforce, "interactive", true); + keyClick("k"); + compare(listInteractiveCurrentIndexEnforce.currentIndex, 1); + tryCompare(listInteractiveCurrentIndexEnforce, "contentX", listInteractiveCurrentIndexEnforce.width); + } } } diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index 7d50e130f2..760cdd7b49 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -281,7 +281,7 @@ void tst_QQuickAccessible::basicPropertiesTest() QVERIFY(item); QCOMPARE(item->childCount(), 2); QCOMPARE(item->rect().size(), QSize(400, 400)); - QCOMPARE(item->role(), QAccessible::Pane); + QCOMPARE(item->role(), QAccessible::Client); QCOMPARE(iface->indexOfChild(item), 0); QAccessibleInterface *text = item->child(0); diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 863fb69b84..a350074b42 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -3613,12 +3613,13 @@ void tst_QQuickGridView::resetModel_headerFooter() model.reset(); - header = findItem<QQuickItem>(contentItem, "header"); - QVERIFY(header); + // A reset should not force a new header or footer to be created. + QQuickItem *newHeader = findItem<QQuickItem>(contentItem, "header"); + QVERIFY(newHeader == header); QCOMPARE(header->y(), -header->height()); - footer = findItem<QQuickItem>(contentItem, "footer"); - QVERIFY(footer); + QQuickItem *newFooter = findItem<QQuickItem>(contentItem, "footer"); + QVERIFY(newFooter == footer); QCOMPARE(footer->y(), 60.*2); delete window; diff --git a/tests/auto/quick/qquickitem2/data/keysforward.qml b/tests/auto/quick/qquickitem2/data/keysforward.qml index f0cb4d9508..5677aa62f1 100644 --- a/tests/auto/quick/qquickitem2/data/keysforward.qml +++ b/tests/auto/quick/qquickitem2/data/keysforward.qml @@ -58,7 +58,7 @@ Item { objectName: "primary" property var pressedKeys: [] property var releasedKeys: [] - Keys.forwardTo: secondaryTarget + Keys.forwardTo: [ secondaryTarget, extraTarget ] Keys.onPressed: { event.accepted = event.key === Qt.Key_P; var keys = pressedKeys; keys.push(event.key); pressedKeys = keys } Keys.onReleased: { event.accepted = event.key === Qt.Key_P; var keys = releasedKeys; keys.push(event.key); releasedKeys = keys } @@ -81,4 +81,8 @@ Item { Keys.onPressed: { event.accepted = event.key === Qt.Key_S; var keys = pressedKeys; keys.push(event.key); pressedKeys = keys } Keys.onReleased: { event.accepted = event.key === Qt.Key_S; var keys = releasedKeys; keys.push(event.key); releasedKeys = keys } } + + Item { + id: extraTarget + } } diff --git a/tests/auto/quick/qquicklistview/data/layoutChangeSort.qml b/tests/auto/quick/qquicklistview/data/layoutChangeSort.qml new file mode 100644 index 0000000000..e54f164e45 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/layoutChangeSort.qml @@ -0,0 +1,58 @@ +import QtQuick 2.0 +import QtQml.Models 2.1 + +Rectangle { + id: root + width: 240 + height: 320 + color: "#ffffff" + + + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + height: 20 + width: 240 + Text { + objectName: "delegateText" + text: display + } + color: ListView.isCurrentItem ? "lightsteelblue" : "white" + } + } + + DelegateModel { + id: delegateModel + objectName: "delegateModel" + model: testModel + delegate: myDelegate + } + + ListView { + id: list + objectName: "listView" + model: delegateModel; + focus: true + anchors.fill: parent + + + section { + property: "SortRole"; + delegate: Rectangle { + width: parent.width; + height: 20; + color: "steelblue"; + + Text { + anchors { + fill: parent; + margins: 5; + } + text: section; + } + } + } + } +} diff --git a/tests/auto/quick/qquicklistview/data/simplelistview.qml b/tests/auto/quick/qquicklistview/data/simplelistview.qml new file mode 100644 index 0000000000..56a96150c5 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/simplelistview.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +ListView { + width: 400 + height: 400 + model: 100 + delegate: Rectangle { + height: 40; width: 400 + color: index % 2 ? "lightsteelblue" : "lightgray" + } +} diff --git a/tests/auto/quick/qquicklistview/qquicklistview.pro b/tests/auto/quick/qquicklistview/qquicklistview.pro index 2ae04d32fe..c9b634b9e8 100644 --- a/tests/auto/quick/qquicklistview/qquicklistview.pro +++ b/tests/auto/quick/qquicklistview/qquicklistview.pro @@ -4,10 +4,13 @@ TARGET = tst_qquicklistview macx:CONFIG -= app_bundle HEADERS += incrementalmodel.h \ - proxytestinnermodel.h + proxytestinnermodel.h \ + randomsortmodel.h SOURCES += tst_qquicklistview.cpp \ incrementalmodel.cpp \ - proxytestinnermodel.cpp + proxytestinnermodel.cpp \ + randomsortmodel.cpp + include (../../shared/util.pri) include (../shared/util.pri) diff --git a/tests/auto/quick/qquicklistview/randomsortmodel.cpp b/tests/auto/quick/qquicklistview/randomsortmodel.cpp new file mode 100644 index 0000000000..e9082e39c9 --- /dev/null +++ b/tests/auto/quick/qquicklistview/randomsortmodel.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "randomsortmodel.h" + +RandomSortModel::RandomSortModel(QObject* parent): + QAbstractListModel(parent) +{ + for (int i = 0; i < 10; ++i) { + mData.append(qMakePair(QString::fromLatin1("Item %1").arg(i), i * 10)); + } +} + +QHash<int, QByteArray> RandomSortModel::roleNames() const +{ + QHash<int,QByteArray> roles = QAbstractItemModel::roleNames(); + roles[Qt::UserRole] = "SortRole"; + return roles; +} + + +int RandomSortModel::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid()) + return mData.count(); + + return 0; +} + +QVariant RandomSortModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (index.row() >= mData.count()) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return QString::fromLatin1("%1 (weight %2)").arg(mData[index.row()].first).arg(mData[index.row()].second); + } else if (role == Qt::UserRole) { + return mData[index.row()].second; + } + + return QVariant(); +} + +void RandomSortModel::randomize() +{ + const int row = qrand() % mData.count(); + int random; + bool exists = false; + // Make sure we won't end up with two items with the same weight, as that + // would make unit-testing much harder + do { + exists = false; + random = qrand() % (mData.count() * 10); + QList<QPair<QString, int> >::ConstIterator iter, end; + for (iter = mData.constBegin(), end = mData.constEnd(); iter != end; ++iter) { + if ((*iter).second == random) { + exists = true; + break; + } + } + } while (exists); + mData[row].second = random; + Q_EMIT dataChanged(index(row, 0), index(row, 0)); +} diff --git a/tests/auto/quick/qquicklistview/randomsortmodel.h b/tests/auto/quick/qquicklistview/randomsortmodel.h new file mode 100644 index 0000000000..8d28698d9b --- /dev/null +++ b/tests/auto/quick/qquicklistview/randomsortmodel.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RANDOMSORTMODEL_H +#define RANDOMSORTMODEL_H + +#include <QAbstractListModel> + +class RandomSortModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit RandomSortModel(QObject* parent = 0); + QHash<int, QByteArray> roleNames() const; + + QVariant data(const QModelIndex& index, int role) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + + void randomize(); + + private: + QList<QPair<QString, int> > mData; +}; + +#endif // RANDOMSORTMODEL_H diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index f267b93b26..d22f3487da 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -58,6 +58,7 @@ #include "../shared/visualtestutil.h" #include "incrementalmodel.h" #include "proxytestinnermodel.h" +#include "randomsortmodel.h" #include <math.h> Q_DECLARE_METATYPE(Qt::LayoutDirection) @@ -232,6 +233,11 @@ private slots: void roundingErrors(); void roundingErrors_data(); + void QTBUG_38209(); + void programmaticFlickAtBounds(); + + void layoutChange(); + void QTBUG_39492_data(); void QTBUG_39492(); @@ -4085,12 +4091,13 @@ void tst_QQuickListView::resetModel_headerFooter() model.reset(); - header = findItem<QQuickItem>(contentItem, "header"); - QVERIFY(header); + // A reset should not force a new header or footer to be created. + QQuickItem *newHeader = findItem<QQuickItem>(contentItem, "header"); + QVERIFY(newHeader == header); QCOMPARE(header->y(), -header->height()); - footer = findItem<QQuickItem>(contentItem, "footer"); - QVERIFY(footer); + QQuickItem *newFooter = findItem<QQuickItem>(contentItem, "footer"); + QVERIFY(newFooter == footer); QCOMPARE(footer->y(), 30.*4); delete window; @@ -7316,6 +7323,93 @@ void tst_QQuickListView::roundingErrors_data() QTest::newRow("pixelAligned=false") << false; } +void tst_QQuickListView::QTBUG_38209() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("simplelistview.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = qobject_cast<QQuickListView *>(window->rootObject()); + QVERIFY(listview); + + // simulate mouse flick + flick(window.data(), QPoint(200, 200), QPoint(200, 50), 100); + QTRY_VERIFY(listview->isMoving() == false); + qreal contentY = listview->contentY(); + + // flick down + listview->flick(0, 1000); + + // ensure we move more than just a couple pixels + QTRY_VERIFY(contentY - listview->contentY() > qreal(100.0)); +} + +void tst_QQuickListView::programmaticFlickAtBounds() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("simplelistview.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = qobject_cast<QQuickListView *>(window->rootObject()); + QVERIFY(listview); + QSignalSpy spy(listview, SIGNAL(contentYChanged())); + + // flick down + listview->flick(0, 1000); + + // verify that there is movement beyond bounds + QVERIFY(spy.wait(100)); + + // reset, and test with StopAtBounds + listview->cancelFlick(); + listview->returnToBounds(); + QTRY_COMPARE(listview->contentY(), qreal(0.0)); + listview->setBoundsBehavior(QQuickFlickable::StopAtBounds); + + // flick down + listview->flick(0, 1000); + + // verify that there is no movement beyond bounds + QVERIFY(!spy.wait(100)); +} + +void tst_QQuickListView::layoutChange() +{ + RandomSortModel *model = new RandomSortModel; + QSortFilterProxyModel *sortModel = new QSortFilterProxyModel; + sortModel->setSourceModel(model); + sortModel->setSortRole(Qt::UserRole); + sortModel->setDynamicSortFilter(true); + sortModel->sort(0); + + QScopedPointer<QQuickView> window(createView()); + window->rootContext()->setContextProperty("testModel", QVariant::fromValue(sortModel)); + window->setSource(testFileUrl("layoutChangeSort.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = window->rootObject()->findChild<QQuickListView *>("listView"); + QVERIFY(listview); + + for (int iter = 0; iter < 100; iter++) { + for (int i = 0; i < sortModel->rowCount(); ++i) { + QQuickItem *delegateItem = listview->itemAt(10, 10 + 2 * i * 20 + 20); // item + group + QVERIFY(delegateItem); + QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>("delegateText"); + QVERIFY(delegateText); + + QCOMPARE(delegateText->property("text").toString(), + sortModel->index(i, 0, QModelIndex()).data().toString()); + } + + model->randomize(); + listview->forceLayout(); + QTest::qWait(5); // give view a chance to update + } +} + void tst_QQuickListView::QTBUG_39492_data() { QStandardItemModel *sourceModel = new QStandardItemModel(this); diff --git a/tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml b/tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml new file mode 100644 index 0000000000..f02ab35faf --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml @@ -0,0 +1,45 @@ +import QtQuick 2.3 + +PathView { + id: view + objectName: "pathView" + width: 100 + height: delegateHeight * pathItemCount + model: ["A", "B", "C"] + pathItemCount: 3 + anchors.centerIn: parent + + property int delegateHeight: 0 + + activeFocusOnTab: true + Keys.onDownPressed: view.incrementCurrentIndex() + Keys.onUpPressed: view.decrementCurrentIndex() + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + + delegate: Rectangle { + objectName: "delegate" + modelData + width: view.width + height: textItem.height + border.color: "red" + + onHeightChanged: { + if (index == 0) + view.delegateHeight = textItem.height + } + + Text { + id: textItem + text: modelData + } + } + + path: Path { + startX: view.width / 2 + startY: 0 + PathLine { + x: view.width / 2 + y: view.pathItemCount * view.delegateHeight + } + } +} diff --git a/tests/auto/quick/qquickpathview/data/incorrectSteal.qml b/tests/auto/quick/qquickpathview/data/incorrectSteal.qml new file mode 100644 index 0000000000..bcd6923b73 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/incorrectSteal.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Flickable { + objectName: "flickable" + width: 400; height: 400 + + contentHeight: height + contentWidth: width * 3 + contentX: 400 + + Row { + Rectangle { width: 400; height: 400; color: "green" } + Rectangle { + width: 400; height: 400; color: "blue" + clip: true + + PathView { + id: pathView + objectName: "pathView" + width: parent.width + height: 200 + anchors.verticalCenter: parent.verticalCenter + + dragMargin: 400 + pathItemCount: 6 + + model: 10 + path: Path { + startX: -pathView.width / 2 + startY: pathView.height / 2 + PathLine { x: pathView.width + pathView.width / 2; y: pathView.height / 2 } + } + + delegate: Rectangle { + width: 100; height: 200 + color: "purple" + MouseArea { + anchors.fill: parent + } + } + } + } + Rectangle { width: 400; height: 400; color: "yellow" } + } +} diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 579cb954aa..1960775ad3 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -47,6 +47,7 @@ #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> #include <QtQuick/private/qquickpathview_p.h> +#include <QtQuick/private/qquickflickable_p.h> #include <QtQuick/private/qquickpath_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickrectangle_p.h> @@ -142,6 +143,8 @@ private slots: void indexAt_itemAt(); void indexAt_itemAt_data(); void cacheItemCount(); + void incorrectSteal(); + void changePathDuringRefill(); }; class TestObject : public QObject @@ -2120,7 +2123,102 @@ void tst_QQuickPathView::cacheItemCount() bool b = true; controller.incubateWhile(&b); } +} + +static void testCurrentIndexChange(QQuickPathView *pathView, const QStringList &objectNamesInOrder) +{ + for (int visualIndex = 0; visualIndex < objectNamesInOrder.size() - 1; ++visualIndex) { + QQuickRectangle *delegate = findItem<QQuickRectangle>(pathView, objectNamesInOrder.at(visualIndex)); + QVERIFY(delegate); + + QQuickRectangle *nextDelegate = findItem<QQuickRectangle>(pathView, objectNamesInOrder.at(visualIndex + 1)); + QVERIFY(nextDelegate); + + QVERIFY(delegate->y() < nextDelegate->y()); + } +} + +void tst_QQuickPathView::changePathDuringRefill() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("changePathDuringRefill.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); + + QQuickPathView *pathView = qobject_cast<QQuickPathView*>(window->rootObject()); + QVERIFY(pathView != 0); + + testCurrentIndexChange(pathView, QStringList() << "delegateC" << "delegateA" << "delegateB"); + + pathView->incrementCurrentIndex(); + /* + Decrementing moves delegateA down, resulting in an offset of 1, + so incrementing will move it up, resulting in an offset of 2: + + delegateC delegateA + delegateA => delegateB + delegateB delegateC + */ + QTRY_COMPARE(pathView->offset(), 2.0); + testCurrentIndexChange(pathView, QStringList() << "delegateA" << "delegateB" << "delegateC"); +} + +void tst_QQuickPathView::incorrectSteal() +{ + QScopedPointer<QQuickView> window(createView()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->setSource(testFileUrl("incorrectSteal.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); + + QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "pathView"); + QVERIFY(pathview != 0); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); + QVERIFY(flickable != 0); + + QSignalSpy movingSpy(pathview, SIGNAL(movingChanged())); + QSignalSpy moveStartedSpy(pathview, SIGNAL(movementStarted())); + QSignalSpy moveEndedSpy(pathview, SIGNAL(movementEnded())); + + QSignalSpy fflickingSpy(flickable, SIGNAL(flickingChanged())); + QSignalSpy fflickStartedSpy(flickable, SIGNAL(flickStarted())); + QSignalSpy fflickEndedSpy(flickable, SIGNAL(flickEnded())); + + int waitInterval = 5; + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(23,218)); + + QTest::mouseMove(window.data(), QPoint(25,218), waitInterval); + QTest::mouseMove(window.data(), QPoint(26,218), waitInterval); + QTest::mouseMove(window.data(), QPoint(28,219), waitInterval); + QTest::mouseMove(window.data(), QPoint(31,219), waitInterval); + QTest::mouseMove(window.data(), QPoint(39,219), waitInterval); + + // first move beyond threshold does not trigger drag + QVERIFY(!pathview->isMoving()); + QVERIFY(!pathview->isDragging()); + QCOMPARE(movingSpy.count(), 0); + QCOMPARE(moveStartedSpy.count(), 0); + QCOMPARE(moveEndedSpy.count(), 0); + QCOMPARE(fflickingSpy.count(), 0); + QCOMPARE(fflickStartedSpy.count(), 0); + QCOMPARE(fflickEndedSpy.count(), 0); + + // no further moves after the initial move beyond threshold + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(53,219)); + QTRY_COMPARE(movingSpy.count(), 2); + QTRY_COMPARE(moveEndedSpy.count(), 1); + QCOMPARE(moveStartedSpy.count(), 1); + // Flickable should not handle this + QEXPECT_FAIL("", "QTBUG-37859", Abort); + QCOMPARE(fflickingSpy.count(), 0); + QCOMPARE(fflickStartedSpy.count(), 0); + QCOMPARE(fflickEndedSpy.count(), 0); } QTEST_MAIN(tst_QQuickPathView) diff --git a/tests/auto/quick/qquickwindow/data/unloadSubWindow.qml b/tests/auto/quick/qquickwindow/data/unloadSubWindow.qml new file mode 100644 index 0000000000..bf9df4867d --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/unloadSubWindow.qml @@ -0,0 +1,22 @@ +import QtQuick 2.3 +import QtQuick.Window 2.2 + +Window { + id: root + property var transientWindow + property Loader loader1: Loader { + sourceComponent: Item { + Loader { + id: loader2 + sourceComponent : Window { + id: inner + visible: true + Component.onCompleted: root.transientWindow = inner + } + } + Component.onDestruction: { + loader2.active = false; + } + } + } +} diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index b05146fa3a..b8f9102775 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -47,6 +47,7 @@ #include <QtQml/QQmlEngine> #include <QtQml/QQmlComponent> #include <QtQuick/private/qquickrectangle_p.h> +#include <QtQuick/private/qquickloader_p.h> #include "../../shared/util.h" #include "../shared/visualtestutil.h" #include <QSignalSpy> @@ -347,6 +348,8 @@ private slots: void crashWhenHoverItemDeleted(); + void unloadSubWindow(); + void qobjectEventFilter_touch(); void qobjectEventFilter_key(); void qobjectEventFilter_mouse(); @@ -1705,6 +1708,26 @@ void tst_qquickwindow::crashWhenHoverItemDeleted() } } +// QTBUG-33436 +void tst_qquickwindow::unloadSubWindow() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("unloadSubWindow.qml")); + QQuickWindow *window = qobject_cast<QQuickWindow *>(component.create()); + QVERIFY(window); + window->show(); + QTest::qWaitForWindowExposed(window); + QQuickWindow *transient = Q_NULLPTR; + QTRY_VERIFY(transient = window->property("transientWindow").value<QQuickWindow*>()); + QTest::qWaitForWindowExposed(transient); + + // Unload the inner window (in nested Loaders) and make sure it doesn't crash + QQuickLoader *loader = window->property("loader1").value<QQuickLoader*>(); + loader->setActive(false); + QTRY_VERIFY(!transient->isVisible()); +} + // QTBUG-32004 void tst_qquickwindow::qobjectEventFilter_touch() { |