diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-12-16 17:05:21 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-12-16 17:05:21 +0100 |
commit | 13e88fe2b9b1680cb161a249289c3ba998f08c0c (patch) | |
tree | 496a9d88c69b441e8c88aa0416b327faca3a1532 /tests | |
parent | a2dad3ddee9c4bf274a7c6469342e4104605ceeb (diff) | |
parent | 470ba767663e4ad9d3183fb56ee89361354dfefb (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
src/quick/items/qquickitem.cpp
src/quick/items/qquicktext.cpp
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
Change-Id: I0bc5786098193c2c40b6fd8905de75d90f6ed0cf
Diffstat (limited to 'tests')
34 files changed, 904 insertions, 62 deletions
diff --git a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp index 98209e218c..43600e226e 100644 --- a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp +++ b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp @@ -86,7 +86,7 @@ void tst_qquicktrailemitter::test_basic() QVERIFY(myFuzzyLEQ(d->t, ((qreal)system->timeInt/1000.0))); } - QCOMPARE(system->groupData[1]->size(), 500); + QVERIFY(extremelyFuzzyCompare(system->groupData[1]->size(), 500, 10)); foreach (QQuickParticleData *d, system->groupData[1]->data) { if (d->t == -1) continue; //Particle data unused diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 39086d75ac..726f8636b6 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -134,6 +134,7 @@ private slots: void reentrancy_Array(); void reentrancy_objectCreation(); void jsIncDecNonObjectProperty(); + void JSONparse(); void qRegExpInport_data(); void qRegExpInport(); @@ -2500,6 +2501,13 @@ void tst_QJSEngine::jsIncDecNonObjectProperty() } } +void tst_QJSEngine::JSONparse() +{ + QJSEngine eng; + QJSValue ret = eng.evaluate("var json=\"{\\\"1\\\": null}\"; JSON.parse(json);"); + QVERIFY(ret.isObject()); +} + static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } void tst_QJSEngine::qRegExpInport_data() diff --git a/tests/auto/qml/qqmlecmascript/data/misctypetest.qml b/tests/auto/qml/qqmlecmascript/data/misctypetest.qml index 60ff53a2b4..088ede67ec 100644 --- a/tests/auto/qml/qqmlecmascript/data/misctypetest.qml +++ b/tests/auto/qml/qqmlecmascript/data/misctypetest.qml @@ -11,7 +11,7 @@ Item { return mtt.invalidUrl() == mtt.invalidUrl(); } - function test_invalid_url_refequal() + function test_invalid_url_strictequal() { return mtt.invalidUrl() === mtt.invalidUrl(); } @@ -21,7 +21,7 @@ Item { return mtt.validUrl() == mtt.validUrl(); } - function test_valid_url_refequal() + function test_valid_url_strictequal() { return mtt.validUrl() === mtt.validUrl(); } diff --git a/tests/auto/qml/qqmlecmascript/data/noCaptureWhenWritingProperty.qml b/tests/auto/qml/qqmlecmascript/data/noCaptureWhenWritingProperty.qml new file mode 100644 index 0000000000..8b8601692d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/noCaptureWhenWritingProperty.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 +QtObject { + property bool somePropertyEvaluated: false; + + property int someProperty: { + // It's sort of evil to set the property here, but that doesn't mean that + // this expression should get re-evaluated when unrelatedProperty changes later. + somePropertyEvaluated = true + return 20; + } + Component.onCompleted: { + somePropertyEvaluated = false + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml index 52abda1e55..8d08cc5559 100644 --- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml @@ -140,6 +140,22 @@ Item { if (msco.intListProperty.toString() != expected.toString()) success = false; expected = 7; if (poppedVal != expected) success = false; + + // concat + msco.stringListProperty = [ "one", "two" ] + stringList = [ "hello", "world" ] + stringList = stringList.concat(msco.stringListProperty) + expected = [ "hello", "world", "one", "two" ] + if (stringList.length != expected.length) { + success = false; + } else { + for (var i = 0; i < stringList.length; ++i) { + if (stringList[i] != expected[i]) { + success = false; + break; + } + } + } } 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 41fa3672bd..22fac2013e 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -106,7 +106,7 @@ public: void MyQmlObject::v8function(QQmlV4Function *function) { - QV8Engine::getV4(function->engine())->current->throwError(QStringLiteral("Exception thrown from within QObject slot")); + QV8Engine::getV4(function->engine())->currentContext()->throwError(QStringLiteral("Exception thrown from within QObject slot")); } static QJSValue script_api(QQmlEngine *engine, QJSEngine *scriptEngine) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 660be13f71..7b89709923 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -319,6 +319,7 @@ private slots: void stackLimits(); void idsAsLValues(); void qtbug_34792(); + void noCaptureWhenWritingProperty(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -2267,7 +2268,7 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::ValueRef o, cons QV4::Script program(QV8Engine::getV4(engine)->rootContext, functionSource); program.inheritContext = true; - QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->current; + QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->currentContext(); QV4::Scope scope(ctx); QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); @@ -2295,7 +2296,7 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::ValueRef o, QV4::Script program(QV8Engine::getV4(engine)->rootContext, functionSource); program.inheritContext = true; - QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->current; + QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->currentContext(); QV4::Scope scope(ctx); QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); @@ -2324,7 +2325,7 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::ValueRef QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->current; + QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->currentContext(); QV4::Scope scope(ctx); QV4::Script program(QV8Engine::getV4(engine)->rootContext, functionSource); @@ -7498,6 +7499,14 @@ void tst_qqmlecmascript::qtbug_34792() delete object; } +void tst_qqmlecmascript::noCaptureWhenWritingProperty() +{ + QQmlComponent component(&engine, testFileUrl("noCaptureWhenWritingProperty.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("somePropertyEvaluated").toBool(), false); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 382bfe4b73..004514d39c 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -54,7 +54,7 @@ #include <QQmlExpression> #include <QQmlIncubationController> #include <private/qqmlengine_p.h> -#include <private/qqmlabstracturlinterceptor_p.h> +#include <QQmlAbstractUrlInterceptor> class tst_qqmlengine : public QQmlDataTest { diff --git a/tests/auto/qml/qqmllanguage/data/customParserBindingScopes.qml b/tests/auto/qml/qqmllanguage/data/customParserBindingScopes.qml new file mode 100644 index 0000000000..2efc199f32 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/customParserBindingScopes.qml @@ -0,0 +1,19 @@ +import Test 1.0 +import QtQml 2.0 +QtObject { + id: root + + property int otherProperty: 10 + + property QtObject child: QtObject { + id: child + + property int testProperty; + property int otherProperty: 41 + + property QtObject customBinder: CustomBinding { + target: child + testProperty: otherProperty + 1 + } + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 3957f9d872..4a4ab3b81a 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -89,6 +89,8 @@ void registerTypes() qmlRegisterUncreatableType<MyUncreateableBaseClass,1>("Test", 1, 1, "MyUncreateableBaseClass", "Cannot create MyUncreateableBaseClass"); qmlRegisterType<MyCreateableDerivedClass,1>("Test", 1, 1, "MyCreateableDerivedClass"); + + qmlRegisterCustomType<CustomBinding>("Test", 1, 0, "CustomBinding", new CustomBindingParser); } QVariant myCustomVariantTypeConverter(const QString &data) @@ -97,3 +99,61 @@ QVariant myCustomVariantTypeConverter(const QString &data) rv.a = data.toInt(); return QVariant::fromValue(rv); } + + +QByteArray CustomBindingParser::compile(const QList<QQmlCustomParserProperty> &properties) +{ + QByteArray result; + QDataStream ds(&result, QIODevice::WriteOnly); + + ds << properties.count(); + for (int i = 0; i < properties.count(); ++i) { + const QQmlCustomParserProperty &prop = properties.at(i); + ds << prop.name(); + + Q_ASSERT(prop.assignedValues().count() == 1); + QVariant value = prop.assignedValues().first(); + + Q_ASSERT(value.userType() == qMetaTypeId<QQmlScript::Variant>()); + QQmlScript::Variant v = qvariant_cast<QQmlScript::Variant>(value); + Q_ASSERT(v.type() == QQmlScript::Variant::Script); + int bindingId = bindingIdentifier(v, prop.name()); + ds << bindingId; + + ds << prop.location().line; + } + + return result; +} + +void CustomBindingParser::setCustomData(QObject *object, const QByteArray &data) +{ + CustomBinding *customBinding = qobject_cast<CustomBinding*>(object); + Q_ASSERT(customBinding); + customBinding->m_bindingData = data; +} + +void CustomBinding::componentComplete() +{ + Q_ASSERT(m_target); + + QDataStream ds(m_bindingData); + int count; + ds >> count; + for (int i = 0; i < count; ++i) { + QString name; + ds >> name; + + int bindingId; + ds >> bindingId; + + int line; + ds >> line; + + QQmlBinding *binding = QQmlBinding::createBinding(QQmlBinding::Identifier(bindingId), m_target, qmlContext(this), QString(), line); + + QQmlProperty property(m_target, name, qmlContext(this)); + binding->setTarget(property); + QQmlPropertyPrivate::setBinding(property, binding); + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 703b26a73c..a968d9a25a 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1070,6 +1070,29 @@ QML_DECLARE_TYPE(MyRevisionedSubclass) QML_DECLARE_TYPE(MySubclass) QML_DECLARE_TYPE(MyReceiversTestObject) +class CustomBinding : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QObject* target READ target WRITE setTarget) +public: + + virtual void classBegin() {} + virtual void componentComplete(); + + QObject *target() const { return m_target; } + void setTarget(QObject *newTarget) { m_target = newTarget; } + + QPointer<QObject> m_target; + QByteArray m_bindingData; +}; + +class CustomBindingParser : public QQmlCustomParser +{ + virtual QByteArray compile(const QList<QQmlCustomParserProperty> &properties); + virtual void setCustomData(QObject *object, const QByteArray &data); +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 621061ab6a..6a577ec91c 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -214,6 +214,8 @@ private slots: void compositeSingletonSelectors(); void compositeSingletonRegistered(); + void customParserBindingScopes(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -3534,6 +3536,17 @@ void tst_qqmllanguage::compositeSingletonRegistered() verifyCompositeSingletonPropertyValues(o, "value1", 925, "value2", 755); } +void tst_qqmllanguage::customParserBindingScopes() +{ + QQmlComponent component(&engine, testFile("customParserBindingScopes.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + QPointer<QObject> child = qvariant_cast<QObject*>(o->property("child")); + QVERIFY(!child.isNull()); + QCOMPARE(child->property("testProperty").toInt(), 42); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp index d76c11b833..28a9bce98d 100644 --- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp +++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp @@ -55,6 +55,8 @@ public: tst_QQmlPropertyMap() {} private slots: + void initTestCase(); + void insert(); void operatorInsert(); void operatorValue(); @@ -68,8 +70,39 @@ private slots: void metaObjectAccessibility(); void QTBUG_31226(); void QTBUG_29836(); + void QTBUG_35233(); + void disallowExtending(); +}; + +class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(int someFixedProperty READ someFixedProperty WRITE setSomeFixedProperty) +public: + LazyPropertyMap() + : QQmlPropertyMap(this, /*parent*/0) + , value(0) + {} + + virtual void classBegin() {} + virtual void componentComplete() { + insert(QStringLiteral("lateProperty"), QStringLiteral("lateValue")); + } + + int someFixedProperty() const { return value; } + void setSomeFixedProperty(int v) { value = v; } + +private: + int value; }; +void tst_QQmlPropertyMap::initTestCase() +{ + qmlRegisterType<LazyPropertyMap>("QTBUG_35233", 1, 0, "LazyPropertyMap"); +} + void tst_QQmlPropertyMap::insert() { QQmlPropertyMap map; @@ -369,6 +402,54 @@ void tst_QQmlPropertyMap::QTBUG_29836() } +void tst_QQmlPropertyMap::QTBUG_35233() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQml 2.0\n" + "import QTBUG_35233 1.0\n" + "QtObject {\n" + " property QtObject testMap: LazyPropertyMap {\n" + " id: map\n" + " }\n" + " property QtObject sibling: QtObject {\n" + " objectName: \"sibling\"\n" + " property string testValue: map.lateProperty\n" + " }\n" + "}", QUrl()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QObject *sibling = obj->findChild<QObject*>("sibling"); + QVERIFY(sibling); + QCOMPARE(sibling->property("testValue").toString(), QString("lateValue")); +} + +void tst_QQmlPropertyMap::disallowExtending() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQml 2.0\n" + "import QTBUG_35233 1.0\n" + "LazyPropertyMap {\n" + " id: blah\n" + " someFixedProperty: 42\n" + "}\n", QUrl()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + component.setData("import QtQml 2.0\n" + "import QTBUG_35233 1.0\n" + "LazyPropertyMap {\n" + " id: blah\n" + " property int someNewProperty;\n" + "}\n", QUrl()); + obj.reset(component.create()); + QVERIFY(obj.isNull()); + QCOMPARE(component.errors().count(), 1); + QCOMPARE(component.errors().at(0).toString(), QStringLiteral("<Unknown File>: Fully dynamic types cannot declare new properties.")); +} + QTEST_MAIN(tst_QQmlPropertyMap) #include "tst_qqmlpropertymap.moc" diff --git a/tests/auto/qml/qqmltranslation/data/jstranslation.qml b/tests/auto/qml/qqmltranslation/data/jstranslation.qml new file mode 100644 index 0000000000..7adde019fa --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/jstranslation.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 +import "translation.js" as Js + +QtObject { + property string basic: Js.basic() + property string basic2: Js.basic2() + property string basic3: Js.basic3() + + property string disambiguation: Js.disambiguation() + property string disambiguation2: Js.disambiguation2() + property string disambiguation3: Js.disambiguation3() + + property string noop: Js.noop() + property string noop2: Js.noop2() + + property string singular: Js.singular() + property string singular2: Js.singular2() + property string plural: Js.plural() + property string plural2: Js.plural2() +} diff --git a/tests/auto/qml/qqmltranslation/data/translation.js b/tests/auto/qml/qqmltranslation/data/translation.js new file mode 100644 index 0000000000..c1e25dcd2f --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/translation.js @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2013 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$ +** +****************************************************************************/ + +function basic() { + return qsTr("hello") +} + +function basic2() { + return qsTranslate("CustomContext", "goodbye") +} + +function basic3() { + if (1) + return qsTr("hello") + return ""; +} + +function disambiguation() { + return qsTr("hi", "informal 'hello'") +} + +function disambiguation2() { + return qsTranslate("CustomContext", "see ya", "informal 'goodbye'") +} + +function disambiguation3() { + if (1) + return qsTr("hi", "informal 'hello'") + return ""; +} + +function noop() { + var _noop = QT_TR_NOOP("hello") + return qsTr(_noop) +} + +function noop2() { + var _noop2 = QT_TRANSLATE_NOOP("CustomContext", "goodbye") + return qsTranslate("CustomContext", _noop2) +} + +function singular() { + return qsTr("%n duck(s)", "", 1) +} + +function singular2() { + if (1) + return qsTr("%n duck(s)", "", 1) + return ""; +} + +function plural() { + return qsTr("%n duck(s)", "", 2) +} + +function plural2() { + if (1) + return qsTr("%n duck(s)", "", 2) + return ""; +} diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 0e22d3cfca..01e1cf85fb 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -52,19 +52,32 @@ public: tst_qqmltranslation() {} private slots: + void translation_data(); void translation(); void idTranslation(); - void translationInQrc(); }; +void tst_qqmltranslation::translation_data() +{ + QTest::addColumn<QString>("translation"); + QTest::addColumn<QUrl>("testFile"); + + QTest::newRow("qml") << QStringLiteral("qml_fr") << testFileUrl("translation.qml"); + QTest::newRow("qrc") << QStringLiteral(":/qml_fr.qm") << QUrl("qrc:/translation.qml"); + QTest::newRow("js") << QStringLiteral("qml_fr") << testFileUrl("jstranslation.qml"); +} + void tst_qqmltranslation::translation() { + QFETCH(QString, translation); + QFETCH(QUrl, testFile); + QTranslator translator; - translator.load(QLatin1String("qml_fr"), dataDirectory()); + translator.load(translation, dataDirectory()); QCoreApplication::installTranslator(&translator); QQmlEngine engine; - QQmlComponent component(&engine, testFileUrl("translation.qml")); + QQmlComponent component(&engine, testFile); QObject *object = component.create(); QVERIFY(object != 0); @@ -104,34 +117,6 @@ void tst_qqmltranslation::idTranslation() delete object; } -void tst_qqmltranslation::translationInQrc() -{ - QTranslator translator; - translator.load(":/qml_fr.qm"); - QCoreApplication::installTranslator(&translator); - - QQmlEngine engine; - QQmlComponent component(&engine, QUrl("qrc:/translation.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); - - QCOMPARE(object->property("basic").toString(), QLatin1String("bonjour")); - QCOMPARE(object->property("basic2").toString(), QLatin1String("au revoir")); - QCOMPARE(object->property("basic3").toString(), QLatin1String("bonjour")); - QCOMPARE(object->property("disambiguation").toString(), QLatin1String("salut")); - QCOMPARE(object->property("disambiguation2").toString(), QString::fromUtf8("\xc3\xa0 plus tard")); - QCOMPARE(object->property("disambiguation3").toString(), QLatin1String("salut")); - QCOMPARE(object->property("noop").toString(), QLatin1String("bonjour")); - QCOMPARE(object->property("noop2").toString(), QLatin1String("au revoir")); - QCOMPARE(object->property("singular").toString(), QLatin1String("1 canard")); - QCOMPARE(object->property("singular2").toString(), QLatin1String("1 canard")); - QCOMPARE(object->property("plural").toString(), QLatin1String("2 canards")); - QCOMPARE(object->property("plural2").toString(), QLatin1String("2 canards")); - - QCoreApplication::removeTranslator(&translator); - delete object; -} - QTEST_MAIN(tst_qqmltranslation) #include "tst_qqmltranslation.moc" diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp index ef27445920..dfcef43a7e 100644 --- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp +++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp @@ -184,7 +184,6 @@ void tst_qquickapplication::layoutDirection() // mirrored QGuiApplication::setLayoutDirection(Qt::RightToLeft); - QEXPECT_FAIL("", "QTBUG-21573", Abort); QCOMPARE(Qt::LayoutDirection(item->property("layoutDirection").toInt()), Qt::RightToLeft); // not mirrored again diff --git a/tests/auto/quick/qquickflickable/data/stopAtBounds.qml b/tests/auto/quick/qquickflickable/data/stopAtBounds.qml new file mode 100644 index 0000000000..d8661f3e45 --- /dev/null +++ b/tests/auto/quick/qquickflickable/data/stopAtBounds.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +Flickable { + id: outer + objectName: "flickable" + width: 400 + height: 400 + contentX: 50 + contentY: 50 + contentWidth: 500 + contentHeight: 500 + boundsBehavior: Flickable.StopAtBounds + + Rectangle { + x: 100 + y: 100 + width: 300 + height: 300 + + color: "yellow" + } +} diff --git a/tests/auto/quick/qquickflickable/qquickflickable.pro b/tests/auto/quick/qquickflickable/qquickflickable.pro index dfc8d7541b..88dc2fbc2a 100644 --- a/tests/auto/quick/qquickflickable/qquickflickable.pro +++ b/tests/auto/quick/qquickflickable/qquickflickable.pro @@ -11,4 +11,3 @@ TESTDATA = data/* QT += core-private gui-private qml-private quick-private testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 -CONFIG+=insignificant_test diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 3c518c509b..794f9a8af6 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -94,6 +94,8 @@ private slots: void flickTwiceUsingTouches(); void nestedStopAtBounds(); void nestedStopAtBounds_data(); + void stopAtBounds(); + void stopAtBounds_data(); void nestedMouseAreaUsingTouch(); private: @@ -285,11 +287,6 @@ void tst_qquickflickable::rebound() window->rootObject()->setProperty("transitionsStarted", 0); window->rootObject()->setProperty("transitionsFinished", 0); -#ifdef Q_OS_MAC - QSKIP("QTBUG-26696 - sometimes fails on Mac"); - return; -#endif - // flick and trigger the transition multiple times // (moving signals are emitted as soon as the first transition starts) flick(window.data(), QPoint(20,20), QPoint(120,120), 200); // both x and y will bounce back @@ -425,8 +422,7 @@ void tst_qquickflickable::pressDelay() // As we moved pass the drag threshold, we should never receive the press QVERIFY(mouseArea->property("pressed").toBool() == false); - QTest::qWait(200); - QVERIFY(mouseArea->property("pressed").toBool() == false); + QTRY_VERIFY(mouseArea->property("pressed").toBool() == false); // On release the clicked signal should *not* be emitted QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(150, 190)); @@ -1180,8 +1176,7 @@ void tst_qquickflickable::margins() // position to the far right, including margin obj->setContentX(1600 + 50 - obj->width()); obj->returnToBounds(); - QTest::qWait(200); - QCOMPARE(obj->contentX(), 1600. + 50. - obj->width()); + QTRY_COMPARE(obj->contentX(), 1600. + 50. - obj->width()); // position beyond the far right, including margin obj->setContentX(1600 + 50 - obj->width() + 1.); @@ -1196,8 +1191,7 @@ void tst_qquickflickable::margins() // position to the far bottom, including margin obj->setContentY(600 + 30 - obj->height()); obj->returnToBounds(); - QTest::qWait(200); - QCOMPARE(obj->contentY(), 600. + 30. - obj->height()); + QTRY_COMPARE(obj->contentY(), 600. + 30. - obj->height()); // position beyond the far bottom, including margin obj->setContentY(600 + 30 - obj->height() + 1.); @@ -1266,14 +1260,12 @@ void tst_qquickflickable::clickAndDragWhenTransformed() // click outside child rect QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(190, 190)); - QTest::qWait(10); - QCOMPARE(flickable->property("itemPressed").toBool(), false); + QTRY_COMPARE(flickable->property("itemPressed").toBool(), false); QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(190, 190)); // click inside child rect QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(200, 200)); - QTest::qWait(10); - QCOMPARE(flickable->property("itemPressed").toBool(), true); + QTRY_COMPARE(flickable->property("itemPressed").toBool(), true); QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(200, 200)); const int threshold = qApp->styleHints()->startDragDistance(); @@ -1322,13 +1314,15 @@ void tst_qquickflickable::flickTwiceUsingTouches() qreal contentYAfterFirstFlick = flickable->contentY(); qDebug() << "contentYAfterFirstFlick " << contentYAfterFirstFlick; QVERIFY(contentYAfterFirstFlick > 50.0f); + // Wait until view stops moving + QTRY_VERIFY(!flickable->isMoving()); flickWithTouch(window.data(), touchDevice, QPoint(100, 400), QPoint(100, 240)); // In the original bug, that second flick would cause Flickable to halt immediately qreal contentYAfterSecondFlick = flickable->contentY(); qDebug() << "contentYAfterSecondFlick " << contentYAfterSecondFlick; - QVERIFY(contentYAfterSecondFlick > (contentYAfterFirstFlick + 80.0f)); + QTRY_VERIFY(contentYAfterSecondFlick > (contentYAfterFirstFlick + 80.0f)); } void tst_qquickflickable::flickWithTouch(QWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to) @@ -1419,6 +1413,90 @@ void tst_qquickflickable::nestedStopAtBounds() QTRY_VERIFY(!outer->isMoving()); } +void tst_qquickflickable::stopAtBounds_data() +{ + QTest::addColumn<bool>("transpose"); + QTest::addColumn<bool>("invert"); + + QTest::newRow("left") << false << false; + QTest::newRow("right") << false << true; + QTest::newRow("top") << true << false; + QTest::newRow("bottom") << true << true; +} + +void tst_qquickflickable::stopAtBounds() +{ + QFETCH(bool, transpose); + QFETCH(bool, invert); + + QQuickView view; + view.setSource(testFileUrl("stopAtBounds.qml")); + QTRY_COMPARE(view.status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(&view); + QQuickViewTestUtil::moveMouseAway(&view); + view.show(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(view.rootObject()); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(view.rootObject()); + QVERIFY(flickable); + + if (transpose) + flickable->setContentY(invert ? 100 : 0); + else + flickable->setContentX(invert ? 100 : 0); + + const int threshold = qApp->styleHints()->startDragDistance(); + + QPoint position(200, 200); + int &axis = transpose ? position.ry() : position.rx(); + + // drag away from the aligned boundary. View should not move + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + for (int i = 0; i < 3; ++i) { + axis += invert ? -threshold : threshold; + QTest::mouseMove(&view, position); + } + QCOMPARE(flickable->isDragging(), false); + if (invert) + QCOMPARE(transpose ? flickable->isAtYEnd() : flickable->isAtXEnd(), true); + else + QCOMPARE(transpose ? flickable->isAtYBeginning() : flickable->isAtXBeginning(), true); + + // drag back towards boundary + for (int i = 0; i < 24; ++i) { + axis += invert ? threshold / 3 : -threshold / 3; + QTest::mouseMove(&view, position); + } + QTRY_COMPARE(flickable->isDragging(), true); + if (invert) + QCOMPARE(transpose ? flickable->isAtYEnd() : flickable->isAtXEnd(), false); + else + QCOMPARE(transpose ? flickable->isAtYBeginning() : flickable->isAtXBeginning(), false); + + // Drag away from the aligned boundary again. + // None of the mouse movements will position the view at the boundary exactly, + // but the view should end up aligned on the boundary + for (int i = 0; i < 5; ++i) { + axis += invert ? -threshold * 2 : threshold * 2; + QTest::mouseMove(&view, position); + } + QCOMPARE(flickable->isDragging(), true); + + // we should have hit the boundary and stopped + if (invert) { + QCOMPARE(transpose ? flickable->isAtYEnd() : flickable->isAtXEnd(), true); + QCOMPARE(transpose ? flickable->contentY() : flickable->contentX(), 100.0); + } else { + QCOMPARE(transpose ? flickable->isAtYBeginning() : flickable->isAtXBeginning(), true); + QCOMPARE(transpose ? flickable->contentY() : flickable->contentX(), 0.0); + } + + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); +} + void tst_qquickflickable::nestedMouseAreaUsingTouch() { QTouchDevice *touchDevice = new QTouchDevice; diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 2c6dcd72ba..ad3c4fc208 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -1310,7 +1310,10 @@ void tst_qquickitem::touchEventAcceptIgnore() bool accepted = window.event(&event); QVERIFY(item->touchEventReached); - QCOMPARE(accepted && event.isAccepted(), itemSupportsTouch); + + // always true because QtQuick always eats touch events so as to not + // allow QtGui to synthesise them for us. + QCOMPARE(accepted && event.isAccepted(), true); } { QTouchEvent::TouchPoint point; @@ -1330,7 +1333,10 @@ void tst_qquickitem::touchEventAcceptIgnore() bool accepted = window.event(&event); QCOMPARE(item->touchEventReached, itemSupportsTouch); - QCOMPARE(accepted && event.isAccepted(), itemSupportsTouch); + + // always true because QtQuick always eats touch events so as to not + // allow QtGui to synthesise them for us. + QCOMPARE(accepted && event.isAccepted(), true); } { QTouchEvent::TouchPoint point; @@ -1350,7 +1356,10 @@ void tst_qquickitem::touchEventAcceptIgnore() bool accepted = window.event(&event); QCOMPARE(item->touchEventReached, itemSupportsTouch); - QCOMPARE(accepted && event.isAccepted(), itemSupportsTouch); + + // always true because QtQuick always eats touch events so as to not + // allow QtGui to synthesise them for us. + QCOMPARE(accepted && event.isAccepted(), true); } } diff --git a/tests/auto/quick/qquicklistview/data/HighlightResize.qml b/tests/auto/quick/qquicklistview/data/HighlightResize.qml new file mode 100644 index 0000000000..2b07b39d20 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/HighlightResize.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +ListView { + id: view + + width: 400 + height: 400 + model: 5 + + highlight: Rectangle { + color: "skyblue" + } + + delegate: Item { + width: 100 + index * 20 + height: 100 + index * 10 + + Text { + anchors.centerIn: parent + text: model.index + } + } +} diff --git a/tests/auto/quick/qquicklistview/data/listview-sections.qml b/tests/auto/quick/qquicklistview/data/listview-sections.qml index d5b8a4400d..e9fb83910a 100644 --- a/tests/auto/quick/qquicklistview/data/listview-sections.qml +++ b/tests/auto/quick/qquicklistview/data/listview-sections.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 Rectangle { + property bool sectionsInvalidOnCompletion width: 240 height: 320 color: "#ffffff" @@ -9,6 +10,26 @@ Rectangle { id: myDelegate Item { id: wrapper + + function validateInitialSections() { + var invalid = false + if (index == 0) { + invalid |= wrapper.ListView.previousSection != "" + } + if (index == model.count - 1) { + invalid |= wrapper.ListView.nextSection != "" + } + if (index % 5 == 0 && index > 0) { + invalid |= wrapper.ListView.previousSection != Number(wrapper.ListView.section) - 1 + } else if ((index + 1) % 5 == 0 && index < model.count - 1) { + invalid |= wrapper.ListView.nextSection != Number(wrapper.ListView.section) + 1 + } else if (index > 0 && index < model.count - 1) { + invalid |= wrapper.ListView.previousSection != wrapper.ListView.section + invalid |= wrapper.ListView.nextSection != wrapper.ListView.section + } + sectionsInvalidOnCompletion |= invalid + } + objectName: "wrapper" height: ListView.previousSection != ListView.section ? 40 : 20; width: 240 @@ -49,6 +70,10 @@ Rectangle { visible: wrapper.ListView.previousSection != wrapper.ListView.section ? true : false Text { text: wrapper.ListView.section } } + ListView.onPreviousSectionChanged: validateInitialSections() + ListView.onNextSectionChanged: validateInitialSections() + ListView.onSectionChanged: validateInitialSections() + Component.onCompleted: validateInitialSections() } } ] diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index c3a5c64ba1..c3f42d393e 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -217,6 +217,8 @@ private slots: void typedModel(); void displayMargin(); + void highlightItemGeometryChanges(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -1957,6 +1959,8 @@ void tst_QQuickListView::sections(const QUrl &source) QCOMPARE(next->text().toInt(), (i+1)/5); } + QVERIFY(!listview->property("sectionsInvalidOnCompletion").toBool()); + QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged())); // Remove section boundary @@ -7048,6 +7052,25 @@ void tst_QQuickListView::displayMargin() delete window; } +void tst_QQuickListView::highlightItemGeometryChanges() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("HighlightResize.qml")); + + QScopedPointer<QObject> object(component.create()); + + QQuickListView *listview = qobject_cast<QQuickListView *>(object.data()); + QVERIFY(listview); + + QCOMPARE(listview->count(), 5); + + for (int i = 0; i < listview->count(); ++i) { + listview->setCurrentIndex(i); + QTRY_COMPARE(listview->highlightItem()->width(), qreal(100 + i * 20)); + QTRY_COMPARE(listview->highlightItem()->height(), qreal(100 + i * 10)); + } +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickloader/data/SimpleTestComponent.qml b/tests/auto/quick/qquickloader/data/SimpleTestComponent.qml new file mode 100644 index 0000000000..0e69012662 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/SimpleTestComponent.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +QtObject {} diff --git a/tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml b/tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml new file mode 100644 index 0000000000..ab86883af5 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Loader { + active: false + function setSourceComponent() { + sourceComponent = Qt.createComponent("SimpleTestComponent.qml"); + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 50ded4d95a..1e23d689ff 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -128,6 +128,8 @@ private slots: void sizeBound(); void QTBUG_30183(); + void sourceComponentGarbageCollection(); + private: QQmlEngine engine; }; @@ -1144,6 +1146,26 @@ void tst_QQuickLoader::QTBUG_30183() delete loader; } +void tst_QQuickLoader::sourceComponentGarbageCollection() +{ + QQmlComponent component(&engine, testFileUrl("sourceComponentGarbageCollection.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QMetaObject::invokeMethod(obj.data(), "setSourceComponent"); + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + + QSignalSpy spy(obj.data(), SIGNAL(loaded())); + + obj->setProperty("active", true); + + if (spy.isEmpty()) + QVERIFY(spy.wait()); + + QCOMPARE(spy.count(), 1); +} + QTEST_MAIN(tst_QQuickLoader) #include "tst_qquickloader.moc" diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 72639556ec..31b323af5d 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -89,6 +89,7 @@ private slots: void dragThreshold(); void invalidDrag_data() { rejectedButton_data(); } void invalidDrag(); + void cancelDragging(); void setDragOnPressed(); void updateMouseAreaPosOnClick(); void updateMouseAreaPosOnResize(); @@ -440,6 +441,57 @@ void tst_QQuickMouseArea::invalidDrag() QCOMPARE(blackRect->y(), 50.0); } +void tst_QQuickMouseArea::cancelDragging() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QVERIFY(window.rootObject() != 0); + + QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion"); + QQuickDrag *drag = mouseRegion->drag(); + QVERIFY(mouseRegion != 0); + QVERIFY(drag != 0); + + mouseRegion->setAcceptedButtons(Qt::LeftButton); + + // target + QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect"); + QVERIFY(blackRect != 0); + QVERIFY(blackRect == drag->target()); + + QVERIFY(!drag->active()); + + QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); + + QVERIFY(!drag->active()); + QCOMPARE(blackRect->x(), 50.0); + QCOMPARE(blackRect->y(), 50.0); + + QTest::mouseMove(&window, QPoint(111,111), 50); + QTest::mouseMove(&window, QPoint(116,116), 50); + QTest::mouseMove(&window, QPoint(122,122), 50); + + QTRY_VERIFY(drag->active()); + QTRY_COMPARE(blackRect->x(), 61.0); + QCOMPARE(blackRect->y(), 61.0); + + mouseRegion->QQuickItem::ungrabMouse(); + QTRY_VERIFY(!drag->active()); + QCOMPARE(blackRect->x(), 61.0); + QCOMPARE(blackRect->y(), 61.0); + + QTest::mouseMove(&window, QPoint(132,132), 50); + QTRY_VERIFY(!drag->active()); + QCOMPARE(blackRect->x(), 61.0); + QCOMPARE(blackRect->y(), 61.0); + + QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(122,122)); +} + void tst_QQuickMouseArea::setDragOnPressed() { QQuickView window; diff --git a/tests/auto/quick/qquickwindow/data/focus.qml b/tests/auto/quick/qquickwindow/data/focus.qml index 899b999cdc..fa8ae9dc69 100644 --- a/tests/auto/quick/qquickwindow/data/focus.qml +++ b/tests/auto/quick/qquickwindow/data/focus.qml @@ -12,4 +12,8 @@ Window.Window { Item { objectName: "item2" } + + FocusScope { + Item { objectName: "item3" } + } } diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 1d9beedbab..bdd70f6a6e 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -316,6 +316,7 @@ private slots: void qmlCreation(); void clearColor(); + void defaultState(); void grab_data(); void grab(); @@ -974,6 +975,25 @@ void tst_qquickwindow::clearColor() QCOMPARE(window->color(), QColor(Qt::blue)); } +void tst_qquickwindow::defaultState() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0; import QtQuick.Window 2.1; Window { }", QUrl()); + QObject *created = component.create(); + QScopedPointer<QObject> cleanup(created); + QVERIFY(created); + + QQuickWindow *qmlWindow = qobject_cast<QQuickWindow*>(created); + QVERIFY(qmlWindow); + + QQuickWindow cppWindow; + cppWindow.show(); + QTest::qWaitForWindowExposed(&cppWindow); + + QCOMPARE(qmlWindow->windowState(), cppWindow.windowState()); +} + void tst_qquickwindow::grab_data() { QTest::addColumn<bool>("visible"); @@ -1144,20 +1164,35 @@ void tst_qquickwindow::focusObject() QQuickWindow *window = qobject_cast<QQuickWindow*>(created); QVERIFY(window); + QSignalSpy focusObjectSpy(window, SIGNAL(focusObjectChanged(QObject*))); + window->show(); QVERIFY(QTest::qWaitForWindowExposed(window)); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); + QCOMPARE(window->contentItem(), window->focusObject()); + QCOMPARE(focusObjectSpy.count(), 1); + QQuickItem *item1 = window->findChild<QQuickItem*>("item1"); QVERIFY(item1); item1->setFocus(true); QCOMPARE(item1, window->focusObject()); + QCOMPARE(focusObjectSpy.count(), 2); QQuickItem *item2 = window->findChild<QQuickItem*>("item2"); QVERIFY(item2); item2->setFocus(true); QCOMPARE(item2, window->focusObject()); + QCOMPARE(focusObjectSpy.count(), 3); + + // set focus for item in non-focused focus scope and + // ensure focusObject does not change and signal is not emitted + QQuickItem *item3 = window->findChild<QQuickItem*>("item3"); + QVERIFY(item3); + item3->setFocus(true); + QCOMPARE(item2, window->focusObject()); + QCOMPARE(focusObjectSpy.count(), 3); } void tst_qquickwindow::ignoreUnhandledMouseEvents() diff --git a/tests/auto/quick/scenegraph/data/simple.qml b/tests/auto/quick/scenegraph/data/simple.qml new file mode 100644 index 0000000000..b2924f135e --- /dev/null +++ b/tests/auto/quick/scenegraph/data/simple.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 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$ +** +****************************************************************************/ + +import QtQuick 2.2 + +Rectangle { + width: 200 + height: 200 + color: "steelblue" + Rectangle { + width: 150 + height: 150 + anchors.centerIn: parent + color: "palegreen" + rotation: 45 + Text { + rotation: -45 + text: "Simple QML.." + anchors.centerIn: parent + } + } +} diff --git a/tests/auto/quick/scenegraph/scenegraph.pro b/tests/auto/quick/scenegraph/scenegraph.pro index 105221b7f4..1aa73ca60a 100644 --- a/tests/auto/quick/scenegraph/scenegraph.pro +++ b/tests/auto/quick/scenegraph/scenegraph.pro @@ -11,4 +11,5 @@ QT += core-private gui-private qml-private quick-private testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 OTHER_FILES += \ - data/render_OutOfFloatRange.qml + data/render_OutOfFloatRange.qml \ + data/simple.qml diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 3b79f01f12..780d5a97db 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -58,6 +58,8 @@ private slots: void render_data(); void render(); + + void hideWithOtherContext(); }; template <typename T> class ScopedList : public QList<T> { @@ -401,6 +403,40 @@ void tst_SceneGraph::render() } } +// Testcase for QTBUG-34898. We make another context current on another surface +// in the GUI thread and hide the QQuickWindow while the other context is +// current on the other window. +void tst_SceneGraph::hideWithOtherContext() +{ + QWindow window; + window.setSurfaceType(QWindow::OpenGLSurface); + window.resize(100, 100); + window.create(); + QOpenGLContext context; + context.create(); + bool renderingOnMainThread = false; + + { + QQuickView view; + view.setSource(QUrl::fromLocalFile("data/simple.qml")); + view.setResizeMode(QQuickView::SizeViewToRootObject); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + renderingOnMainThread = view.openglContext()->thread() == QGuiApplication::instance()->thread(); + + // Make the local context current on the local window... + context.makeCurrent(&window); + } + + // The local context should no longer be the current one. It is not + // rebound because all well behaving Qt/OpenGL applications are + // required to makeCurrent their context again before making any + // GL calls to a new frame (see QOpenGLContext docs). + QVERIFY(!renderingOnMainThread || QOpenGLContext::currentContext() != &context); +} + + #include "tst_scenegraph.moc" QTEST_MAIN(tst_SceneGraph) diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 6cf0aa4749..5b4ad0ffa3 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -154,6 +154,7 @@ private slots: void mouseOverTouch(); void buttonOnFlickable(); + void buttonOnDelayedPressFlickable(); void buttonOnTouch(); void pinchOnFlickable(); @@ -162,9 +163,22 @@ private slots: void tapOnDismissiveTopMouseAreaClicksBottomOne(); +protected: + bool eventFilter(QObject *, QEvent *event) + { + if (event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseMove || + event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + filteredEventList.append(Event(me->type(), me->pos(), me->globalPos())); + } + return false; + } + private: QQuickView *createView(); QTouchDevice *device; + QList<Event> filteredEventList; }; QQuickView *tst_TouchMouse::createView() @@ -496,7 +510,7 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(eventItem1->eventList.size(), 0); QPoint p1 = QPoint(20, 130); QTest::touchEvent(window, device).press(0, p1, window); - QCOMPARE(eventItem1->eventList.size(), 2); + QTRY_COMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); QTest::touchEvent(window, device).release(0, p1, window); @@ -559,12 +573,96 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove); QCOMPARE(windowPriv->mouseGrabberItem, flickable); + QCOMPARE(windowPriv->touchMouseId, 0); + QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); QVERIFY(flickable->isMovingVertically()); QTest::touchEvent(window, device).release(0, p3, window); delete window; } +void tst_TouchMouse::buttonOnDelayedPressFlickable() +{ + // flickable - height 500 / 1000 + // - eventItem1 y: 100, height 100 + // - eventItem2 y: 300, height 100 + + qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true); + filteredEventList.clear(); + + QQuickView *window = createView(); + + window->setSource(testFileUrl("buttononflickable.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(window->rootObject() != 0); + + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable"); + QVERIFY(flickable); + + window->installEventFilter(this); + + flickable->setPressDelay(60); + + // should a mouse area button be clickable on top of flickable? yes :) + EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1"); + QVERIFY(eventItem1); + eventItem1->setAcceptedMouseButtons(Qt::LeftButton); + eventItem1->acceptMouse = true; + + // should a touch button be touchable on top of flickable? yes :) + EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2"); + QVERIFY(eventItem2); + QCOMPARE(eventItem2->eventList.size(), 0); + eventItem2->acceptTouch = true; + + // wait to avoid getting a double click event + QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); + + // check that flickable moves - mouse button + QCOMPARE(eventItem1->eventList.size(), 0); + QPoint p1 = QPoint(10, 110); + QTest::touchEvent(window, device).press(0, p1, window); + // Flickable initially steals events + QCOMPARE(eventItem1->eventList.size(), 0); + // but we'll get the delayed mouse press after a delay + QTRY_COMPARE(eventItem1->eventList.size(), 1); + QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); + + // eventItem1 should have the mouse grab, and have moved the itemForTouchPointId + // for the touchMouseId to the new grabber. + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); + QCOMPARE(windowPriv->touchMouseId, 0); + QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1); + QCOMPARE(windowPriv->mouseGrabberItem, eventItem1); + + p1 += QPoint(0, -10); + QPoint p2 = p1 + QPoint(0, -10); + QPoint p3 = p2 + QPoint(0, -10); + QTest::qWait(10); + QTest::touchEvent(window, device).move(0, p1, window); + QTest::qWait(10); + QTest::touchEvent(window, device).move(0, p2, window); + QTest::qWait(10); + QTest::touchEvent(window, device).move(0, p3, window); + QVERIFY(flickable->isMovingVertically()); + + // flickable should have the mouse grab, and have moved the itemForTouchPointId + // for the touchMouseId to the new grabber. + QCOMPARE(windowPriv->mouseGrabberItem, flickable); + QCOMPARE(windowPriv->touchMouseId, 0); + QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); + + QTest::touchEvent(window, device).release(0, p3, window); + + // We should not have received any synthesised mouse events from Qt gui. + QCOMPARE(filteredEventList.count(), 0); + + delete window; +} + void tst_TouchMouse::buttonOnTouch() { // 400x800 |