From 99f338f27c48530468277fb5817ac9e185368154 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 17 Jan 2018 16:46:39 +0100 Subject: Add README for tests/auto/bic Task-number: QTBUG-65827 Change-Id: I0c0b960066377acea5de2e45ac21675aab223c64 Reviewed-by: Liang Qi Reviewed-by: Frederik Gladhorn --- tests/auto/bic/README | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/auto/bic/README (limited to 'tests') diff --git a/tests/auto/bic/README b/tests/auto/bic/README new file mode 100644 index 0000000000..2bde3dd9b9 --- /dev/null +++ b/tests/auto/bic/README @@ -0,0 +1,13 @@ +This directory contains test data to be used by tst_bic to check that we don't +introduce binary incompatible changes between releases. The bic test is run +after every successful integration on Linux only. This data is generated after +each minor release. The generated BC files contain virtual tables, and the bic +test checks new content against the old data. + +See the following README for an explanation of how to generate this data: + +http://code.qt.io/cgit/qt/qtqa.git/tree/tests/postbuild/bic/README + +The test itself can be found here: + +http://code.qt.io/cgit/qt/qtqa.git/tree/tests/postbuild/bic -- cgit v1.2.3 From eec58534ab9c3fae74a1b4cb0861d4b40253cd2d Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 10 Jan 2018 13:51:23 +0100 Subject: Fix segfault when alias target refers to lowercase-named type Create an error via QQmlCompileError and return it instead of asserting. Task-number: QTBUG-43567 Change-Id: I0c0741943d30516379eff5f44ed8618a0f0116a4 Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt | 1 + tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml | 10 ++++++++++ tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml | 5 +++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 1 + 4 files changed, 17 insertions(+) create mode 100644 tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt create mode 100644 tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml create mode 100644 tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml (limited to 'tests') diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt new file mode 100644 index 0000000000..234753ad59 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt @@ -0,0 +1 @@ +6:5:Invalid alias target diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml new file mode 100644 index 0000000000..4050c0a7ad --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 + +QtObject { + property alias dataValue: dataVal + + invalidAliasComponent { + id: dataVal + strValue: "value2" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml b/tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml new file mode 100644 index 0000000000..a45b1806a3 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string strValue: "value1" +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 1af57f9247..803bf0c468 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -513,6 +513,7 @@ void tst_qqmllanguage::errors_data() QTest::newRow("invalidAlias.10") << "invalidAlias.10.qml" << "invalidAlias.10.errors.txt" << false; QTest::newRow("invalidAlias.11") << "invalidAlias.11.qml" << "invalidAlias.11.errors.txt" << false; QTest::newRow("invalidAlias.12") << "invalidAlias.12.qml" << "invalidAlias.12.errors.txt" << false; + QTest::newRow("invalidAlias.13") << "invalidAlias.13.qml" << "invalidAlias.13.errors.txt" << false; QTest::newRow("invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml" << "invalidAttachedProperty.1.errors.txt" << false; QTest::newRow("invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml" << "invalidAttachedProperty.2.errors.txt" << false; -- cgit v1.2.3 From f42f1366dcd4b070c69c9e7fe42a704ef23da14d Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 13 Jan 2017 12:45:08 +0100 Subject: Use the image handler to return a scaled size for the SVG When the SVG was made to be drawn at a small width and a large height then it would lead to blurring in the rendering of the SVG. By having the image handler deal with returning the image we want for the scaled size it will ensure this is rendered correctly since it is able to account for the ratio already inside the SVG image handler. Task-number: QTBUG-65789 Change-Id: Ib8627c0537679aab022e88a0eea73a21d8cbc564 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickimage/tst_qquickimage.cpp | 4 +- tests/manual/imagehandler/large.svg | 462 +++++++++++++++++++++++ tests/manual/imagehandler/main.qml | 96 +++++ 3 files changed, 560 insertions(+), 2 deletions(-) create mode 100644 tests/manual/imagehandler/large.svg create mode 100644 tests/manual/imagehandler/main.qml (limited to 'tests') diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index a2a65aa803..f6720f96e3 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -400,11 +400,11 @@ void tst_qquickimage::svg() component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickImage *obj = qobject_cast(component.create()); QVERIFY(obj != 0); - QCOMPARE(int(obj->width()), 212); // round down: highdpi can give back fractional values + QCOMPARE(obj->width(), 300.0); QCOMPARE(obj->height(), 300.0); obj->setSourceSize(QSize(200,200)); - QCOMPARE(int(obj->width()), 141); // round down: highdpi can give back fractional values + QCOMPARE(obj->width(), 200.0); QCOMPARE(obj->height(), 200.0); delete obj; } diff --git a/tests/manual/imagehandler/large.svg b/tests/manual/imagehandler/large.svg new file mode 100644 index 0000000000..99b3b67616 --- /dev/null +++ b/tests/manual/imagehandler/large.svg @@ -0,0 +1,462 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/manual/imagehandler/main.qml b/tests/manual/imagehandler/main.qml new file mode 100644 index 0000000000..55e5b89cae --- /dev/null +++ b/tests/manual/imagehandler/main.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Window 2.0 + +Window { + id: root + width: 480 + height: 480 + visible: true + Image { + id: svgImage + source: "large.svg" + height: parent.height - column.height + width: parent.width + sourceSize.height: height + sourceSize.width: width + } + ListModel { + id: imageFillModeModel + ListElement { text: "Stretch"; fillMode: Image.Stretch } + ListElement { text: "Preserve Aspect Fit"; fillMode: Image.PreserveAspectFit } + ListElement { text: "Preserve Aspect Crop"; fillMode: Image.PreserveAspectCrop } + ListElement { text: "Tile"; fillMode: Image.Tile } + ListElement { text: "Tile Vertically"; fillMode: Image.TileVertically } + ListElement { text: "Tile Horizontally"; fillMode: Image.TileHorizontally } + ListElement { text: "Pad"; fillMode: Image.Pad } + } + Column { + id: column + height: 75 + anchors.bottom: parent.bottom + Text { + text: "Click the options below to change the fill mode" + font.pointSize: 16 + } + + Row { + id: checkBoxesRow + width: parent.width + Repeater { + model: imageFillModeModel + Rectangle { + color: "lightGreen" + height: 50 + width: 100 + Text { + text: model.text + wrapMode: Text.Wrap + font.pointSize: 16 + anchors.fill: parent + MouseArea { + anchors.fill: parent + onClicked: { + svgImage.fillMode = model.fillMode + if (svgImage.fillMode === Image.Tile || svgImage.fillMode === Image.TileHorizontally || + svgImage.fillMode === Image.TileVertically) + svgImage.sourceSize.height = svgImage.sourceSize.width = 300 + else { + svgImage.sourceSize.height = svgImage.height + svgImage.sourceSize.width = svgImage.width + } + } + } + } + } + } + } + } +} -- cgit v1.2.3 From 406ef45aaa3e84eb402a451eb4900afa17d20ea9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 30 Jan 2018 09:47:06 +0100 Subject: Fix exposure of -1 as enum value in QML exposed C++ singletons When a C++ singleton has an enum with the value -1, we would expose that value correctly when taking the accelerated property access code path in the optimizer, but when going through the slower QQmlTypeWrapper we would return undefined. This turned out to be a silly logic error that assumed that -1 is not a valid value for an enum and instead indicates an enum value not present. [ChangeLog][Qml] Fix -1 as enum value in QML exposed C++ singletons showing up as undefined. Task-number: QTBUG-66067 Change-Id: Ib66dad7a4b59822b2c40ad6bd9af4b72469582e9 Reviewed-by: Lars Knoll Reviewed-by: Michael Brasser --- tests/auto/qml/qqmlecmascript/testtypes.h | 3 ++- tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index e15a05a00c..ec20714c51 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1670,7 +1670,8 @@ class SingletonWithEnum : public QObject Q_ENUMS(TestEnum) public: enum TestEnum { - TestValue = 42 + TestValue = 42, + TestValue_MinusOne = -1 }; }; diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 14c2aa18bf..db7ec87c02 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7914,6 +7914,15 @@ void tst_qqmlecmascript::singletonWithEnum() QVariant prop = obj->property("testValue"); QCOMPARE(prop.type(), QVariant::Int); QCOMPARE(prop.toInt(), int(SingletonWithEnum::TestValue)); + + { + QQmlExpression expr(qmlContext(obj.data()), obj.data(), "SingletonWithEnum.TestValue_MinusOne"); + bool valueUndefined = false; + QVariant result = expr.evaluate(&valueUndefined); + QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString())); + QVERIFY(!valueUndefined); + QCOMPARE(result.toInt(), -1); + } } void tst_qqmlecmascript::lazyBindingEvaluation() -- cgit v1.2.3 From e447285925623c40f5ddce65fa20e16333c75de0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 25 Jan 2018 17:21:30 +0100 Subject: Blacklist QTBUG_34576_velocityZero ListView test on macOS 10.11 Task-number: QTBUG-65964 Change-Id: Ie1ba02a38cbd019077c7a89ed310d79ec28b6bec Reviewed-by: Shawn Rutledge (cherry-picked from f6222f825831202c084835412a3c217a9420cad7) --- tests/auto/quick/qquicklistview/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tests') diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index d259c11219..8cf8a57eee 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -5,3 +5,6 @@ osx #QTBUG-53863 [populateTransitions] opensuse-42.1 +#QTBUG-65964 +[QTBUG_34576_velocityZero] +osx-10.11 ci -- cgit v1.2.3 From 6a7c662bd5f7fe4a223aba2e15bb24a9ffc92df6 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Thu, 22 Jun 2017 10:12:56 +0200 Subject: Do not stop delivering to handlers if all points are accepted Some Pointer Handlers can perform the desired interaction using only passive grabs. When such a handler is used to modify behavior of another event-handling Item or Handler which needs to take the exclusive grab, this allows them to cooperate: both can see the updates, and neither prevents delivery of events to both. Change-Id: I312cc301c52fcdf805245bbe0ac60fd28f92c01f Reviewed-by: Shawn Rutledge --- .../data/simpleTapAndDragHandlers.qml | 112 +++++++++++++++++++++ .../qquickdraghandler/qquickdraghandler.pro | 8 +- .../qquickdraghandler/tst_qquickdraghandler.cpp | 85 +++++++++++++++- .../tst_qquickpointerhandler.cpp | 15 +-- 4 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml (limited to 'tests') diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml new file mode 100644 index 0000000000..adb8332213 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/simpleTapAndDragHandlers.qml @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import Qt.labs.handlers 1.0 + +Rectangle { + id: root + width: 900 + height: 850 + objectName: "root" + color: "#222222" + + Row { + objectName: "row" + anchors.fill: parent + spacing: 10 + Rectangle { + width: 50 + height: 50 + color: "aqua" + objectName: "dragAndTap" + DragHandler { + objectName: "drag" + } + TapHandler { + objectName: "tap" + gesturePolicy: TapHandler.DragThreshold + } + } + Rectangle { + width: 50 + height: 50 + color: "aqua" + objectName: "tapAndDrag" + TapHandler { + objectName: "tap" + gesturePolicy: TapHandler.DragThreshold + } + DragHandler { + objectName: "drag" + } + } + + Rectangle { + color: "aqua" + width: 50 + height: 50 + objectName: "dragAndTapNotSiblings" + DragHandler { + objectName: "drag" + } + Rectangle { + color: "blue" + width: 30 + height: 30 + anchors.centerIn: parent + TapHandler { + objectName: "tap" + gesturePolicy: TapHandler.DragThreshold + } + } + } + Rectangle { + color: "aqua" + width: 50 + height: 50 + objectName: "tapAndDragNotSiblings" + TapHandler { + objectName: "tap" + gesturePolicy: TapHandler.DragThreshold + } + Rectangle { + color: "blue" + x: 10 + y: 10 + width: 30 + height: 30 + DragHandler { + objectName: "drag" + } + } + } + + + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro index b50fe5ca6f..42c4e46c4f 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro @@ -12,4 +12,10 @@ include (../../shared/util.pri) TESTDATA = data/* -# OTHER_FILES += data/foo.qml +OTHER_FILES += data/DragAnywhereSlider.qml \ + data/FlashAnimation.qml \ + data/Slider.qml \ + data/draggables.qml \ + data/grabberstate.qml \ + data/multipleSliders.qml \ + data/reparenting.qml \ diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index 8dc035949e..f827b82205 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -58,9 +58,12 @@ private slots: void touchDragMulti(); void touchDragMultiSliders_data(); void touchDragMultiSliders(); + void touchPassiveGrabbers_data(); + void touchPassiveGrabbers(); private: void createView(QScopedPointer &window, const char *fileName); + QSet passiveGrabbers(QQuickWindow *window, int pointId = 0); QTouchDevice *touchDevice; }; @@ -77,6 +80,24 @@ void tst_DragHandler::createView(QScopedPointer &window, const char QVERIFY(window->rootObject() != 0); } +QSet tst_DragHandler::passiveGrabbers(QQuickWindow *window, int pointId /*= 0*/) +{ + QSet result; + QQuickWindowPrivate *winp = QQuickWindowPrivate::get(window); + if (QQuickPointerDevice* device = QQuickPointerDevice::touchDevice(touchDevice)) { + QQuickPointerEvent *pointerEvent = winp->pointerEventInstance(device); + for (int i = 0; i < pointerEvent->pointCount(); ++i) { + QQuickEventPoint *eventPoint = pointerEvent->point(i); + QVector > passives = eventPoint->passiveGrabbers(); + if (!pointId || eventPoint->pointId() == pointId) { + for (auto it = passives.constBegin(); it != passives.constEnd(); ++it) + result << it->data(); + } + } + } + return result; +} + void tst_DragHandler::initTestCase() { // This test assumes that we don't get synthesized mouse events from QGuiApplication @@ -398,6 +419,68 @@ void tst_DragHandler::touchDragMultiSliders() touch.commit(); } +void tst_DragHandler::touchPassiveGrabbers_data() +{ + QTest::addColumn("itemName"); + QTest::addColumn("expectedPassiveGrabberNames"); + + QTest::newRow("Drag And Tap") << "dragAndTap" << QStringList({"drag", "tap"}); + QTest::newRow("Tap And Drag") << "tapAndDrag" << QStringList({"tap", "drag"}); + QTest::newRow("Drag And Tap (not siblings)") << "dragAndTapNotSiblings" << QStringList({"drag", "tap"}); + QTest::newRow("Tap And Drag (not siblings)") << "tapAndDragNotSiblings" << QStringList({"tap", "drag"}); +} + +void tst_DragHandler::touchPassiveGrabbers() +{ + QFETCH(QString, itemName); + QFETCH(QStringList, expectedPassiveGrabberNames); + + QScopedPointer windowPtr; + createView(windowPtr, "simpleTapAndDragHandlers.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *row2 = window->rootObject()->findChild(itemName); + QSet expectedPassiveGrabbers; + for (QString objectName : expectedPassiveGrabberNames) + expectedPassiveGrabbers << row2->findChild(objectName); + + QPointF p1 = row2->mapToScene(row2->clipRect().center()); + QTest::QTouchEventSequence touch = QTest::touchEvent(window, touchDevice); + touch.press(1, p1.toPoint()).commit(); + QQuickTouchUtils::flush(window); + + QCOMPARE(passiveGrabbers(window), expectedPassiveGrabbers); + + QQuickDragHandler *dragHandler = nullptr; + for (QQuickPointerHandler *handler: expectedPassiveGrabbers) { + QCOMPARE(static_cast(handler)->point().scenePressPosition(), p1); + QQuickDragHandler *dh = qmlobject_cast(handler); + if (dh) + dragHandler = dh; + } + QVERIFY(dragHandler); + QPointF initialPos = dragHandler->target()->position(); + + p1 += QPointF(50, 50); + touch.move(1, p1.toPoint()).commit(); + QQuickTouchUtils::flush(window); + QTRY_VERIFY(dragHandler->active()); + + p1 += QPointF(50, 50); + touch.move(1, p1.toPoint()).commit(); + QQuickTouchUtils::flush(window); + QPointF movementDelta = dragHandler->target()->position() - initialPos; + qCDebug(lcPointerTests) << "DragHandler moved the target by" << movementDelta; + QVERIFY(movementDelta.x() >= 100); + QVERIFY(movementDelta.y() >= 100); + + QTest::qWait(500); + + touch.release(1, p1.toPoint()); + touch.commit(); + QQuickTouchUtils::flush(window); +} + QTEST_MAIN(tst_DragHandler) #include "tst_qquickdraghandler.moc" diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index cc39dd54f7..d38ae3190e 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -504,17 +504,18 @@ void tst_PointerHandlers::mouseEventDelivery() eventItem1->grabPointer = true; p1 = QPoint(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(eventItem1->eventList.size(), 2); + QTRY_COMPARE(eventItem1->eventList.size(), 3); QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::None, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); QCOMPARE_EVENT(1, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, QQuickEventPoint::GrabExclusive); + QCOMPARE_EVENT(2, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, 0); p1 += QPoint(10, 0); QTest::mouseMove(window, p1); - QCOMPARE(eventItem1->eventList.size(), 3); - QCOMPARE_EVENT(2, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); + QCOMPARE(eventItem1->eventList.size(), 4); + QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointMoved, QQuickEventPoint::GrabExclusive); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QCOMPARE(eventItem1->eventList.size(), 5); - QCOMPARE_EVENT(3, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, QQuickEventPoint::GrabExclusive); - QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::None, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); + QCOMPARE(eventItem1->eventList.size(), 6); + QCOMPARE_EVENT(4, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointReleased, QQuickEventPoint::GrabExclusive); + QCOMPARE_EVENT(5, Event::HandlerDestination, QEvent::None, Qt::TouchPointReleased, QQuickEventPoint::UngrabExclusive); eventItem1->eventList.clear(); } -- cgit v1.2.3 From 6eaa95662c2d4ba287ac5d1de5ec49bd3a9f59e6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 22 Jan 2018 20:00:27 +0100 Subject: Change default TapHandler.gesturePolicy to DragThreshold This is based on the idea that TapHandler may be more often used to modify existing behavior rather than building Button controls from scratch. DragThreshold is reasonable newbie-friendly default behavior for both use cases. The drag-off-drag-back-release-and-click behavior is more advanced, and the designers of the best-behaving Button controls can be expected to discover the need to change gesturePolicy to get it. Change-Id: If220acf080e04f664d020d5e832f8d16a16b857a Reviewed-by: Shawn Rutledge --- .../pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml index 14a8d67300..219d4a70a8 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/buttonOverrideHandler.qml @@ -38,6 +38,7 @@ Item { label: "Overridden" x: 10; y: 10; width: parent.width - 20; height: 40 TapHandler { + gesturePolicy: TapHandler.ReleaseWithinBounds objectName: "override" onTapped: button.tapped() } -- cgit v1.2.3 From 1e350a8c98d9c98823dde83a6745d2f26a9c0785 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 24 Jan 2018 17:23:03 +0100 Subject: Disallow registration of types beginning with lowercase letters Allowing types with lowercase names causes ambiguity, as can be seen in QTBUG-43567 and the comment in IRBuilder::visit(), which explains that "the grammar can't distinguish between two different definitions" whose only difference is casing of the first letter. - Prevent registration (return -1 with e.g. qmlRegisterType()) when a type name doesn't begin with an uppercase letter. - Document the uppercase type name rule in more places. Change-Id: I4e522c65990f418eaafa45a256e3cb07a3e01ba4 Reviewed-by: Shawn Rutledge --- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests') diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 803bf0c468..83151fb6e2 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -271,6 +271,8 @@ private slots: void accessDeletedObject(); + void lowercaseTypeNames(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -4596,6 +4598,12 @@ void tst_qqmllanguage::accessDeletedObject() QVERIFY(!o.isNull()); } +void tst_qqmllanguage::lowercaseTypeNames() +{ + QCOMPARE(qmlRegisterType("Test", 1, 0, "lowerCaseTypeName"), -1); + QCOMPARE(qmlRegisterSingletonType("Test", 1, 0, "lowerCaseTypeName", nullptr), -1); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3 From 5a10cf060e6c843f05d8bd820a4be4bb08f277ec Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 21 Nov 2017 13:05:45 +0100 Subject: Prevent invalid characters being entered at the appropriate times When a validator does not allow for certain characters to be entered, then it should not allow these to be entered in even if an input mask is set. This fixes a regression introduced in 1b21b73e89942d567c90a17a3bf7a7ecae3de258. The test modified is because this is in fact a general limitation when combining validators and input masks, when a separator is used. Whereas the original patch did allow this to be possible, this is now not possible again. Task-number: QTBUG-64616 Change-Id: Ic6a3f40a9faa7c04abc055cfc2752044fddd33a0 Reviewed-by: Frederik Gladhorn Reviewed-by: Robin Burchell --- tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 9b526f80c5..5b2c585a92 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -6143,9 +6143,17 @@ void tst_qquicktextinput::keypress_inputMask_withValidator_data() KeyList keys; // inserting '1111.11' then two backspaces keys << Qt::Key_Home << "1111.11" << Qt::Key_Backspace << Qt::Key_Backspace; - QTest::newRow("backspaceWithRegExp") << QString("9999.99;_") << 0.0 << 0.0 << 0 + QTest::newRow("backspaceWithRegExp") << QString("9999;_") << 0.0 << 0.0 << 0 << QString("/^[-]?((\\.\\d+)|(\\d+(\\.\\d+)?))$/") - << keys << QString("1111.") << QString("1111.__"); + << keys << QString("11") << QString("11__"); + } + { + KeyList keys; + // inserting '99' - QTBUG-64616 + keys << Qt::Key_Home << "99"; + QTest::newRow("invalidTextWithRegExp") << QString("X9;_") << 0.0 << 0.0 << 0 + << QString("/[+-][0+9]/") + << keys << QString("") << QString("__"); } } -- cgit v1.2.3 From 8c0501855787986365519da12e9e580b30fb26af Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 29 Jan 2018 11:42:40 +0100 Subject: Fix dead lock / race in QML type loader when importing plugins When importing modules - in the QML loader thread - with plugins we keep globally track of the Qt plugins that we have loaded that contain QML modules, to ensure that we don't call the engine-independent registerTypes() function on the plugin multiple times. After registerTypes() we may also call initializeEngine() on the plugin for the engine-specific initialization, which - as a QQmlEngine is provided as parameter - must happen in the gui thread. For that we issue a thread-blocking call that waits until the gui thread has woken up and processed the event/call. During that time the global plugin lock is held by that QML loader thread. If meanwhile the gui thread instantiates a second QQmlEngine and attempts to issue a synchronous type compilation (using QQmlComponent::CompilationMode::PreferSynchronous), then gui thread is blocking and waiting for its own QML loader thread to complete the type compilation, which may involve processing an import that requires loading a plugin. Now this second QML loader thread is blocked by trying to acquire the global plugin registry lock (qmlEnginePluginsWithRegisteredTypes()->mutex) in qqmlimports.cpp. Now the first QML loader thread is blocked because the gui thread is not processing the call events for the first engine. The gui thread is blocked waiting for the second QML loader thread, which in turn is stuck trying to acquire the lock held by the first QML loader thread. The provided test case triggers this scenario, although through a slightly different way. It's not possible to wait in the gui thread for the plugin lock to be held in a loader thread via the registerTypes callback, as that also acquires the QQmlMetaType lock that will interfere with the test-case. However the same plugin lock issue appears when the first QML engine is located in a different thread altogether. In that case the dispatch to the engine thread /works/, but it won't be the gui thread but instead the secondary helper thread of the test case that will sit in our initializeEngine() callback. This bug was spotted in production customer code with backtraces pointing into the three locations described above: One QML loader thread blocking on a call to the gui thread, the gui thread blocking on a second QML loader thread and that one blocking on acquisition of the plugin lock held by the first. Fortunately it is not necessary to hold on to the global plugin lock when doing the engine specific initialization. That allows the second QML loader thread to complete its work and finally resume the GUI thread's event loop. Change-Id: If757b3fc9b473f42b266427e55d7a1572b937515 Reviewed-by: Ulf Hermann --- .../qqmlmoduleplugin/moduleWithStaticPlugin/qmldir | 2 + .../moduleWithWaitingPlugin/qmldir | 2 + .../qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp | 119 +++++++++++++++++++++ .../qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro | 8 ++ 4 files changed, 131 insertions(+) create mode 100644 tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir create mode 100644 tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir (limited to 'tests') diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir new file mode 100644 index 0000000000..104c4bf673 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir @@ -0,0 +1,2 @@ +module moduleWithStaticPlugin +plugin secondStaticPlugin diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir new file mode 100644 index 0000000000..45a02b2ffe --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir @@ -0,0 +1,2 @@ +module moduleWithWaitingPlugin +plugin pluginThatWaits diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index 8600e1e8ab..9abff7b2f6 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include #if defined(Q_OS_MAC) @@ -73,12 +76,80 @@ private slots: void importsChildPlugin(); void importsChildPlugin2(); void importsChildPlugin21(); + void parallelPluginImport(); private: QString m_importsDirectory; QString m_dataImportsDirectory; }; +class PluginThatWaits : public QQmlExtensionPlugin +{ +public: + static QByteArray metaData; + + static QMutex initializeEngineEntered; + static QWaitCondition waitingForInitializeEngineEntry; + static QMutex leavingInitializeEngine; + static QWaitCondition waitingForInitializeEngineLeave; + + void registerTypes(const char *uri) override + { + qmlRegisterModule(uri, 1, 0); + } + + void initializeEngine(QQmlEngine *engine, const char *uri) override + { + initializeEngineEntered.lock(); + leavingInitializeEngine.lock(); + waitingForInitializeEngineEntry.wakeOne(); + initializeEngineEntered.unlock(); + waitingForInitializeEngineLeave.wait(&leavingInitializeEngine); + leavingInitializeEngine.unlock(); + } +}; +QByteArray PluginThatWaits::metaData; +QMutex PluginThatWaits::initializeEngineEntered; +QWaitCondition PluginThatWaits::waitingForInitializeEngineEntry; +QMutex PluginThatWaits::leavingInitializeEngine; +QWaitCondition PluginThatWaits::waitingForInitializeEngineLeave; + +class SecondStaticPlugin : public QQmlExtensionPlugin +{ +public: + static QByteArray metaData; + + void registerTypes(const char *uri) override + { + qmlRegisterModule(uri, 1, 0); + } +}; +QByteArray SecondStaticPlugin::metaData; + +template +void registerStaticPlugin(const char *uri) +{ + QStaticPlugin plugin; + plugin.instance = []() { + static PluginType plugin; + return static_cast(&plugin); + }; + + QJsonObject md; + md.insert(QStringLiteral("IID"), QQmlExtensionInterface_iid); + QJsonArray uris; + uris.append(uri); + md.insert(QStringLiteral("uri"), uris); + + PluginType::metaData.append(QLatin1String("QTMETADATA ")); + PluginType::metaData.append(QJsonDocument(md).toBinaryData()); + + plugin.rawMetaData = []() { + return PluginType::metaData.constData(); + }; + qRegisterStaticPluginFunction(plugin); +}; + void tst_qqmlmoduleplugin::initTestCase() { QQmlDataTest::initTestCase(); @@ -88,6 +159,9 @@ void tst_qqmlmoduleplugin::initTestCase() m_dataImportsDirectory = directory() + QStringLiteral("/imports"); QVERIFY2(QFileInfo(m_dataImportsDirectory).isDir(), qPrintable(QString::fromLatin1("Imports directory '%1' does not exist.").arg(m_dataImportsDirectory))); + + registerStaticPlugin("moduleWithWaitingPlugin"); + registerStaticPlugin("moduleWithStaticPlugin"); } #define VERIFY_ERRORS(errorfile) \ @@ -635,6 +709,51 @@ void tst_qqmlmoduleplugin::importsChildPlugin21() delete object; } +void tst_qqmlmoduleplugin::parallelPluginImport() +{ + QMutexLocker locker(&PluginThatWaits::initializeEngineEntered); + + QThread worker; + QObject::connect(&worker, &QThread::started, [&worker](){ + // Engines in separate threads are tricky, but as long as we do not create a graphical + // object and move objects created by the engines across thread boundaries, this is safe. + // At the same time this allows us to place the engine's loader thread into the position + // where, without the fix for this bug, the global lock is acquired. + QQmlEngine engineInThread; + + QQmlComponent component(&engineInThread); + component.setData("import moduleWithWaitingPlugin 1.0\nimport QtQml 2.0\nQtObject {}", + QUrl()); + + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + worker.quit(); + }); + worker.start(); + + PluginThatWaits::waitingForInitializeEngineEntry.wait(&PluginThatWaits::initializeEngineEntered); + + { + // After acquiring this lock, the engine in the other thread as well as its type loader + // thread are blocked. However they should not hold the global plugin lock + // qmlEnginePluginsWithRegisteredTypes()->mutex in qqmllimports.cpp, allowing for the load + // of a component in a different engine with its own plugin to proceed. + QMutexLocker continuationLock(&PluginThatWaits::leavingInitializeEngine); + + QQmlEngine secondEngine; + QQmlComponent secondComponent(&secondEngine); + secondComponent.setData("import moduleWithStaticPlugin 1.0\nimport QtQml 2.0\nQtObject {}", + QUrl()); + QScopedPointer o(secondComponent.create()); + QVERIFY(!o.isNull()); + + PluginThatWaits::waitingForInitializeEngineLeave.wakeOne(); + } + + worker.wait(); +} + QTEST_MAIN(tst_qqmlmoduleplugin) #include "tst_qqmlmoduleplugin.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro index 43bd112415..118ca26ee9 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro @@ -10,4 +10,12 @@ include (../../shared/util.pri) TESTDATA = data/* imports/* $$OUT_PWD/imports/* +waitingPlugin.files = moduleWithWaitingPlugin +waitingPlugin.prefix = /qt-project.org/imports/ +RESOURCES += waitingPlugin + +staticPlugin.files = moduleWithStaticPlugin +staticPlugin.prefix = /qt-project.org/imports/ +RESOURCES += staticPlugin + QT += core-private gui-private qml-private network testlib -- cgit v1.2.3 From 7bd5d93899ca6c2175d6937f2011428c654bff02 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2018 15:41:58 +0100 Subject: Fix memory leak with QtQuick compiler generated files When for the QQC code path we do QML type re-compilation, we allocate a new QV4::CompiledData::Unit. We must make sure that this dynamically allocated memory is released in QV4::CompiledData::CompilationUnit's destructor, by ensuring that the StaticData flag is not set. This isn't directly applicable to the ahead-of-time generated cache file unit data as they will always be re-generated (and thus the unsetting of StaticData at the end of createCompilationUnit::createUnitData()), but I've added a test-case nevertheless to ensure the correct engine behavior. Change-Id: I16973d7989567892bf8bf9dd6214bf293055d260 Reviewed-by: Lars Knoll --- tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'tests') diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index b69071dd59..98a3a9d6ef 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -33,6 +33,7 @@ #include #include #include +#include class tst_qmlcachegen: public QObject { @@ -114,6 +115,16 @@ void tst_qmlcachegen::loadGeneratedFile() const QString cacheFilePath = testFilePath + QLatin1Char('c'); QVERIFY(QFile::exists(cacheFilePath)); + + { + QFile cache(cacheFilePath); + QVERIFY(cache.open(QIODevice::ReadOnly)); + const QV4::CompiledData::Unit *cacheUnit = reinterpret_cast(cache.map(/*offset*/0, sizeof(QV4::CompiledData::Unit))); + QVERIFY(cacheUnit); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + } + QVERIFY(QFile::remove(testFilePath)); QQmlEngine engine; @@ -121,6 +132,13 @@ void tst_qmlcachegen::loadGeneratedFile() QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); + + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit; + QVERIFY(compilationUnit); + QVERIFY(compilationUnit->data); + QVERIFY(!(compilationUnit->data->flags & QV4::CompiledData::Unit::StaticData)); } void tst_qmlcachegen::translationExpressionSupport() -- cgit v1.2.3 From 4b014effe1f27407f5073ba738498ce87b918b9d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 15 Dec 2017 13:26:28 +0100 Subject: If Loader loads Window, set its transient parent to the Loader's window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the Item { Loader { sourceComponent: Window { } } } case consistent with the Item { Window { } } case: the inner Window is transient for the outer Window. It works even if the Loader's Window has a visible: true declaration: in that case, until now, the Loader's Window would become visible at component creation time, before the outer Item became visible. So the test to check whether it had a transient parent did not work. We now change the delayed-visibility mechanism in QQuickWindowQmlImpl to wait for the parent Item to acquire a window of its own rather than waiting for the transient-parent-if-any to become visible. It should still take care of all the old cases too, e.g. in the Window { Window { } } case, the inner Window's QObject parent is actually the QQuickRootItem. (Amends 701255c76f671f100338a799f0194bf10e26c9d1) [ChangeLog][QtQuick][QQuickWindow] When a Window is declared inside another Item or Window, the window will not be created until the parent window is created. This allows it to have the correct transientParent and be managed as a transient window. Task-number: QTBUG-52944 Change-Id: Iaf4aafbd696f6e8dd0eec1d02db8bd181483bd07 Reviewed-by: Morten Johan Sørvig Reviewed-by: Shawn Rutledge --- .../qquickloader/data/itemLoaderItemWindow.qml | 27 +++++++ .../quick/qquickloader/data/itemLoaderWindow.qml | 22 ++++++ tests/auto/quick/qquickloader/tst_qquickloader.cpp | 86 ++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml create mode 100644 tests/auto/quick/qquickloader/data/itemLoaderWindow.qml (limited to 'tests') diff --git a/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml b/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml new file mode 100644 index 0000000000..d4c5daecab --- /dev/null +++ b/tests/auto/quick/qquickloader/data/itemLoaderItemWindow.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 +import QtQuick.Window 2.1 + +Item { + width: 400 + height: 400 + objectName: "root Item" + + Loader { + sourceComponent: Rectangle { + objectName: "yellow rectangle" + x: 50; y: 50; width: 300; height: 300 + color: "yellow" + Window { + objectName: "red transient Window" + width: 100 + height: 100 + visible: true // makes it harder, because it wants to become visible before root has a window + color: "red" + title: "red" + flags: Qt.Dialog + onVisibilityChanged: console.log("visibility " + visibility) + onVisibleChanged: console.log("visible " + visible) + } + } + } +} diff --git a/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml b/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml new file mode 100644 index 0000000000..69421448e0 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/itemLoaderWindow.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import QtQuick.Window 2.1 + +Item { + width: 400 + height: 400 + objectName: "root Item" + + Loader { + sourceComponent: Window { + objectName: "red transient Window" + width: 100 + height: 100 + visible: true // makes it harder, because it wants to become visible before root has a window + color: "red" + title: "red" + flags: Qt.Dialog + onVisibilityChanged: console.log("visibility " + visibility) + onVisibleChanged: console.log("visible " + visible) + } + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 521388c5fa..582ba2aabc 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -32,11 +32,15 @@ #include #include #include +#include #include +#include #include "testhttpserver.h" #include "../../shared/util.h" #include "../shared/geometrytestutil.h" +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + class SlowComponent : public QQmlComponent { Q_OBJECT @@ -114,6 +118,8 @@ private slots: void parented(); void sizeBound(); void QTBUG_30183(); + void transientWindow(); + void nestedTransientWindow(); void sourceComponentGarbageCollection(); @@ -1207,6 +1213,86 @@ void tst_QQuickLoader::QTBUG_30183() delete loader; } +void tst_QQuickLoader::transientWindow() // QTBUG-52944 +{ + QQuickView view; + view.setSource(testFileUrl("itemLoaderWindow.qml")); + QQuickItem *root = qobject_cast(view.rootObject()); + QVERIFY(root); + QQuickLoader *loader = root->findChild(); + QVERIFY(loader); + QTRY_COMPARE(loader->status(), QQuickLoader::Ready); + QQuickWindowQmlImpl *loadedWindow = qobject_cast(loader->item()); + QVERIFY(loadedWindow); + QCOMPARE(loadedWindow->visibility(), QWindow::Hidden); + + QElapsedTimer timer; + qint64 viewVisibleTime = -1; + qint64 loadedWindowVisibleTime = -1; + connect(&view, &QWindow::visibleChanged, + [&viewVisibleTime, &timer]() { viewVisibleTime = timer.elapsed(); } ); + connect(loadedWindow, &QQuickWindowQmlImpl::visibilityChanged, + [&loadedWindowVisibleTime, &timer]() { loadedWindowVisibleTime = timer.elapsed(); } ); + timer.start(); + view.show(); + + QTest::qWaitForWindowExposed(&view); + QTRY_VERIFY(loadedWindowVisibleTime >= 0); + QVERIFY(viewVisibleTime >= 0); + + // now that we're sure they are both visible, which one became visible first? + qCDebug(lcTests) << "transient Window became visible" << (loadedWindowVisibleTime - viewVisibleTime) << "ms after the root Item"; + QVERIFY((loadedWindowVisibleTime - viewVisibleTime) >= 0); + + QWindowList windows = QGuiApplication::topLevelWindows(); + QTRY_COMPARE(windows.size(), 2); + + // TODO Ideally we would now close the outer window and make sure the transient window closes too. + // It works during manual testing because of QWindowPrivate::maybeQuitOnLastWindowClosed() + // but quitting an autotest doesn't make sense. +} + +void tst_QQuickLoader::nestedTransientWindow() // QTBUG-52944 +{ + QQuickView view; + view.setSource(testFileUrl("itemLoaderItemWindow.qml")); + QQuickItem *root = qobject_cast(view.rootObject()); + QVERIFY(root); + QQuickLoader *loader = root->findChild(); + QVERIFY(loader); + QTRY_COMPARE(loader->status(), QQuickLoader::Ready); + QQuickItem *loadedItem = qobject_cast(loader->item()); + QVERIFY(loadedItem); + QQuickWindowQmlImpl *loadedWindow = loadedItem->findChild(); + QVERIFY(loadedWindow); + QCOMPARE(loadedWindow->visibility(), QWindow::Hidden); + + QElapsedTimer timer; + qint64 viewVisibleTime = -1; + qint64 loadedWindowVisibleTime = -1; + connect(&view, &QWindow::visibleChanged, + [&viewVisibleTime, &timer]() { viewVisibleTime = timer.elapsed(); } ); + connect(loadedWindow, &QQuickWindowQmlImpl::visibilityChanged, + [&loadedWindowVisibleTime, &timer]() { loadedWindowVisibleTime = timer.elapsed(); } ); + timer.start(); + view.show(); + + QTest::qWaitForWindowExposed(&view); + QTRY_VERIFY(loadedWindowVisibleTime >= 0); + QVERIFY(viewVisibleTime >= 0); + + // now that we're sure they are both visible, which one became visible first? + qCDebug(lcTests) << "transient Window became visible" << (loadedWindowVisibleTime - viewVisibleTime) << "ms after the root Item"; + QVERIFY((loadedWindowVisibleTime - viewVisibleTime) >= 0); + + QWindowList windows = QGuiApplication::topLevelWindows(); + QTRY_COMPARE(windows.size(), 2); + + // TODO Ideally we would now close the outer window and make sure the transient window closes too. + // It works during manual testing because of QWindowPrivate::maybeQuitOnLastWindowClosed() + // but quitting an autotest doesn't make sense. +} + void tst_QQuickLoader::sourceComponentGarbageCollection() { QQmlComponent component(&engine, testFileUrl("sourceComponentGarbageCollection.qml")); -- cgit v1.2.3 From b483144d3a2639b64f3fd2d72bc074bb9321211b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 30 Nov 2017 11:36:42 +0100 Subject: add tst_qquickwidget::synthMouseFromTouch test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure events are delivered as expected and that synth-mouse events have the right source(). Task-number: QTBUG-64241 Change-Id: I95a073ce0faea2111c8e1aca42fa44c1b529a6ec Reviewed-by: Jan Arve Sæther --- .../quickwidgets/qquickwidget/tst_qquickwidget.cpp | 119 ++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 6c8d8191a5..7163e3cebf 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include +#include #include #include #include @@ -37,9 +38,77 @@ #include #include #include - +#include #include +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + +class MouseRecordingQQWidget : public QQuickWidget +{ +public: + explicit MouseRecordingQQWidget(QWidget *parent = nullptr) : QQuickWidget(parent) { + setAttribute(Qt::WA_AcceptTouchEvents); + } + +protected: + void mousePressEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + QQuickWidget::mousePressEvent(event); + } + void mouseMoveEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + QQuickWidget::mouseMoveEvent(event); + } + void mouseReleaseEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + QQuickWidget::mouseReleaseEvent(event); + } + +public: + QList m_mouseEvents; +}; + +class MouseRecordingItem : public QQuickItem +{ +public: + MouseRecordingItem(bool acceptTouch, QQuickItem *parent = nullptr) + : QQuickItem(parent) + , m_acceptTouch(acceptTouch) + { + setSize(QSizeF(300, 300)); + setAcceptedMouseButtons(Qt::LeftButton); + } + +protected: + void touchEvent(QTouchEvent* event) override { + event->setAccepted(m_acceptTouch); + m_touchEvents << *event; + qCDebug(lcTests) << "accepted?" << event->isAccepted() << event; + } + void mousePressEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + void mouseMoveEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + void mouseReleaseEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + +public: + QList m_mouseEvents; + QList m_touchEvents; + +private: + bool m_acceptTouch; +}; + class tst_qquickwidget : public QQmlDataTest { Q_OBJECT @@ -63,8 +132,12 @@ private slots: void shortcuts(); void enterLeave(); void mouseEventWindowPos(); -}; + void synthMouseFromTouch_data(); + void synthMouseFromTouch(); +private: + QTouchDevice *device = QTest::createTouchDevice(); +}; tst_qquickwidget::tst_qquickwidget() { @@ -462,6 +535,48 @@ void tst_qquickwidget::mouseEventWindowPos() QTRY_VERIFY(rootItem->property("wasMoved").toBool()); } +void tst_qquickwidget::synthMouseFromTouch_data() +{ + QTest::addColumn("synthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents + QTest::addColumn("acceptTouch"); // QQuickItem::touchEvent: setAccepted() + + QTest::newRow("no synth, accept") << false << true; // suitable for touch-capable UIs + QTest::newRow("no synth, don't accept") << false << false; + QTest::newRow("synth and accept") << true << true; + QTest::newRow("synth, don't accept") << true << false; // the default +} + +void tst_qquickwidget::synthMouseFromTouch() +{ + QFETCH(bool, synthMouse); + QFETCH(bool, acceptTouch); + + QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, synthMouse); + QWidget window; + window.setAttribute(Qt::WA_AcceptTouchEvents); + QScopedPointer childView(new MouseRecordingQQWidget(&window)); + MouseRecordingItem *item = new MouseRecordingItem(acceptTouch, nullptr); + childView->setContent(QUrl(), nullptr, item); + window.resize(300, 300); + childView->resize(300, 300); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QVERIFY(childView->quickWindow()->isVisible()); + QVERIFY(item->isVisible()); + + QPoint p1 = QPoint(20, 20); + QPoint p2 = QPoint(30, 30); + QTest::touchEvent(&window, device).press(0, p1, &window); + QTest::touchEvent(&window, device).move(0, p2, &window); + QTest::touchEvent(&window, device).release(0, p2, &window); + + QCOMPARE(item->m_touchEvents.count(), 3); + QCOMPARE(item->m_mouseEvents.count(), acceptTouch ? 0 : 3); + QCOMPARE(childView->m_mouseEvents.count(), 0); + for (const QMouseEvent &ev : item->m_mouseEvents) + QCOMPARE(ev.source(), Qt::MouseEventSynthesizedByQt); +} + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc" -- cgit v1.2.3 From c2bd33af9fb548ff20bf34323271baa89cbc8a55 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 17 Jan 2018 08:29:12 +0100 Subject: Add QQuickWindow synthMouseFromTouch test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to the one for QQuickWidget, it tests the effect of AA_SynthesizeMouseForUnhandledTouchEvents and verifies the source() of the synthesized mouse event. Change-Id: Ib706b5cc7b9a374850f3197ca6ce2c3aed4d1ba2 Reviewed-by: Jan Arve Sæther --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 107 +++++++++++++++++++++ 1 file changed, 107 insertions(+) (limited to 'tests') diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index d358383ecb..5e06260128 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -47,6 +47,8 @@ #include #include +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + struct TouchEventData { QEvent::Type type; QWidget *widget; @@ -271,6 +273,70 @@ protected: } }; +class MouseRecordingWindow : public QQuickWindow +{ +public: + explicit MouseRecordingWindow(QWindow *parent = nullptr) : QQuickWindow(parent) { } + +protected: + void mousePressEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + QQuickWindow::mousePressEvent(event); + } + void mouseMoveEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + QQuickWindow::mouseMoveEvent(event); + } + void mouseReleaseEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + QQuickWindow::mouseReleaseEvent(event); + } + +public: + QList m_mouseEvents; +}; + +class MouseRecordingItem : public QQuickItem +{ +public: + MouseRecordingItem(bool acceptTouch, QQuickItem *parent = nullptr) + : QQuickItem(parent) + , m_acceptTouch(acceptTouch) + { + setSize(QSizeF(300, 300)); + setAcceptedMouseButtons(Qt::LeftButton); + } + +protected: + void touchEvent(QTouchEvent* event) override { + event->setAccepted(m_acceptTouch); + m_touchEvents << *event; + qCDebug(lcTests) << "accepted?" << event->isAccepted() << event; + } + void mousePressEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + void mouseMoveEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + void mouseReleaseEvent(QMouseEvent *event) override { + qCDebug(lcTests) << event; + m_mouseEvents << *event; + } + +public: + QList m_mouseEvents; + QList m_touchEvents; + +private: + bool m_acceptTouch; +}; + class tst_qquickwindow : public QQmlDataTest { Q_OBJECT @@ -308,6 +374,8 @@ private slots: void mergeTouchPointLists(); void mouseFromTouch_basic(); + void synthMouseFromTouch_data(); + void synthMouseFromTouch(); void clearWindow(); @@ -1074,6 +1142,45 @@ void tst_qquickwindow::mouseFromTouch_basic() delete item; } +void tst_qquickwindow::synthMouseFromTouch_data() +{ + QTest::addColumn("synthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents + QTest::addColumn("acceptTouch"); // QQuickItem::touchEvent: setAccepted() + + QTest::newRow("no synth, accept") << false << true; // suitable for touch-capable UIs + QTest::newRow("no synth, don't accept") << false << false; + QTest::newRow("synth and accept") << true << true; + QTest::newRow("synth, don't accept") << true << false; // the default +} + +void tst_qquickwindow::synthMouseFromTouch() +{ + QFETCH(bool, synthMouse); + QFETCH(bool, acceptTouch); + + QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, synthMouse); + QScopedPointer window(new MouseRecordingWindow); + QScopedPointer item(new MouseRecordingItem(acceptTouch, nullptr)); + item->setParentItem(window->contentItem()); + window->resize(250, 250); + window->setPosition(100, 100); + window->setTitle(QTest::currentTestFunction()); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + QPoint p1 = QPoint(20, 20); + QPoint p2 = QPoint(30, 30); + QTest::touchEvent(window.data(), touchDevice).press(0, p1, window.data()); + QTest::touchEvent(window.data(), touchDevice).move(0, p2, window.data()); + QTest::touchEvent(window.data(), touchDevice).release(0, p2, window.data()); + + QCOMPARE(item->m_touchEvents.count(), 3); + QCOMPARE(item->m_mouseEvents.count(), acceptTouch ? 0 : 3); + QCOMPARE(window->m_mouseEvents.count(), 0); + for (const QMouseEvent &ev : item->m_mouseEvents) + QCOMPARE(ev.source(), Qt::MouseEventSynthesizedByQt); +} + void tst_qquickwindow::clearWindow() { QQuickWindow *window = new QQuickWindow; -- cgit v1.2.3 From 8501993e5275076d9163a7e2f8bab9ba2f187f72 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 26 Jan 2018 11:28:06 +0100 Subject: QML: Collapse all NaNs into one single (encoded) NaN The idea of NaN boxing is to use one single NaN as a "true" NaN, and all others as a boxed value. So when encoding some NaN, be sure to use that one "true" NaN. Otherwise, it will be interpreted as an encoded value. Task-number: QTBUG-65998 Change-Id: Ia6e4641be180f3d626c40a57b473f181358e04db Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmlecmascript/data/nans.qml | 5 +++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/auto/qml/qqmlecmascript/data/nans.qml (limited to 'tests') diff --git a/tests/auto/qml/qqmlecmascript/data/nans.qml b/tests/auto/qml/qqmlecmascript/data/nans.qml new file mode 100644 index 0000000000..ece69f2d79 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nans.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property var prop: undefined +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index db7ec87c02..14447383c1 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -338,6 +338,7 @@ private slots: void freeze_empty_object(); void singleBlockLoops(); void qtbug_60547(); + void anotherNaN(); private: // static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); @@ -8266,6 +8267,20 @@ void tst_qqmlecmascript::qtbug_60547() QCOMPARE(object->property("counter"), QVariant(int(1))); } +void tst_qqmlecmascript::anotherNaN() +{ + QQmlComponent component(&engine, testFileUrl("nans.qml")); + QScopedPointer object(component.create()); + QVERIFY2(!object.isNull(), qPrintable(component.errorString())); + object->setProperty("prop", std::numeric_limits::quiet_NaN()); // don't crash + + std::uint64_t anotherNaN = 0xFFFFFF01000000F7ul; + double d; + std::memcpy(&d, &anotherNaN, sizeof(d)); + QVERIFY(std::isnan(d)); + object->setProperty("prop", d); // don't crash +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" -- cgit v1.2.3 From f70c0ceee44b6e36696e33a9c4df278e1c40daa6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 27 Nov 2017 13:22:34 +0100 Subject: touchEvent_propagation: remove some QWaits I doubt it will make the test any less flaky, but may be an improvement. Task-number: QTBUG-53916 Change-Id: I3901ab26107abfd1420947392232243ad8c40ead Reviewed-by: Mitch Curtis --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 5e06260128..cb710e2c8e 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -747,9 +747,8 @@ void tst_qquickwindow::touchEvent_propagation() // single touch to top item, should be received by middle item QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window); - QTest::qWait(50); + QTRY_COMPARE(middleItem->lastEvent.touchPoints.count(), 1); QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); - QCOMPARE(middleItem->lastEvent.touchPoints.count(), 1); QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)))); @@ -758,9 +757,8 @@ void tst_qquickwindow::touchEvent_propagation() // touch top and middle items, middle item should get both events QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) .press(1, pointInMiddleItem, window); - QTest::qWait(50); + QTRY_COMPARE(middleItem->lastEvent.touchPoints.count(), 2); QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); - QCOMPARE(middleItem->lastEvent.touchPoints.count(), 2); QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty()); COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, (QList() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos)) @@ -778,10 +776,9 @@ void tst_qquickwindow::touchEvent_propagation() // touch top and middle items, bottom item should get all events QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window) .press(1, pointInMiddleItem, window); - QTest::qWait(50); + QTRY_COMPARE(bottomItem->lastEvent.touchPoints.count(), 2); QVERIFY(topItem->lastEvent.touchPoints.isEmpty()); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); - QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 2); COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, (QList() << makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos)) << makeTouchPoint(bottomItem, bottomItem->mapFromItem(middleItem, pos)) ))); -- cgit v1.2.3