diff options
Diffstat (limited to 'tests/auto/qml')
206 files changed, 4857 insertions, 856 deletions
diff --git a/tests/auto/qml/bindingdependencyapi/dummy_imports.qml b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml new file mode 100644 index 0000000000..b9a196e188 --- /dev/null +++ b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++ +// code in tst_parserstress.cpp + +import QtQuick 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quit.js b/tests/auto/qml/debugger/qqmldebugjs/data/quit.js new file mode 100644 index 0000000000..1a45fd5538 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/quit.js @@ -0,0 +1,4 @@ +function quit() { + console.log("hit"); + Qt.quit(); +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml new file mode 100644 index 0000000000..6e4183100b --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.0 +import "quit.js" as Quit; + +//DO NOT CHANGE + +Item { + Timer { + running: true + triggeredOnStart: true + onTriggered: Quit.quit(); + } +} + diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 1ac28c473b..5b6c43bc0c 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -38,7 +38,6 @@ #include <QtTest/qtest.h> #include <QtTest/qtestsystem.h> #include <QtCore/qprocess.h> -#include <QtCore/qtimer.h> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> #include <QtCore/qmutex.h> @@ -59,6 +58,8 @@ const char *ONCOMPLETED_QMLFILE = "oncompleted.qml"; const char *CREATECOMPONENT_QMLFILE = "createComponent.qml"; const char *CONDITION_QMLFILE = "condition.qml"; const char *QUIT_QMLFILE = "quit.qml"; +const char *QUITINJS_QMLFILE = "quitInJS.qml"; +const char *QUIT_JSFILE = "quit.js"; const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml"; const char *STEPACTION_QMLFILE = "stepAction.qml"; const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; @@ -110,8 +111,10 @@ private slots: void setBreakpointInScriptOnOptimizedBinding(); void setBreakpointInScriptWithCondition_data() { targetData(); } void setBreakpointInScriptWithCondition(); - void setBreakpointInScriptThatQuits_data() { targetData(); } + void setBreakpointInScriptThatQuits_data() { targetData(); }; void setBreakpointInScriptThatQuits(); + void setBreakpointInJavaScript_data(); + void setBreakpointInJavaScript(); void setBreakpointWhenAttaching(); void clearBreakpoint_data() { targetData(); } @@ -162,8 +165,6 @@ private: void targetData(); bool waitForClientSignal(const char *signal, int timeout = 30000); void checkVersionParameters(); - - QTime t; }; @@ -171,7 +172,6 @@ private: void tst_QQmlDebugJS::initTestCase() { QQmlDebugTest::initTestCase(); - t.start(); } QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile, @@ -454,6 +454,48 @@ void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); } +void tst_QQmlDebugJS::setBreakpointInJavaScript_data() +{ + QTest::addColumn<bool>("qmlscene"); + QTest::addColumn<bool>("seedCache"); + QTest::newRow("custom / immediate") << false << false; + QTest::newRow("qmlscene / immediate") << true << false; + QTest::newRow("custom / seeded") << false << true; + QTest::newRow("qmlscene / seeded") << true << true; +} + +void tst_QQmlDebugJS::setBreakpointInJavaScript() +{ + QFETCH(bool, qmlscene); + QFETCH(bool, seedCache); + + if (seedCache) { // Make sure there is a qmlc file that the engine should _not_ laod. + QProcess process; + process.start(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", + { testFile(QUITINJS_QMLFILE) }); + QTRY_COMPARE(process.state(), QProcess::NotRunning); + } + + QCOMPARE(init(qmlscene, QUITINJS_QMLFILE), ConnectSuccess); + + const int sourceLine = 2; + + m_client->setBreakpoint(QLatin1String(QUIT_JSFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(QUIT_JSFILE)); + + m_client->continueDebugging(QV4DebugClient::Continue); + + QVERIFY(m_process->waitForFinished()); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + void tst_QQmlDebugJS::setBreakpointWhenAttaching() { int sourceLine = 35; diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 99c90c142f..0ebf43eb6f 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -524,7 +524,7 @@ void tst_QQmlEngineDebugService::watch_property() QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name.toUtf8()); - QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2)); + QCOMPARE(spy.at(0).at(1).value<QVariant>(), QVariant::fromValue(origWidth*2)); } void tst_QQmlEngineDebugService::watch_object() @@ -772,11 +772,11 @@ void tst_QQmlEngineDebugService::queryObject() } // test specific property values - QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500)); - QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600)); - QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue"))); + QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500)); + QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600)); + QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue"))); - QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); + QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue"))); } else { foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); @@ -851,11 +851,11 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() } // test specific property values - QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500)); - QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600)); - QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue"))); + QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500)); + QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600)); + QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue"))); - QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); + QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue"))); } else { foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); @@ -1004,15 +1004,15 @@ void tst_QQmlEngineDebugService::queryExpressionResult_data() QTest::addColumn<QString>("expr"); QTest::addColumn<QVariant>("result"); - QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60); - QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500); - QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>")); - QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>")); - QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QVariantList() << QVariant(QString("<unnamed object>"))); + QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60); + QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500); + QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>")); + QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>")); + QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>"))); QVariantMap map; map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>"))); - QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map); - QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05); + QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map); + QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05); } void tst_QQmlEngineDebugService::queryExpressionResultInRootContext() @@ -1052,15 +1052,15 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC_data() QTest::addColumn<QString>("expr"); QTest::addColumn<QVariant>("result"); - QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60); - QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500); - QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>")); - QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>")); - QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QVariantList() << QVariant(QString("<unnamed object>"))); + QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60); + QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500); + QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>")); + QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>")); + QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>"))); QVariantMap map; map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>"))); - QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map); - QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05); + QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map); + QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05); } void tst_QQmlEngineDebugService::setBindingForObject() diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 497c721f50..84f5eebd10 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -36,7 +36,6 @@ #include <QQmlComponent> #include <private/qv4engine_p.h> #include <private/qv4debugging_p.h> -#include <private/qv8engine_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4string_p.h> #include <private/qqmlbuiltinfunctions_p.h> diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index a5246c8792..4c04afe886 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -211,7 +211,6 @@ built-ins/Promise/prototype/then/ctor-throws.js fails built-ins/Promise/race/ctx-ctor.js fails built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails -built-ins/RegExp/S15.10.2.12_A2_T1.js fails built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails @@ -219,7 +218,6 @@ built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails built-ins/RegExp/prototype/exec/success-lastindex-access.js fails built-ins/RegExp/prototype/source/value-line-terminator.js fails built-ins/RegExp/prototype/test/S15.10.6.3_A1_T22.js fails -built-ins/RegExp/u180e.js fails built-ins/RegExp/unicode_restricted_brackets.js fails built-ins/RegExp/unicode_restricted_character_class_escape.js fails built-ins/RegExp/unicode_restricted_identity_escape.js fails diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp index 2f41e57324..9fe2de5368 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp +++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp @@ -509,7 +509,7 @@ static bool executeTest(const QByteArray &data, bool runAsModule = false, const QVector<QUrl> modulesToLoad = { rootModuleUrl }; while (!modulesToLoad.isEmpty()) { QUrl url = modulesToLoad.takeFirst(); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> module; + QQmlRefPointer<QV4::ExecutableCompilationUnit> module; QFile f(url.toLocalFile()); if (f.open(QIODevice::ReadOnly)) { diff --git a/tests/auto/qml/parserstress/dummy_imports.qml b/tests/auto/qml/parserstress/dummy_imports.qml new file mode 100644 index 0000000000..b9a196e188 --- /dev/null +++ b/tests/auto/qml/parserstress/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++ +// code in tst_parserstress.cpp + +import QtQuick 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/parserstress/tst_parserstress.cpp b/tests/auto/qml/parserstress/tst_parserstress.cpp index e32fcabaf3..11851de76e 100644 --- a/tests/auto/qml/parserstress/tst_parserstress.cpp +++ b/tests/auto/qml/parserstress/tst_parserstress.cpp @@ -130,8 +130,7 @@ void tst_parserstress::ecmascript() QCOMPARE(component.errors().at(1).line(), 142); } else { - - QVERIFY(!component.isError()); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); } } diff --git a/tests/auto/qml/qjsengine/dummy_imports.qml b/tests/auto/qml/qjsengine/dummy_imports.qml new file mode 100644 index 0000000000..8d86f3583b --- /dev/null +++ b/tests/auto/qml/qjsengine/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++ +// code in tst_parserstress.cpp + +import QtQml 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index cd7796827d..f1ff396d4f 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -66,6 +66,10 @@ private slots: void newArray(); void newArray_HooliganTask218092(); void newArray_HooliganTask233836(); + void toScriptValue_data(); + void toScriptValue(); + void toScriptValuenotroundtripped_data(); + void toScriptValuenotroundtripped(); void newVariant(); void newVariant_valueOfToString(); void newVariant_valueOfEnum(); @@ -94,6 +98,7 @@ private slots: void valueConversion_basic2(); void valueConversion_dateTime(); void valueConversion_regExp(); + void valueConversion_RegularExpression(); void castWithMultipleInheritance(); void collectGarbage(); void gcWithNestedDataStructure(); @@ -135,6 +140,8 @@ private slots: void qRegExpInport_data(); void qRegExpInport(); + void qRegularExpressionImport_data(); + void qRegularExpressionImport(); void dateRoundtripJSQtJS(); void dateRoundtripQtJSQt(); void dateConversionJSQt(); @@ -194,7 +201,9 @@ private slots: void engineForObject(); void intConversion_QTBUG43309(); +#ifdef QT_DEPRECATED void toFixed(); +#endif void argumentEvaluationOrder(); @@ -239,6 +248,9 @@ private slots: void aggressiveGc(); void noAccumulatorInTemplateLiteral(); + void interrupt_data(); + void interrupt(); + void triggerBackwardJumpWithDestructuring(); public: @@ -484,17 +496,97 @@ void tst_QJSEngine::newArray_HooliganTask233836() } } +void tst_QJSEngine::toScriptValue_data() +{ + QTest::addColumn<QVariant>("input"); + + QTest::newRow("UnknownType") << QVariant(int(QMetaType::UnknownType), nullptr); + QTest::newRow("Nullptr") << QVariant(int(QMetaType::Nullptr), nullptr); + QTest::newRow("true") << QVariant(true); + QTest::newRow("false") << QVariant(false); + QTest::newRow("int") << QVariant(int(42)); + QTest::newRow("uint") << QVariant(uint(42)); + QTest::newRow("longlong") << QVariant(qlonglong(4242)); + QTest::newRow("ulonglong") << QVariant(qulonglong(4242)); + QTest::newRow("double") << QVariant(double(42.42)); + QTest::newRow("float") << QVariant(float(42.42)); + QTest::newRow("qstring") << QVariant(QString::fromLatin1("hello")); + QTest::newRow("qbytearray") << QVariant(QByteArray("hello")); + QTest::newRow("short") << QVariant(short('r')); + QTest::newRow("ushort") << QVariant(short('b')); + QTest::newRow("char") << QVariant(char('r')); + QTest::newRow("uchar") << QVariant(uchar('b')); + QTest::newRow("qchar") << QVariant(QString::fromUtf8("å").at(0)); + QTest::newRow("qdate") << QVariant(QDate(1925, 5, 8)); + QTest::newRow("qtime") << QVariant(QTime(4, 5, 6)); + QTest::newRow("qregularexpression") << QVariant(QRegularExpression(".*")); + QTest::newRow("qpointf") << QVariant(QPointF(42, 24)); + QTest::newRow("qvariantlist") << QVariant(QVariantList() << 42.24 << 5 << "hello"); + QTest::newRow("qvariantlist_point") << QVariant(QVariantList() << 42.24 << QPointF(42.24, 24.42) << QPointF(24.42, 42.24)); + QVariantMap vm; vm.insert("test", 55); vm.insert("abc", 42.42);; + QTest::newRow("qvariantmap") << QVariant(vm); + vm.clear(); vm.insert("point1", QPointF(42.24, 24.42)); vm.insert("point2", QPointF(42.24, 24.42)); + QTest::newRow("qvariantmap_point") << QVariant(vm); + QTest::newRow("qvariant") << QVariant(QVariant(42)); + QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>() << 1 << 2 << 3 << 4); + QTest::newRow("QVector<int>") << QVariant::fromValue(QVector<int>() << 1 << 2 << 3 << 4); + QTest::newRow("QList<QString>") << QVariant::fromValue(QVector<QString>() << "1" << "2" << "3" << "4"); + QTest::newRow("QStringList") << QVariant::fromValue(QStringList() << "1" << "2" << "3" << "4"); + QTest::newRow("QMap<QString, QString>") << QVariant::fromValue(QMap<QString, QString>{{ "1", "2" }, { "3", "4" }}); + QTest::newRow("QHash<QString, QString>") << QVariant::fromValue(QHash<QString, QString>{{ "1", "2" }, { "3", "4" }}); + QTest::newRow("QMap<QString, QPointF>") << QVariant::fromValue(QMap<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }}); + QTest::newRow("QHash<QString, QPointF>") << QVariant::fromValue(QHash<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }}); +} + +void tst_QJSEngine::toScriptValue() +{ + QFETCH(QVariant, input); + + QJSEngine engine; + QJSValue outputJS = engine.toScriptValue(input); + QVariant output = engine.fromScriptValue<QVariant>(outputJS); + + QCOMPARE(input, output); +} + +void tst_QJSEngine::toScriptValuenotroundtripped_data() +{ + QTest::addColumn<QVariant>("input"); + QTest::addColumn<QVariant>("output"); + + QTest::newRow("QList<QObject*>") << QVariant::fromValue(QList<QObject*>() << this) << QVariant(QVariantList() << QVariant::fromValue(this)); + QTest::newRow("QObjectList") << QVariant::fromValue(QObjectList() << this) << QVariant(QVariantList() << QVariant::fromValue(this)); + QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); + QTest::newRow("QVector<QPoint>") << QVariant::fromValue(QVector<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); + QTest::newRow("VoidStar") << QVariant(int(QMetaType::VoidStar), nullptr) << QVariant(int(QMetaType::Nullptr), nullptr); + QTest::newRow("qregex") << QVariant(QRegExp(".*", Qt::CaseSensitive, QRegExp::RegExp2)) << QVariant(QRegularExpression(".*")); +} + +// This is almost the same as toScriptValue, but the inputs don't roundtrip to +// exactly the same value. +void tst_QJSEngine::toScriptValuenotroundtripped() +{ + QFETCH(QVariant, input); + QFETCH(QVariant, output); + + QJSEngine engine; + QJSValue outputJS = engine.toScriptValue(input); + QVariant actualOutput = engine.fromScriptValue<QVariant>(outputJS); + + QCOMPARE(actualOutput, output); +} + void tst_QJSEngine::newVariant() { QJSEngine eng; { QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2))); QVERIFY(!opaque.isUndefined()); - QCOMPARE(opaque.isVariant(), true); + QCOMPARE(opaque.isVariant(), false); QVERIFY(!opaque.isCallable()); QCOMPARE(opaque.isObject(), true); QVERIFY(!opaque.prototype().isUndefined()); - QCOMPARE(opaque.prototype().isVariant(), true); + QCOMPARE(opaque.prototype().isVariant(), false); QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque)); } } @@ -508,7 +600,7 @@ void tst_QJSEngine::newVariant_valueOfToString() QJSValue value = object.property("valueOf").callWithInstance(object); QVERIFY(value.isObject()); QVERIFY(value.strictlyEquals(object)); - QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))")); + QCOMPARE(object.toString(), QString::fromLatin1("QPoint(10, 20)")); } } @@ -526,22 +618,27 @@ void tst_QJSEngine::newVariant_valueOfEnum() void tst_QJSEngine::newRegExp() { QJSEngine eng; - QJSValue rexp = eng.toScriptValue(QRegExp("foo")); - QVERIFY(!rexp.isUndefined()); - QCOMPARE(rexp.isRegExp(), true); - QCOMPARE(rexp.isObject(), true); - QCOMPARE(rexp.isCallable(), false); - // prototype should be RegExp.prototype - QVERIFY(!rexp.prototype().isUndefined()); - QCOMPARE(rexp.prototype().isObject(), true); - // Get [[Class]] internal property of RegExp Prototype Object. - // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". - // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". - QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); - QCOMPARE(r.toString(), QString::fromLatin1("[object Object]")); - QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); + QJSValue rexps[] = { + eng.toScriptValue(QRegularExpression("foo")), + eng.toScriptValue(QRegExp("foo")) + }; + for (const auto &rexp : rexps) { + QVERIFY(!rexp.isUndefined()); + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isObject(), true); + QCOMPARE(rexp.isCallable(), false); + // prototype should be RegExp.prototype + QVERIFY(!rexp.prototype().isUndefined()); + QCOMPARE(rexp.prototype().isObject(), true); + // Get [[Class]] internal property of RegExp Prototype Object. + // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". + // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". + QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); + QCOMPARE(r.toString(), QString::fromLatin1("[object Object]")); + QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); - QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); + QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); + } } void tst_QJSEngine::jsRegExp() @@ -1351,12 +1448,10 @@ public: Q_DECLARE_METATYPE(Foo) Q_DECLARE_METATYPE(Foo*) -Q_DECLARE_METATYPE(QLinkedList<QString>) Q_DECLARE_METATYPE(QList<Foo>) Q_DECLARE_METATYPE(QVector<QChar>) Q_DECLARE_METATYPE(QStack<int>) Q_DECLARE_METATYPE(QQueue<char>) -Q_DECLARE_METATYPE(QLinkedList<QStack<int> >) void tst_QJSEngine::valueConversion_basic() { @@ -1493,15 +1588,15 @@ void tst_QJSEngine::valueConversion_QVariant() QCOMPARE(val.toString(), str); } { - QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this)); + QJSValue val = eng.toScriptValue(QVariant::fromValue((QObject*)this)); QVERIFY(!val.isVariant()); QVERIFY(val.isQObject()); QCOMPARE(val.toQObject(), (QObject*)this); } { - QVariant var = qVariantFromValue(QPoint(123, 456)); + QVariant var = QVariant::fromValue(QPoint(123, 456)); QJSValue val = eng.toScriptValue(var); - QVERIFY(val.isVariant()); + QVERIFY(!val.isVariant()); QCOMPARE(val.toVariant(), var); } @@ -1607,6 +1702,28 @@ void tst_QJSEngine::valueConversion_regExp() } } +void tst_QJSEngine::valueConversion_RegularExpression() +{ + QJSEngine eng; + { + QRegularExpression in = QRegularExpression("foo"); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QRegularExpression out = qjsvalue_cast<QRegularExpression>(val); + QCOMPARE(out.pattern(), in.pattern()); + QCOMPARE(out.patternOptions(), in.patternOptions()); + } + { + QRegularExpression in = QRegularExpression("foo", + QRegularExpression::CaseInsensitiveOption); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QCOMPARE(qjsvalue_cast<QRegularExpression>(val), in); + QRegularExpression out = qjsvalue_cast<QRegularExpression>(val); + QCOMPARE(out.patternOptions(), in.patternOptions()); + } +} + Q_DECLARE_METATYPE(QGradient) Q_DECLARE_METATYPE(QGradient*) Q_DECLARE_METATYPE(QLinearGradient) @@ -2956,6 +3073,8 @@ void tst_QJSEngine::reentrancy_objectCreation() QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')"); QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2)); QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1)); + QCOMPARE(qjsvalue_cast<QRegularExpression>(r1), qjsvalue_cast<QRegularExpression>(r2)); + QCOMPARE(qjsvalue_cast<QRegularExpression>(r2), qjsvalue_cast<QRegularExpression>(r1)); } { QJSValue o1 = eng1.newQObject(temp); @@ -3151,6 +3270,56 @@ void tst_QJSEngine::qRegExpInport() } } +void tst_QJSEngine::qRegularExpressionImport_data() +{ + QTest::addColumn<QRegularExpression>("rx"); + QTest::addColumn<QString>("string"); + QTest::addColumn<QString>("matched"); + + QTest::newRow("normal") << QRegularExpression("(test|foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("normal2") << QRegularExpression("(Test|Foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive") << QRegularExpression("(test|foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive2") << QRegularExpression("(Test|Foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo"; + QTest::newRow("b(a*)(b*)") << QRegularExpression("b(a*)(b*)", QRegularExpression::CaseInsensitiveOption) << "aaabbBbaAabaAaababaaabbaaab"; + QTest::newRow("greedy") << QRegularExpression("a*(a*)", QRegularExpression::CaseInsensitiveOption) << "aaaabaaba"; + QTest::newRow("wildcard") << QRegularExpression(".*\\.txt") << "file.txt"; + QTest::newRow("wildcard 2") << QRegularExpression("a.b\\.txt") << "ab.txt abb.rtc acb.txt"; + QTest::newRow("slash") << QRegularExpression("g/.*/s", QRegularExpression::CaseInsensitiveOption) << "string/string/string"; + QTest::newRow("slash2") << QRegularExpression("g / .* / s", QRegularExpression::CaseInsensitiveOption) << "string / string / string"; + QTest::newRow("fixed") << QRegularExpression("a\\*aa\\.a\\(ba\\)\\*a\\\\ba", QRegularExpression::CaseInsensitiveOption) << "aa*aa.a(ba)*a\\ba"; + QTest::newRow("fixed insensitive") << QRegularExpression("A\\*A", QRegularExpression::CaseInsensitiveOption) << "a*A A*a A*A a*a"; + QTest::newRow("fixed sensitive") << QRegularExpression("A\\*A") << "a*A A*a A*A a*a"; + QTest::newRow("html") << QRegularExpression("<b>(.*)</b>") << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("html minimal") << QRegularExpression("^<b>(.*)</b>$") << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("aaa") << QRegularExpression("a{2,5}") << "aAaAaaaaaAa"; + QTest::newRow("aaa minimal") << QRegularExpression("^a{2,5}$") << "aAaAaaaaaAa"; + QTest::newRow("minimal") << QRegularExpression("^.*\\} [*8]$") << "}?} ?} *"; + QTest::newRow(".? minimal") << QRegularExpression("^.?$") << ".?"; + QTest::newRow(".+ minimal") << QRegularExpression("^.+$") << ".+"; + QTest::newRow("[.?] minimal") << QRegularExpression("^[.?]$") << ".?"; + QTest::newRow("[.+] minimal") << QRegularExpression("^[.+]$") << ".+"; +} + +void tst_QJSEngine::qRegularExpressionImport() +{ + QFETCH(QRegularExpression, rx); + QFETCH(QString, string); + + QJSEngine eng; + QJSValue rexp; + rexp = eng.toScriptValue(rx); + + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isCallable(), false); + + QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })"); + QJSValue result = func.call(QJSValueList() << string << rexp); + + const QRegularExpressionMatch match = rx.match(string); + for (int i = 0; i <= match.lastCapturedIndex(); i++) + QCOMPARE(result.property(i).toString(), match.captured(i)); +} + // QScriptValue::toDateTime() returns a local time, whereas JS dates // are always stored as UTC. Qt Script must respect the current time // zone, and correctly adjust for daylight saving time that may be in @@ -4156,7 +4325,10 @@ void tst_QJSEngine::engineForObject() QVERIFY(!qjsEngine(&object)); QJSValue wrapper = engine.newQObject(&object); QQmlEngine::setObjectOwnership(&object, QQmlEngine::CppOwnership); + QVERIFY(qjsEngine(&object)); +#ifdef QT_DEPRECATED QCOMPARE(qjsEngine(&object), wrapper.engine()); +#endif } QVERIFY(!qjsEngine(&object)); } @@ -4171,6 +4343,7 @@ void tst_QJSEngine::intConversion_QTBUG43309() QCOMPARE(result.toNumber(), 25.0); } +#ifdef QT_DEPRECATED // QTBUG-44039 and QTBUG-43885: void tst_QJSEngine::toFixed() { @@ -4182,6 +4355,7 @@ void tst_QJSEngine::toFixed() QVERIFY(result.isString()); QCOMPARE(result.toString(), QStringLiteral("12.1")); } +#endif void tst_QJSEngine::argumentEvaluationOrder() { @@ -4693,6 +4867,87 @@ void tst_QJSEngine::noAccumulatorInTemplateLiteral() qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); } +void tst_QJSEngine::interrupt_data() +{ + QTest::addColumn<int>("jitThreshold"); + QTest::addColumn<QString>("code"); + + const int big = (1 << 24); + for (int i = 0; i <= big; i += big) { + const char *mode = i ? "interpret" : "jit"; + QTest::addRow("for with content / %s", mode) << i << "var a = 0; for (;;) { a += 2; }"; + QTest::addRow("for empty / %s", mode) << i << "for (;;) {}"; + QTest::addRow("for continue / %s", mode) << i << "for (;;) { continue; }"; + QTest::addRow("while with content / %s", mode) << i << "var a = 0; while (true) { a += 2; }"; + QTest::addRow("while empty / %s", mode) << i << "while (true) {}"; + QTest::addRow("while continue / %s", mode) << i << "while (true) { continue; }"; + QTest::addRow("do with content / %s", mode) << i << "var a = 0; do { a += 2; } while (true);"; + QTest::addRow("do empty / %s", mode) << i << "do {} while (true);"; + QTest::addRow("do continue / %s", mode) << i << "do { continue; } while (true);"; + QTest::addRow("nested loops / %s", mode) << i << "while (true) { for (;;) {} }"; + QTest::addRow("labeled continue / %s", mode) << i << "a: while (true) { for (;;) { continue a; } }"; + QTest::addRow("labeled break / %s", mode) << i << "while (true) { a: for (;;) { break a; } }"; + QTest::addRow("tail call / %s", mode) << i << "'use strict';\nfunction x() { return x(); }; x();"; + QTest::addRow("huge array join / %s", mode) << i << "Array(1E9)|1"; + } +} + +class TemporaryJitThreshold +{ + Q_DISABLE_COPY_MOVE(TemporaryJitThreshold) +public: + TemporaryJitThreshold(int threshold) { + m_wasSet = qEnvironmentVariableIsSet(m_envVar); + m_value = qgetenv(m_envVar); + qputenv(m_envVar, QByteArray::number(threshold)); + } + + ~TemporaryJitThreshold() + { + if (m_wasSet) + qputenv(m_envVar, m_value); + else + qunsetenv(m_envVar); + } + +private: + const char *m_envVar = "QV4_JIT_CALL_THRESHOLD"; + bool m_wasSet = false; + QByteArray m_value; +}; + +void tst_QJSEngine::interrupt() +{ +#if QT_CONFIG(cxx11_future) + QFETCH(int, jitThreshold); + QFETCH(QString, code); + + TemporaryJitThreshold threshold(jitThreshold); + Q_UNUSED(threshold); + + QJSEngine *engineInThread = nullptr; + QScopedPointer<QThread> worker(QThread::create([&engineInThread, &code, jitThreshold](){ + QJSEngine jsEngine; + engineInThread = &jsEngine; + QJSValue result = jsEngine.evaluate(code); + QVERIFY(jsEngine.isInterrupted()); + QVERIFY(result.isError()); + QCOMPARE(result.toString(), QString::fromLatin1("Error: Interrupted")); + engineInThread = nullptr; + })); + worker->start(); + + QTRY_VERIFY(engineInThread); + + engineInThread->setInterrupted(true); + + QVERIFY(worker->wait()); + QVERIFY(!engineInThread); +#else + QSKIP("This test requires C++11 futures"); +#endif +} + void tst_QJSEngine::triggerBackwardJumpWithDestructuring() { QJSEngine engine; diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index a34a9e5188..37d0ea4dea 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -52,7 +52,9 @@ void tst_QJSValue::ctor_invalid() { QJSValue v; QVERIFY(v.isUndefined()); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } } @@ -63,7 +65,9 @@ void tst_QJSValue::ctor_undefinedWithEngine() QJSValue v = eng.toScriptValue(QVariant()); QVERIFY(v.isUndefined()); QCOMPARE(v.isObject(), false); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -75,7 +79,9 @@ void tst_QJSValue::ctor_nullWithEngine() QVERIFY(!v.isUndefined()); QCOMPARE(v.isNull(), true); QCOMPARE(v.isObject(), false); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -88,7 +94,9 @@ void tst_QJSValue::ctor_boolWithEngine() QCOMPARE(v.isBool(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toBool(), false); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -101,7 +109,9 @@ void tst_QJSValue::ctor_intWithEngine() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -118,7 +128,9 @@ void tst_QJSValue::ctor_int() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } } @@ -131,7 +143,9 @@ void tst_QJSValue::ctor_uintWithEngine() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -148,7 +162,9 @@ void tst_QJSValue::ctor_uint() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } } @@ -161,7 +177,9 @@ void tst_QJSValue::ctor_floatWithEngine() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -178,7 +196,9 @@ void tst_QJSValue::ctor_float() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } } @@ -191,7 +211,9 @@ void tst_QJSValue::ctor_stringWithEngine() QCOMPARE(v.isString(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toString(), QLatin1String("ciao")); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), &eng); +#endif } } @@ -203,7 +225,9 @@ void tst_QJSValue::ctor_string() QCOMPARE(v.isString(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toString(), QLatin1String("ciao")); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } { QJSValue v("ciao"); @@ -211,7 +235,9 @@ void tst_QJSValue::ctor_string() QCOMPARE(v.isString(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toString(), QLatin1String("ciao")); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } } @@ -223,12 +249,16 @@ void tst_QJSValue::ctor_copyAndAssignWithEngine() QJSValue v = eng.toScriptValue(1.0); QJSValue v2(v); QCOMPARE(v2.strictlyEquals(v), true); +#ifdef QT_DEPRECATED QCOMPARE(v2.engine(), &eng); +#endif QJSValue v3(v); QCOMPARE(v3.strictlyEquals(v), true); QCOMPARE(v3.strictlyEquals(v2), true); +#ifdef QT_DEPRECATED QCOMPARE(v3.engine(), &eng); +#endif QJSValue v4 = eng.toScriptValue(2.0); QCOMPARE(v4.strictlyEquals(v), false); @@ -253,7 +283,9 @@ void tst_QJSValue::ctor_undefined() QJSValue v(QJSValue::UndefinedValue); QVERIFY(v.isUndefined()); QCOMPARE(v.isObject(), false); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } void tst_QJSValue::ctor_null() @@ -262,7 +294,9 @@ void tst_QJSValue::ctor_null() QVERIFY(!v.isUndefined()); QCOMPARE(v.isNull(), true); QCOMPARE(v.isObject(), false); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } void tst_QJSValue::ctor_bool() @@ -273,7 +307,9 @@ void tst_QJSValue::ctor_bool() QCOMPARE(v.isBool(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toBool(), false); +#ifdef QT_DEPRECATED QCOMPARE(v.engine(), (QJSEngine *)nullptr); +#endif } void tst_QJSValue::ctor_copyAndAssign() @@ -281,12 +317,16 @@ void tst_QJSValue::ctor_copyAndAssign() QJSValue v(1.0); QJSValue v2(v); QCOMPARE(v2.strictlyEquals(v), true); +#ifdef QT_DEPRECATED QCOMPARE(v2.engine(), (QJSEngine *)nullptr); +#endif QJSValue v3(v); QCOMPARE(v3.strictlyEquals(v), true); QCOMPARE(v3.strictlyEquals(v2), true); +#ifdef QT_DEPRECATED QCOMPARE(v3.engine(), (QJSEngine *)nullptr); +#endif QJSValue v4(2.0); QCOMPARE(v4.strictlyEquals(v), false); @@ -411,8 +451,8 @@ void tst_QJSValue::toString() // variant should use internal valueOf(), then fall back to QVariant::toString(), // then fall back to "QVariant(typename)" QJSValue variant = eng.toScriptValue(QPoint(10, 20)); - QVERIFY(variant.isVariant()); - QCOMPARE(variant.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))")); + QVERIFY(!variant.isVariant()); + QCOMPARE(variant.toString(), QString::fromLatin1("QPoint(10, 20)")); variant = eng.toScriptValue(QUrl()); QVERIFY(variant.isVariant()); QVERIFY(variant.toString().isEmpty()); @@ -423,7 +463,9 @@ void tst_QJSValue::toString() QCOMPARE(o.toString(), QStringLiteral("[object Object]")); o = createUnboundValue(o); +#ifdef QT_DEPRECATED QVERIFY(!o.engine()); +#endif QCOMPARE(o.toString(), QStringLiteral("[object Object]")); } @@ -435,7 +477,9 @@ void tst_QJSValue::toString() QCOMPARE(o.toString(), QStringLiteral("1,2,3")); o = createUnboundValue(o); +#ifdef QT_DEPRECATED QVERIFY(!o.engine()); +#endif QCOMPARE(o.toString(), QStringLiteral("1,2,3")); } @@ -1034,6 +1078,20 @@ void tst_QJSValue::toVariant() QJSValue rxObject = eng.toScriptValue(rx); QVERIFY(rxObject.isRegExp()); QVariant var = rxObject.toVariant(); + + // We can't roundtrip a QRegExp this way, as toVariant() has no information on whether we + // want QRegExp or QRegularExpression. It will always create a QRegularExpression. + QCOMPARE(var.type(), QMetaType::QRegularExpression); + QRegularExpression result = var.toRegularExpression(); + QCOMPARE(result.pattern(), rx.pattern()); + QCOMPARE(result.patternOptions() & QRegularExpression::CaseInsensitiveOption, 0); + } + + { + QRegularExpression rx = QRegularExpression("[0-9a-z]+"); + QJSValue rxObject = eng.toScriptValue(rx); + QVERIFY(rxObject.isRegExp()); + QVariant var = rxObject.toVariant(); QCOMPARE(var, QVariant(rx)); } @@ -1114,7 +1172,7 @@ void tst_QJSValue::toQObject_nonQObject_data() QTest::newRow("array") << engine->newArray(); QTest::newRow("date") << engine->evaluate("new Date(124)"); QTest::newRow("variant(12345)") << engine->toScriptValue(QVariant(12345)); - QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(qVariantFromValue((QObject*)nullptr)); + QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(QVariant::fromValue((QObject*)nullptr)); QTest::newRow("newQObject(0)") << engine->newQObject(nullptr); } @@ -1201,6 +1259,32 @@ void tst_QJSValue::toRegExp() QVERIFY(qjsvalue_cast<QRegExp>(eng.toScriptValue(QVariant())).isEmpty()); } +void tst_QJSValue::toRegularExpression() +{ + QJSEngine eng; + { + QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/foo/")); + QVERIFY(rx.isValid()); + QCOMPARE(rx.pattern(), QString::fromLatin1("foo")); + QVERIFY(!(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption)); + } + { + QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/bar/gi")); + QVERIFY(rx.isValid()); + QCOMPARE(rx.pattern(), QString::fromLatin1("bar")); + QVERIFY(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption); + } + + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("[]")).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("{}")).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.globalObject()).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue()).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(123)).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(false)).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("null")).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.toScriptValue(QVariant())).pattern().isEmpty()); +} + void tst_QJSValue::isArray_data() { newEngine(); @@ -1601,10 +1685,14 @@ void tst_QJSValue::getSetProperty() QCOMPARE(object.property("baz").toNumber(), num.toNumber()); QJSValue strstr = QJSValue("bar"); +#ifdef QT_DEPRECATED QCOMPARE(strstr.engine(), (QJSEngine *)nullptr); +#endif object.setProperty("foo", strstr); QCOMPARE(object.property("foo").toString(), strstr.toString()); +#ifdef QT_DEPRECATED QCOMPARE(strstr.engine(), &eng); // the value has been bound to the engine +#endif QJSValue numnum = QJSValue(123.0); object.setProperty("baz", numnum); @@ -2255,8 +2343,8 @@ void tst_QJSValue::strictlyEquals() { QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a")); QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a")); - QVERIFY(var1.isArray()); - QVERIFY(var2.isArray()); + QVERIFY(!var1.isArray()); + QVERIFY(!var2.isArray()); QVERIFY(!var1.strictlyEquals(var2)); } { @@ -2294,7 +2382,7 @@ void tst_QJSValue::castToPointer() QBrush *bp = qjsvalue_cast<QBrush*>(v); QVERIFY(!bp); - QJSValue v2 = eng.toScriptValue(qVariantFromValue(cp)); + QJSValue v2 = eng.toScriptValue(QVariant::fromValue(cp)); QCOMPARE(qjsvalue_cast<QColor*>(v2), cp); } } @@ -2496,15 +2584,18 @@ void tst_QJSValue::engineDeleted() delete eng; QVERIFY(v1.isUndefined()); - QVERIFY(!v1.engine()); QVERIFY(v2.isUndefined()); - QVERIFY(!v2.engine()); QVERIFY(v3.isUndefined()); - QVERIFY(!v3.engine()); QVERIFY(v4.isUndefined()); - QVERIFY(!v4.engine()); QVERIFY(v5.isString()); // was not bound to engine + +#ifdef QT_DEPRECATED + QVERIFY(!v1.engine()); + QVERIFY(!v2.engine()); + QVERIFY(!v3.engine()); + QVERIFY(!v4.engine()); QVERIFY(!v5.engine()); +#endif QVERIFY(v3.property("foo").isUndefined()); } diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h index ccbacb3acc..f704169d43 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h @@ -74,6 +74,7 @@ private slots: void toQObject(); void toDateTime(); void toRegExp(); + void toRegularExpression(); void isArray_data(); void isArray(); void isDate(); diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 5448088ee5..db9bb52010 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -70,6 +70,7 @@ PRIVATETESTS += \ qqmltranslation \ qqmlimport \ qqmlobjectmodel \ + qqmltablemodel \ qv4assembler \ qv4mm \ qv4identifiertable \ @@ -104,7 +105,3 @@ qtConfig(private_tests): \ qtNomakeTools( \ qmlplugindump \ ) - -QtConfig(qml_tracing) { - PRIVATETESTS += v4traced -} diff --git a/tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml b/tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml new file mode 100644 index 0000000000..2128a54d81 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml @@ -0,0 +1,7 @@ +import QtQml 2.12 + +QtObject { + signal testSignal(string a, int b, string c, bool d, bool e, real f, real g, bool h, int i, int j, string k, int l, string m, string n) + onTestSignal: {} + Component.onCompleted: testSignal("a", 1, "b", true, true, 0.1, 0.1, true, 1, 1, "a", 1, "a", "a") +} diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index 53b26ccfae..4daf1d35c3 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -17,7 +17,8 @@ RESOURCES += \ data/jsmoduleimport.qml \ data/script.mjs \ data/module.mjs \ - data/utils.mjs + data/utils.mjs \ + data/parameterAdjustment.qml workerscripts_test.files = \ data/worker.js \ diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 741989e732..4a1f5378a6 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -36,6 +36,7 @@ #include <QSysInfo> #include <QLoggingCategory> #include <private/qqmlcomponent_p.h> +#include <private/qqmlscriptdata_p.h> #include <qtranslator.h> #include "../../shared/util.h" @@ -72,6 +73,8 @@ private slots: void reproducibleCache_data(); void reproducibleCache(); + + void parameterAdjustment(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -669,6 +672,14 @@ void tst_qmlcachegen::reproducibleCache() QCOMPARE(contents1, contents2); } +void tst_qmlcachegen::parameterAdjustment() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/parameterAdjustment.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); // Doesn't crash +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" diff --git a/tests/auto/qml/qmldiskcache/dummy_imports.qml b/tests/auto/qml/qmldiskcache/dummy_imports.qml new file mode 100644 index 0000000000..b9a196e188 --- /dev/null +++ b/tests/auto/qml/qmldiskcache/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in C++ +// code in tst_parserstress.cpp + +import QtQuick 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 70a5a73e0f..1f0115b926 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -30,10 +30,11 @@ #include <private/qv4compileddata_p.h> #include <private/qv4compiler_p.h> -#include <private/qv8engine_p.h> #include <private/qv4engine_p.h> #include <private/qv4codegen_p.h> #include <private/qqmlcomponent_p.h> +#include <private/qv4executablecompilationunit_p.h> +#include <private/qqmlscriptdata_p.h> #include <QQmlComponent> #include <QQmlEngine> #include <QQmlFileSelector> @@ -119,7 +120,8 @@ struct TestCompiler { closeMapping(); testFilePath = baseDirectory + QStringLiteral("/test.qml"); - cacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + cacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); mappedFile.setFileName(cacheFilePath); } @@ -186,8 +188,10 @@ struct TestCompiler bool verify() { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString); + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit + = QV4::ExecutableCompilationUnit::create(); + return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), + QFileInfo(testFilePath).lastModified(), &lastErrorString); } void closeMapping() @@ -264,8 +268,9 @@ void tst_qmldiskcache::loadLocalAsFallback() f.write(reinterpret_cast<const char *>(&unit), sizeof(unit)); } - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), QFileInfo(testCompiler.testFilePath).lastModified(), + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); + bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), + QFileInfo(testCompiler.testFilePath).lastModified(), &testCompiler.lastErrorString); QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString)); QCOMPARE(unit->objectCount(), 1); @@ -324,7 +329,10 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(2)); QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); - QCOMPARE(obj->bindingTable()->valueAsNumber(reinterpret_cast<const QV4::Value *>(testUnit->constants())), double(42)); + + QCOMPARE(reinterpret_cast<const QV4::Value *>(testUnit->constants()) + [obj->bindingTable()->value.constantValueIndex].doubleValue(), + double(42)); QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); @@ -573,7 +581,8 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); - QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath))); + QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } @@ -588,7 +597,8 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 100); - QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(selectedTestFilePath))); + QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(selectedTestFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } } @@ -636,10 +646,21 @@ void tst_qmldiskcache::localAliases() } } +static QSet<QString> entrySet(const QDir &dir) +{ + const auto &list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + return QSet<QString>(list.cbegin(), list.cend()); +} + +static QSet<QString> entrySet(const QDir &dir, const QStringList &filters) +{ + const auto &list = dir.entryList(filters); + return QSet<QString>(list.cbegin(), list.cend()); +} + void tst_qmldiskcache::cacheResources() { - const QSet<QString> existingFiles = - m_qmlCacheDirectory.entryList(QDir::Files | QDir::NoDotAndDotDot).toSet(); + const QSet<QString> existingFiles = entrySet(m_qmlCacheDirectory); QQmlEngine engine; @@ -650,8 +671,7 @@ void tst_qmldiskcache::cacheResources() QCOMPARE(obj->property("value").toInt(), 20); } - const QSet<QString> entries = - m_qmlCacheDirectory.entryList(QDir::NoDotAndDotDot | QDir::Files).toSet().subtract(existingFiles); + const QSet<QString> entries = entrySet(m_qmlCacheDirectory).subtract(existingFiles); QCOMPARE(entries.count(), 1); QDateTime cacheFileTimeStamp; @@ -680,8 +700,7 @@ void tst_qmldiskcache::cacheResources() } { - const QSet<QString> entries = - m_qmlCacheDirectory.entryList(QDir::NoDotAndDotDot | QDir::Files).toSet().subtract(existingFiles); + const QSet<QString> entries = entrySet(m_qmlCacheDirectory).subtract(existingFiles); QCOMPARE(entries.count(), 1); QCOMPARE(QFileInfo(m_qmlCacheDirectory.absoluteFilePath(*entries.cbegin())).lastModified().toMSecsSinceEpoch(), @@ -738,7 +757,8 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes() QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData()); QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData()); - const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -816,7 +836,8 @@ void tst_qmldiskcache::singletonDependency() QCOMPARE(obj->property("value").toInt(), 42); } - const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -873,7 +894,8 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency() QCOMPARE(value.toInt(), 42); } - const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath( + QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -902,8 +924,7 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency() void tst_qmldiskcache::cacheModuleScripts() { - const QSet<QString> existingFiles = - m_qmlCacheDirectory.entryList(QDir::Files | QDir::NoDotAndDotDot).toSet(); + const QSet<QString> existingFiles = entrySet(m_qmlCacheDirectory); QQmlEngine engine; @@ -924,8 +945,7 @@ void tst_qmldiskcache::cacheModuleScripts() QVERIFY(!compilationUnit->backingFile.isNull()); } - const QSet<QString> entries = - m_qmlCacheDirectory.entryList(QStringList("*.mjsc")).toSet().subtract(existingFiles); + const QSet<QString> entries = entrySet(m_qmlCacheDirectory, QStringList("*.mjsc")); QCOMPARE(entries.count(), 1); diff --git a/tests/auto/qml/qmllint/data/CatchStatement.qml b/tests/auto/qml/qmllint/data/CatchStatement.qml new file mode 100644 index 0000000000..e0f70fce7e --- /dev/null +++ b/tests/auto/qml/qmllint/data/CatchStatement.qml @@ -0,0 +1,8 @@ +import QtQml 2.12 + +QtObject { + function f() { + try {} catch(err) {} + console.log(err); + } +} diff --git a/tests/auto/qml/qmllint/data/FromRoot.qml b/tests/auto/qml/qmllint/data/FromRoot.qml new file mode 100644 index 0000000000..021c09285e --- /dev/null +++ b/tests/auto/qml/qmllint/data/FromRoot.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +Item { + id: root + property int unqualified: 42 + + Item { + Item { + x: unqualified // user defined property from root + } + + QtObject { + property int check: x // existing property from root + } + } + +} diff --git a/tests/auto/qml/qmllint/data/IdFromOuterSpace.qml b/tests/auto/qml/qmllint/data/IdFromOuterSpace.qml new file mode 100644 index 0000000000..774a1cfc7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/IdFromOuterSpace.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + x: alien.x + + Component.onCompleted: { + console.log(alien); + } +} diff --git a/tests/auto/qml/qmllint/data/SignalHandler.qml b/tests/auto/qml/qmllint/data/SignalHandler.qml new file mode 100644 index 0000000000..865277cedb --- /dev/null +++ b/tests/auto/qml/qmllint/data/SignalHandler.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +MouseArea { + onDoubleClicked: { + console.log(mouse); + // do further things + } + onClicked: console.info(mouse) + onPositionChanged: { + console.log(mouse) + } + onPressAndHold: console.warn(mouse) +} diff --git a/tests/auto/qml/qmllint/data/WithStatement.qml b/tests/auto/qml/qmllint/data/WithStatement.qml new file mode 100644 index 0000000000..5641f21eeb --- /dev/null +++ b/tests/auto/qml/qmllint/data/WithStatement.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Item { + Item { + id: target + property int test: 42 + } + Component.onCompleted: { + with(target) { + console.log(test); + } + } +} diff --git a/tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml b/tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml new file mode 100644 index 0000000000..097baa6fcf --- /dev/null +++ b/tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml @@ -0,0 +1,7 @@ +import QtQml 2.12 + +QtObject { + function f() { + try {} catch(err) {console.log(err);} + } +} diff --git a/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml new file mode 100644 index 0000000000..a59b736929 --- /dev/null +++ b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml @@ -0,0 +1,8 @@ +import QtQuick 2.12 +import QtQml 2.12 + +Item { + QtObject { + property int x: parent.x + } +} diff --git a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml new file mode 100644 index 0000000000..1323593031 --- /dev/null +++ b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml @@ -0,0 +1,7 @@ +import QtQuick 2.12 + +Item { + Unknown { + property int x: parent.x + } +} diff --git a/tests/auto/qml/qmllint/main.cpp b/tests/auto/qml/qmllint/main.cpp deleted file mode 100644 index eedbd7c2f2..0000000000 --- a/tests/auto/qml/qmllint/main.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins 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$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include <QProcess> -#include <QString> - -class TestQmllint: public QObject -{ - Q_OBJECT - -private Q_SLOTS: - void initTestCase(); - void test(); - void test_data(); -private: - QString m_qmllintPath; -}; - -void TestQmllint::initTestCase() -{ - m_qmllintPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmllint"); -#ifdef Q_OS_WIN - m_qmllintPath += QLatin1String(".exe"); -#endif - if (!QFileInfo(m_qmllintPath).exists()) { - QString message = QStringLiteral("qmllint executable not found (looked for %0)").arg(m_qmllintPath); - QFAIL(qPrintable(message)); - } -} - -void TestQmllint::test_data() -{ - QTest::addColumn<QString>("filename"); - QTest::addColumn<bool>("isValid"); - - // Valid files: - QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml") << true; - QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml") << true; - QTest::newRow("QTBUG-45916_JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js") << true; - - // Invalid files: - QTest::newRow("Invalid_syntax_QML") << QStringLiteral("failure1.qml") << false; - QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js") << false; -} - -void TestQmllint::test() -{ - QFETCH(QString, filename); - QFETCH(bool, isValid); - filename = QStringLiteral("data/") + filename; - QStringList args; - args << QStringLiteral("--silent") << filename; - - bool success = QProcess::execute(m_qmllintPath, args) == 0; - QCOMPARE(success, isValid); -} - -QTEST_MAIN(TestQmllint) -#include "main.moc" diff --git a/tests/auto/qml/qmllint/qmllint.pro b/tests/auto/qml/qmllint/qmllint.pro index b53a6f6877..95470b4085 100644 --- a/tests/auto/qml/qmllint/qmllint.pro +++ b/tests/auto/qml/qmllint/qmllint.pro @@ -1,6 +1,11 @@ -TEMPLATE = app -TARGET = testqmllint -INCLUDEPATH += . +CONFIG += testcase +TARGET = tst_qmllint +macos:CONFIG -= app_bundle + +SOURCES += tst_qmllint.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* -SOURCES += main.cpp QT += testlib diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp new file mode 100644 index 0000000000..582f146dca --- /dev/null +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QProcess> +#include <QString> + +#include <util.h> + +class TestQmllint: public QQmlDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase() override; + void test(); + void test_data(); + void testUnqualified(); + void testUnqualified_data(); + void testUnqualifiedNoSpuriousParentWarning(); + void catchIdentifierNoFalsePositive(); +private: + QString m_qmllintPath; +}; + +void TestQmllint::initTestCase() +{ + QQmlDataTest::initTestCase(); + m_qmllintPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmllint"); +#ifdef Q_OS_WIN + m_qmllintPath += QLatin1String(".exe"); +#endif + if (!QFileInfo(m_qmllintPath).exists()) { + QString message = QStringLiteral("qmllint executable not found (looked for %0)").arg(m_qmllintPath); + QFAIL(qPrintable(message)); + } +} + +void TestQmllint::test_data() +{ + QTest::addColumn<QString>("filename"); + QTest::addColumn<bool>("isValid"); + + // Valid files: + QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml") << true; + QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml") << true; + QTest::newRow("QTBUG-45916_JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js") << true; + + // Invalid files: + QTest::newRow("Invalid_syntax_QML") << QStringLiteral("failure1.qml") << false; + QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js") << false; +} + +void TestQmllint::testUnqualified() +{ + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); + QFETCH(QString, filename); + QFETCH(QString, warningMessage); + QFETCH(int, warningLine); + QFETCH(int, warningColumn); + QStringList args; + args << QStringLiteral("-U") << testFile(filename) << QStringLiteral("-I") << qmlImportDir; + + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode()); + QString output = process.readAllStandardError(); + QVERIFY(output.contains(QString::asprintf("Warning: unqualified access at %d:%d", warningLine, warningColumn))); + QVERIFY(output.contains(warningMessage)); +} + +void TestQmllint::testUnqualified_data() +{ + QTest::addColumn<QString>("filename"); + QTest::addColumn<QString>("warningMessage"); + QTest::addColumn<int>("warningLine"); + QTest::addColumn<int>("warningColumn"); + + // check for false positive due to and warning about with statement + QTest::newRow("WithStatement") << QStringLiteral("WithStatement.qml") << QStringLiteral("with statements are strongly discouraged") << 10 << 25; + // id from nowhere (as with setContextProperty) + QTest::newRow("IdFromOuterSpaceDirect") << QStringLiteral("IdFromOuterSpace.qml") << "alien.x" << 4 << 8; + QTest::newRow("IdFromOuterSpaceAccess") << QStringLiteral("IdFromOuterSpace.qml") << "console.log(alien)" << 7 << 21; + // access property of root object + QTest::newRow("FromRootDirect") << QStringLiteral("FromRoot.qml") << QStringLiteral("x: root.unqualified") << 9 << 16; // new property + QTest::newRow("FromRootAccess") << QStringLiteral("FromRoot.qml") << QStringLiteral("property int check: root.x") << 13 << 33; // builtin property + // access injected name from signal + QTest::newRow("SignalHandler1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 5 << 21; + QTest::newRow("SignalHandler2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPositionChanged: function(mouse) {...") << 10 << 21; + QTest::newRow("SignalHandlerShort1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onClicked: (mouse) => {...") << 8 << 29; + QTest::newRow("SignalHandlerShort2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => {...") << 12 << 34; + // access catch identifier outside catch block + QTest::newRow("CatchStatement") << QStringLiteral("CatchStatement.qml") << QStringLiteral("err") << 6 << 21; +} + +void TestQmllint::testUnqualifiedNoSpuriousParentWarning() +{ + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); + { + QString filename = testFile("spuriousParentWarning.qml"); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode() == 0); + } + { + QString filename = testFile("nonSpuriousParentWarning.qml"); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode()); + } +} + +void TestQmllint::catchIdentifierNoFalsePositive() +{ + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); + QString filename = QLatin1String("catchIdentifierNoWarning.qml"); + filename.prepend(QStringLiteral("data/")); + QStringList args; + args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir; + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QVERIFY(process.exitStatus() == QProcess::NormalExit); + QVERIFY(process.exitCode() == 0); +} + +void TestQmllint::test() +{ + QFETCH(QString, filename); + QFETCH(bool, isValid); + QStringList args; + args << QStringLiteral("--silent") << testFile(filename); + + bool success = QProcess::execute(m_qmllintPath, args) == 0; + QCOMPARE(success, isValid); +} + +QTEST_MAIN(TestQmllint) +#include "tst_qmllint.moc" diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index d1e74aecef..e244369581 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -64,6 +64,7 @@ tst_qmlmin::tst_qmlmin() void tst_qmlmin::initTestCase() { +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled qmlminPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmlmin"); #ifdef Q_OS_WIN qmlminPath += QLatin1String(".exe"); @@ -129,6 +130,10 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qjsengine/script/com/trolltech/syntaxerror/__init__.js"; invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml"; invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml"; + invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml"; + // generatorFunction.qml is not invalid per se, but the minifier cannot handle yield statements + invalidFiles << "tests/auto/qml/qqmlecmascript/data/generatorFunction.qml"; +#endif } QStringList tst_qmlmin::findFiles(const QDir &d) diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml b/tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml new file mode 100644 index 0000000000..2f0ac401d7 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml @@ -0,0 +1,6 @@ +pragma Singleton +import dumper.Imports 1.0 + +Imports { + property int something: 2 +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro index d20ea967ea..b4bd9baf5b 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro @@ -17,7 +17,7 @@ HEADERS += \ imports.h !equals(_PRO_FILE_PWD_, $$OUT_PWD) { - cp.files = qmldir plugins.qmltypes CompositeImports.qml + cp.files = qmldir plugins.qmltypes CompositeImports.qml Derived.qml cp.path = $$OUT_PWD COPIES += cp } diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes index 937dd60a9e..fb13928ba0 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes @@ -14,4 +14,14 @@ Module { exports: ["dumper.Imports/Imports 1.0"] exportMetaObjectRevisions: [0] } + Component { + prototype: "Imports" + name: "dumper.Imports/Derived 1.0" + exports: ["dumper.Imports/Derived 1.0"] + exportMetaObjectRevisions: [0] + isComposite: true + isCreatable: false + isSingleton: true + Property { name: "something"; type: "int" } + } } diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir index c9058a7f95..f84fca1d75 100644 --- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir @@ -1,3 +1,4 @@ module dumper.Imports plugin Imports CompositeImports 1.0 CompositeImports.qml +singleton Derived 1.0 Derived.qml diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index a9c28a0911..0f5eea8b95 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -52,6 +52,7 @@ private slots: void removeObjectsWhenDestroyed(); void loadTranslation_data(); void loadTranslation(); + void setInitialProperties(); private: QString buildDir; @@ -96,6 +97,9 @@ void tst_qqmlapplicationengine::basicLoading() // will break. void tst_qqmlapplicationengine::testNonResolvedPath() { +#ifdef Q_OS_ANDROID + QSKIP("Android stores QML files in resources, and the path to a resource cannot be relative in this case"); +#endif { // NOTE NOTE NOTE! Missing testFileUrl is *WANTED* here! We want a // non-resolved URL. @@ -117,6 +121,9 @@ void tst_qqmlapplicationengine::testNonResolvedPath() void tst_qqmlapplicationengine::application_data() { +#ifdef Q_OS_ANDROID + QSKIP("Cannot launch external process on Android"); +#endif QTest::addColumn<QByteArray>("qmlFile"); QTest::addColumn<QByteArray>("expectedStdErr"); @@ -269,6 +276,23 @@ void tst_qqmlapplicationengine::loadTranslation() QCOMPARE(rootObject->property("translation").toString(), translation); } +void tst_qqmlapplicationengine::setInitialProperties() +{ + QQmlApplicationEngine test {}; + { + test.setInitialProperties(QVariantMap{{"success", false}}); + test.load(testFileUrl("basicTest.qml")); + QVERIFY(!test.rootObjects().empty()); + QCOMPARE(test.rootObjects().first()->property("success").toBool(), false); + } + { + test.setInitialProperties({{"success", true}}); + test.load(testFileUrl("basicTest.qml")); + QCOMPARE(test.rootObjects().size(), 2); + QCOMPARE(test.rootObjects().at(1)->property("success").toBool(), true); + } +} + QTEST_MAIN(tst_qqmlapplicationengine) #include "tst_qqmlapplicationengine.moc" diff --git a/tests/auto/qml/qqmlbinding/data/MyComponent.qml b/tests/auto/qml/qqmlbinding/data/MyComponent.qml new file mode 100644 index 0000000000..5892539a5d --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/MyComponent.qml @@ -0,0 +1,2 @@ +import QtQuick 2.3 +QtObject { property real p: 0 } diff --git a/tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml b/tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml new file mode 100644 index 0000000000..471db9023b --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + id: root + property MyComponent myProperty + Binding { + target: root + property: "myProperty" + value: myObject + } + MyComponent { id: myObject } +} diff --git a/tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml b/tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml new file mode 100644 index 0000000000..366dbf0464 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml @@ -0,0 +1,14 @@ +import QtQuick 2.11 + +Item { + visible: true + width: 320 + height: 200 + property int val: other.val + + Rectangle { + id: other + anchors.fill: parent; + property int val: undefined / 2 + } +} diff --git a/tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml b/tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml new file mode 100644 index 0000000000..d0f30c5da5 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml @@ -0,0 +1,30 @@ +import QtQuick 2.12 +import QtQuick.Window 2.12 + +Window { +visible: true +width: 640 +height: 480 +title: qsTr("Hello World") + + Rectangle { + id: colorRect + objectName: "colorRect" + anchors.fill: parent + Text { + objectName: "colorLabel" + id: colorLabel + } + } + + Binding { + target: colorLabel + property: "text" + value: "red" + } + Binding { + target: colorRect + property: "color" + value: "red" + } +} diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml new file mode 100644 index 0000000000..b42f975fe0 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.0 +import QtQml 2.14 + +Rectangle { + height: 400 + width: 400 + + Rectangle { + property bool when: true + + id: myItem + objectName: "myItem" + height: 300 + width: 200 + } + + property var boundValue: 100 + + Binding { + restoreMode: Binding.RestoreValue + objectName: "theBinding" + target: myItem + property: "height" + when: myItem.when + value: boundValue + } +} diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml new file mode 100644 index 0000000000..1f63457ab6 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.0 +import QtQml 2.14 + +Rectangle { + height: 400 + width: 400 + + Rectangle { + property bool when: true + + id: myItem + objectName: "myItem" + height: 300 + width: 200 + property var foo: 42 + } + + property var boundValue: 13 + + Binding { + restoreMode: Binding.RestoreBindingOrValue + objectName: "theBinding" + target: myItem + property: "foo" + when: myItem.when + value: boundValue + } +} diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml new file mode 100644 index 0000000000..f34c3b40cf --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.0 +import QtQml 2.14 + +Rectangle { + height: 400 + width: 400 + + Rectangle { + property bool when: true + + id: myItem + objectName: "myItem" + height: 300 + width: 200 + property var foo: original + property bool fooCheck: foo === original && foo.bar() === 42 + } + + property var original: ({ bar: function() { return 42 } }) + + property var boundValue: 13 + + Binding { + restoreMode: Binding.RestoreBinding + objectName: "theBinding" + target: myItem + property: "foo" + when: myItem.when + value: boundValue + } +} diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index 34cf21024d..2610402455 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -42,6 +42,9 @@ private slots: void binding(); void whenAfterValue(); void restoreBinding(); + void restoreBindingValue(); + void restoreBindingVarValue(); + void restoreBindingJSValue(); void restoreBindingWithLoop(); void restoreBindingWithoutCrash(); void deletedObject(); @@ -51,6 +54,9 @@ private slots: void disabledOnReadonlyProperty(); void delayed(); void bindingOverwriting(); + void bindToQmlComponent(); + void bindingDoesNoWeirdConversion(); + void bindNaNToInt(); private: QQmlEngine engine; @@ -64,7 +70,7 @@ void tst_qqmlbinding::binding() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("test-binding.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QQuickRectangle> rect { qobject_cast<QQuickRectangle*>(c.create()) }; QVERIFY(rect != nullptr); QQmlBind *binding3 = qobject_cast<QQmlBind*>(rect->findChild<QQmlBind*>("binding3")); @@ -81,18 +87,16 @@ void tst_qqmlbinding::binding() QQmlBind *binding = qobject_cast<QQmlBind*>(rect->findChild<QQmlBind*>("binding1")); QVERIFY(binding != nullptr); - QCOMPARE(binding->object(), qobject_cast<QObject*>(rect)); + QCOMPARE(binding->object(), qobject_cast<QObject*>(rect.get())); QCOMPARE(binding->property(), QLatin1String("text")); QCOMPARE(binding->value().toString(), QLatin1String("Hello")); - - delete rect; } void tst_qqmlbinding::whenAfterValue() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("test-binding2.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())}; QVERIFY(rect != nullptr); QCOMPARE(rect->color(), QColor("yellow")); @@ -100,15 +104,13 @@ void tst_qqmlbinding::whenAfterValue() rect->setProperty("changeColor", true); QCOMPARE(rect->color(), QColor("red")); - - delete rect; } void tst_qqmlbinding::restoreBinding() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBinding.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QQuickRectangle> rect { qobject_cast<QQuickRectangle*>(c.create()) }; QVERIFY(rect != nullptr); QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); @@ -130,15 +132,85 @@ void tst_qqmlbinding::restoreBinding() //original binding restored myItem->setY(49); QCOMPARE(myItem->x(), qreal(100-49)); +} + +void tst_qqmlbinding::restoreBindingValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreBinding2.qml")); + QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QVERIFY(!rect.isNull()); + + auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); + QVERIFY(myItem != nullptr); + + QCOMPARE(myItem->height(), 100); + myItem->setProperty("when", QVariant(false)); + QCOMPARE(myItem->height(), 300); // make sure the original value was restored + + myItem->setProperty("when", QVariant(true)); + QCOMPARE(myItem->height(), 100); // make sure the value specified in Binding is set + rect->setProperty("boundValue", 200); + QCOMPARE(myItem->height(), 200); // make sure the changed binding value is set + myItem->setProperty("when", QVariant(false)); + // make sure that the original value is back, not e.g. the value from before the + // change (i.e. 100) + QCOMPARE(myItem->height(), 300); +} + +void tst_qqmlbinding::restoreBindingVarValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreBinding3.qml")); + QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QVERIFY(!rect.isNull()); + + auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); + QVERIFY(myItem != nullptr); + + QCOMPARE(myItem->property("foo"), 13); + myItem->setProperty("when", QVariant(false)); + QCOMPARE(myItem->property("foo"), 42); // make sure the original value was restored + + myItem->setProperty("when", QVariant(true)); + QCOMPARE(myItem->property("foo"), 13); // make sure the value specified in Binding is set + rect->setProperty("boundValue", 31337); + QCOMPARE(myItem->property("foo"), 31337); // make sure the changed binding value is set + myItem->setProperty("when", QVariant(false)); + // make sure that the original value is back, not e.g. the value from before the + // change (i.e. 100) + QCOMPARE(myItem->property("foo"), 42); +} + +void tst_qqmlbinding::restoreBindingJSValue() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("restoreBinding4.qml")); + QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); + QVERIFY(!rect.isNull()); + + auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); + QVERIFY(myItem != nullptr); + + QCOMPARE(myItem->property("fooCheck"), false); + myItem->setProperty("when", QVariant(false)); + QCOMPARE(myItem->property("fooCheck"), true); // make sure the original value was restored + + myItem->setProperty("when", QVariant(true)); + QCOMPARE(myItem->property("fooCheck"), false); // make sure the value specified in Binding is set + rect->setProperty("boundValue", 31337); + QCOMPARE(myItem->property("fooCheck"), false); // make sure the changed binding value is set + myItem->setProperty("when", QVariant(false)); + // make sure that the original value is back, not e.g. the value from before the change + QCOMPARE(myItem->property("fooCheck"), true); - delete rect; } void tst_qqmlbinding::restoreBindingWithLoop() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBindingWithLoop.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())}; QVERIFY(rect != nullptr); QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); @@ -166,15 +238,13 @@ void tst_qqmlbinding::restoreBindingWithLoop() myItem->setY(49); QCOMPARE(myItem->x(), qreal(49 + 100)); - - delete rect; } void tst_qqmlbinding::restoreBindingWithoutCrash() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBindingWithoutCrash.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())}; QVERIFY(rect != nullptr); QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); @@ -205,8 +275,6 @@ void tst_qqmlbinding::restoreBindingWithoutCrash() //original binding restored myItem->setY(49); QCOMPARE(myItem->x(), qreal(100-49)); - - delete rect; } //QTBUG-20692 @@ -214,15 +282,13 @@ void tst_qqmlbinding::deletedObject() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("deletedObject.qml")); - QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); + QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())}; QVERIFY(rect != nullptr); QGuiApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); //don't crash rect->setProperty("activateBinding", true); - - delete rect; } void tst_qqmlbinding::warningOnUnknownProperty() @@ -231,9 +297,8 @@ void tst_qqmlbinding::warningOnUnknownProperty() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("unknownProperty.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - delete item; QCOMPARE(messageHandler.messages().count(), 1); @@ -247,9 +312,8 @@ void tst_qqmlbinding::warningOnReadOnlyProperty() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("readonlyProperty.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - delete item; QCOMPARE(messageHandler.messages().count(), 1); @@ -263,9 +327,8 @@ void tst_qqmlbinding::disabledOnUnknownProperty() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("disabledUnknown.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - delete item; QCOMPARE(messageHandler.messages().count(), 0); } @@ -276,10 +339,8 @@ void tst_qqmlbinding::disabledOnReadonlyProperty() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("disabledReadonly.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - delete item; - QCOMPARE(messageHandler.messages().count(), 0); } @@ -287,21 +348,19 @@ void tst_qqmlbinding::delayed() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("delayed.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())}; QVERIFY(item != nullptr); // update on creation QCOMPARE(item->property("changeCount").toInt(), 1); - QMetaObject::invokeMethod(item, "updateText"); + QMetaObject::invokeMethod(item.get(), "updateText"); // doesn't update immediately QCOMPARE(item->property("changeCount").toInt(), 1); QCoreApplication::processEvents(); // only updates once (non-delayed would update twice) QCOMPARE(item->property("changeCount").toInt(), 2); - - delete item; } void tst_qqmlbinding::bindingOverwriting() @@ -311,14 +370,45 @@ void tst_qqmlbinding::bindingOverwriting() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("bindingOverwriting.qml")); - QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())}; QVERIFY(item); - delete item; QLoggingCategory::setFilterRules(QString()); QCOMPARE(messageHandler.messages().count(), 2); } +void tst_qqmlbinding::bindToQmlComponent() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindToQMLComponent.qml")); + QVERIFY(c.create()); +} + +// QTBUG-78943 +void tst_qqmlbinding::bindingDoesNoWeirdConversion() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("noUnexpectedStringConversion.qml")); + QScopedPointer<QObject> o {c.create()}; + QVERIFY(o); + QObject *colorRect = o->findChild<QObject*>("colorRect"); + QVERIFY(colorRect); + QCOMPARE(qvariant_cast<QColor>(colorRect->property("color")), QColorConstants::Red); + QObject *colorLabel = o->findChild<QObject*>("colorLabel"); + QCOMPARE(colorLabel->property("text").toString(), QLatin1String("red")); + QVERIFY(colorLabel); +} + +//QTBUG-72442 +void tst_qqmlbinding::bindNaNToInt() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("nanPropertyToInt.qml")); + QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(c.create())); + + QVERIFY(item != nullptr); + QCOMPARE(item->property("val").toInt(), 0); +} QTEST_MAIN(tst_qqmlbinding) #include "tst_qqmlbinding.moc" diff --git a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro index 9f854f1fa2..cdac5c0ff9 100644 --- a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro +++ b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro @@ -4,4 +4,4 @@ macx:CONFIG -= app_bundle SOURCES += tst_qqmlchangeset.cpp -QT += core-private gui-private qml-private testlib +QT += core-private gui-private qml-private testlib qmlmodels-private diff --git a/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml b/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml new file mode 100644 index 0000000000..0541c9b104 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml @@ -0,0 +1,9 @@ +import QtQuick 2.14 + +Item { + property int i + property bool b + property double d + property string s + property var nothing +} diff --git a/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml b/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml new file mode 100644 index 0000000000..acf08e94d2 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml @@ -0,0 +1,21 @@ +import QtQuick 2.14 + +Item { + property int i + property bool b + property double d + property string s + property var nothing + property url myurl + property color c + property font myfont + property date mydate + property point mypoint + property size mysize + property rect myrect + property matrix4x4 matrix + property quaternion quat + property vector2d vec2 + property vector3d vec3 + property vector4d vec4 +} diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index a872cece96..1d2fa42b75 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -35,7 +35,6 @@ #include <QtQuick> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickmousearea_p.h> -#include <private/qv8engine_p.h> #include <private/qqmlcontext_p.h> #include <private/qv4qmlcontext_p.h> #include <private/qv4scopedvalue_p.h> @@ -122,6 +121,7 @@ private slots: void relativeUrl_data(); void relativeUrl(); void setDataNoEngineNoSegfault(); + void testSetInitialProperties(); private: QQmlEngine engine; @@ -222,12 +222,12 @@ void tst_qqmlcomponent::qmlCreateObjectAutoParent() QVERIFY(window_item); QVERIFY(window_window); - QCOMPARE(qtobject_item->metaObject()->className(), "QQuickItem"); - QCOMPARE(qtobject_window->metaObject()->className(), "QQuickWindow"); - QCOMPARE(item_item->metaObject()->className(), "QQuickItem"); - QCOMPARE(item_window->metaObject()->className(), "QQuickWindow"); - QCOMPARE(window_item->metaObject()->className(), "QQuickItem"); - QCOMPARE(window_window->metaObject()->className(), "QQuickWindow"); + QVERIFY(QByteArray(qtobject_item->metaObject()->className()).startsWith("QQuickItem")); + QVERIFY(QByteArray(qtobject_window->metaObject()->className()).startsWith("QQuickWindow")); + QVERIFY(QByteArray(item_item->metaObject()->className()).startsWith("QQuickItem")); + QVERIFY(QByteArray(item_window->metaObject()->className()).startsWith("QQuickWindow")); + QVERIFY(QByteArray(window_item->metaObject()->className()).startsWith("QQuickItem")); + QVERIFY(QByteArray(window_window->metaObject()->className()).startsWith("QQuickWindow")); QCOMPARE(qtobject_qtobject->parent(), qtobjectParent); QCOMPARE(qtobject_item->parent(), qtobjectParent); @@ -637,9 +637,11 @@ void tst_qqmlcomponent::relativeUrl_data() { QTest::addColumn<QUrl>("url"); +#if !defined(Q_OS_ANDROID) QTest::addRow("fromLocalFile") << QUrl::fromLocalFile("data/QtObjectComponent.qml"); QTest::addRow("fromLocalFileHash") << QUrl::fromLocalFile("data/QtObjectComponent#2.qml"); QTest::addRow("constructor") << QUrl("data/QtObjectComponent.qml"); +#endif QTest::addRow("absolute") << QUrl::fromLocalFile(QFINDTESTDATA("data/QtObjectComponent.qml")); QTest::addRow("qrc") << QUrl("qrc:/data/QtObjectComponent.qml"); } @@ -666,6 +668,101 @@ void tst_qqmlcomponent::setDataNoEngineNoSegfault() QVERIFY(!c); } +void tst_qqmlcomponent::testSetInitialProperties() +{ + QQmlEngine eng; + { + // JSON based initialization + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("allJSONTypes.qml")); + QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) }; + QVERIFY(obj); + comp.setInitialProperties(obj.get(), QVariantMap { + {QLatin1String("i"), 42}, + {QLatin1String("b"), true}, + {QLatin1String("d"), 3.1416}, + {QLatin1String("s"), QLatin1String("hello world")}, + {QLatin1String("nothing"), QVariant::fromValue(nullptr)} + }); + comp.completeCreate(); + if (!comp.errors().empty()) + qDebug() << comp.errorString() << comp.errors(); + QVERIFY(comp.errors().empty()); + QCOMPARE(obj->property("i"), 42); + QCOMPARE(obj->property("b"), true); + QCOMPARE(obj->property("d"), 3.1416); + QCOMPARE(obj->property("s"), QLatin1String("hello world")); + QCOMPARE(obj->property("nothing"), QVariant::fromValue(nullptr)); + } + { + // QVariant + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("variantBasedInitialization.qml")); + QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) }; + QVERIFY(obj); + QUrl myurl = comp.url(); + QFont myfont; + QDateTime mydate = QDateTime::currentDateTime(); + QPoint mypoint {1,2}; + QSizeF mysize {0.5, 0.3}; + QMatrix4x4 matrix {}; + QQuaternion quat {5.0f, 0.3f, 0.2f, 0.1f}; + QVector2D vec2 {2.0f, 3.1f}; + QVector3D vec3 {1.0f, 2.0, 3.0f}; + QVector4D vec4 {1.0f, 2.0f, 3.0f, 4.0f}; +#define ASJSON(NAME) {QLatin1String(#NAME), NAME} + comp.setInitialProperties(obj.get(), QVariantMap { + {QLatin1String("i"), 42}, + {QLatin1String("b"), true}, + {QLatin1String("d"), 3.1416}, + {QLatin1String("s"), QLatin1String("hello world")}, + {QLatin1String("nothing"), QVariant::fromValue( nullptr)}, + ASJSON(myurl), + ASJSON(myfont), + ASJSON(mydate), + ASJSON(mypoint), + ASJSON(mysize), + ASJSON(matrix), + ASJSON(quat), + ASJSON(vec2), ASJSON(vec3), ASJSON(vec4) + }); +#undef ASJSON + comp.completeCreate(); + if (!comp.errors().empty()) + qDebug() << comp.errorString() << comp.errors(); + QVERIFY(comp.errors().empty()); + QCOMPARE(obj->property("i"), 42); + QCOMPARE(obj->property("b"), true); + QCOMPARE(obj->property("d"), 3.1416); + QCOMPARE(obj->property("s"), QLatin1String("hello world")); + QCOMPARE(obj->property("nothing"), QVariant::fromValue(nullptr)); +#define COMPARE(NAME) QCOMPARE(obj->property(#NAME), NAME) + COMPARE(myurl); + COMPARE(myfont); + COMPARE(mydate); + COMPARE(mypoint); + COMPARE(mysize); + COMPARE(matrix); + COMPARE(quat); + COMPARE(vec2); + COMPARE(vec3); + COMPARE(vec4); +#undef COMPARE + + } + { + // createWithInitialProperties: setting a nonexistent property + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("allJSONTypes.qml")); + QScopedPointer<QObject> obj { + comp.createWithInitialProperties(QVariantMap { {"notThePropertiesYoureLookingFor", 42} }) + }; + qDebug() << comp.errorString(); + QVERIFY(obj); + QVERIFY(comp.errorString().contains("Could not set property notThePropertiesYoureLookingFor")); + } +} + QTEST_MAIN(tst_qqmlcomponent) #include "tst_qqmlcomponent.moc" diff --git a/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-no-signal-name.qml index 462a9577ff..462a9577ff 100644 --- a/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-no-signal-name.qml diff --git a/tests/auto/qml/qqmlconnections/data/connection-targetchange.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-targetchange.qml index 154c309c9c..154c309c9c 100644 --- a/tests/auto/qml/qqmlconnections/data/connection-targetchange.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-targetchange.qml diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-ignored.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-ignored.qml index 0780dd1509..0780dd1509 100644 --- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-ignored.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-ignored.qml diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-notarget.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-notarget.qml index 3da3e0f5d1..3da3e0f5d1 100644 --- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-notarget.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-notarget.qml diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-parent.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-parent.qml index 2c55215579..2c55215579 100644 --- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-parent.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-parent.qml diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals.qml index a351016b4a..a351016b4a 100644 --- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals.qml diff --git a/tests/auto/qml/qqmlconnections/data/disabled-at-start.qml b/tests/auto/qml/qqmlconnections/data/bindings/disabled-at-start.qml index 1a823f87f6..1a823f87f6 100644 --- a/tests/auto/qml/qqmlconnections/data/disabled-at-start.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/disabled-at-start.qml diff --git a/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml b/tests/auto/qml/qqmlconnections/data/bindings/override-proxy-type.qml index 80e459966b..80e459966b 100644 --- a/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/override-proxy-type.qml diff --git a/tests/auto/qml/qqmlconnections/data/rewriteError-global.qml b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-global.qml index 1d0b557069..1d0b557069 100644 --- a/tests/auto/qml/qqmlconnections/data/rewriteError-global.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-global.qml diff --git a/tests/auto/qml/qqmlconnections/data/rewriteError-unnamed.qml b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-unnamed.qml index a4849e994b..a4849e994b 100644 --- a/tests/auto/qml/qqmlconnections/data/rewriteError-unnamed.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-unnamed.qml diff --git a/tests/auto/qml/qqmlconnections/data/singletontype-target.qml b/tests/auto/qml/qqmlconnections/data/bindings/singletontype-target.qml index 7de488c2dd..7de488c2dd 100644 --- a/tests/auto/qml/qqmlconnections/data/singletontype-target.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/singletontype-target.qml diff --git a/tests/auto/qml/qqmlconnections/data/test-connection-implicit.qml b/tests/auto/qml/qqmlconnections/data/bindings/test-connection-implicit.qml index d5aa0f102a..d5aa0f102a 100644 --- a/tests/auto/qml/qqmlconnections/data/test-connection-implicit.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/test-connection-implicit.qml diff --git a/tests/auto/qml/qqmlconnections/data/test-connection.qml b/tests/auto/qml/qqmlconnections/data/bindings/test-connection.qml index f44cbc047f..f44cbc047f 100644 --- a/tests/auto/qml/qqmlconnections/data/test-connection.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/test-connection.qml diff --git a/tests/auto/qml/qqmlconnections/data/trimming.qml b/tests/auto/qml/qqmlconnections/data/bindings/trimming.qml index 4c37eb22af..4c37eb22af 100644 --- a/tests/auto/qml/qqmlconnections/data/trimming.qml +++ b/tests/auto/qml/qqmlconnections/data/bindings/trimming.qml diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml new file mode 100644 index 0000000000..04cc36b3c5 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml @@ -0,0 +1,15 @@ +import QtQuick 2.4 + +Item { + id: blaBlaBla + function hint() { + } + + Connections { + //target: blaBlaBla + // function onHint() { hint() }; + on: true + } +} + + diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml new file mode 100644 index 0000000000..692194e837 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 + +Item { + Component { + id: item1 + Item { + objectName: "item1" + } + } + Component { + id: item2 + Item { + objectName: "item2" + } + } + Loader { + id: loader + sourceComponent: item1 + } + Connections { + objectName: "connections" + target: loader.item + function onWidthChanged() { loader.sourceComponent = item2 } + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml new file mode 100644 index 0000000000..f70d8cdb15 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml @@ -0,0 +1,17 @@ +import QtQml 2.0 + +QtObject { + id: root + + property Connections c1: Connections { + target: root; + function onNotFooBar1() {} + ignoreUnknownSignals: true + } + + property Connections c2: Connections { + objectName: "connections" + function onNotFooBar2() {} + ignoreUnknownSignals: true + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml new file mode 100644 index 0000000000..7658728dd9 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +QtObject { + property Connections c1: Connections { + objectName: "connections" + target: null + function onNotFooBar() {} + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml new file mode 100644 index 0000000000..ece76b0cf7 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + property Connections c1: Connections { + objectName: "connections" + function onFooBar() {} + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml new file mode 100644 index 0000000000..a198a724d0 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 + +QtObject { + id: screen + + property Connections c1: Connections { + objectName: "connections" + target: screen + function onFooBar() {} + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml b/tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml new file mode 100644 index 0000000000..981437fe8c --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml @@ -0,0 +1,14 @@ +import QtQuick 2.9 + +Item { + id: root + + property bool tested: false + signal testMe() + + Connections { + target: root + enabled: false + function onTestMe() { root.tested = true; } + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml b/tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml new file mode 100644 index 0000000000..b83f0baa11 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml @@ -0,0 +1,13 @@ +import QtQml 2.12 +import test.proxy 1.0 + +Proxy { + property int testEnum: 0; + id: proxy + property Connections connections: Connections { + target: proxy + function onSomeSignal() { testEnum = Proxy.EnumValue } + } + + Component.onCompleted: someSignal() +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml new file mode 100644 index 0000000000..de3154c431 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 +import Test 1.0 + +TestObject { + property QtObject connection: Connections { + function onSignalWithGlobalName() { ran = true } + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml new file mode 100644 index 0000000000..fa1d1b17d7 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Test 1.0 + +TestObject { + property QtObject connection: Connections { + function onUnnamedArgumentSignal() { ran = true } + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml b/tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml new file mode 100644 index 0000000000..935b610351 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml @@ -0,0 +1,22 @@ +import QtQml 2.0 +import MyTestSingletonType 1.0 as MyTestSingletonType + +QtObject { + id: rootObject + objectName: "rootObject" + property int newIntPropValue: 12 + + property int moduleIntPropChangedCount: 0 + property int moduleOtherSignalCount: 0 + + function setModuleIntProp() { + MyTestSingletonType.Api.intProp = newIntPropValue; + newIntPropValue = newIntPropValue + 1; + } + + property Connections c: Connections { + target: MyTestSingletonType.Api + function onIntPropChanged() { moduleIntPropChangedCount = moduleIntPropChangedCount + 1 } + function onOtherSignal() { moduleOtherSignalCount = moduleOtherSignalCount + 1 } + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml b/tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml new file mode 100644 index 0000000000..2ed5278636 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + width: 50 + + property bool tested: false + + Connections { function onWidthChanged() { tested = true } } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/test-connection.qml b/tests/auto/qml/qqmlconnections/data/functions/test-connection.qml new file mode 100644 index 0000000000..c706797ea4 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/test-connection.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + id: screen; width: 50 + + property bool tested: false + signal testMe + + Connections { + objectName: "connections" + target: screen; + function onWidthChanged() { screen.tested = true } + } +} diff --git a/tests/auto/qml/qqmlconnections/data/functions/trimming.qml b/tests/auto/qml/qqmlconnections/data/functions/trimming.qml new file mode 100644 index 0000000000..7dfd673539 --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/functions/trimming.qml @@ -0,0 +1,13 @@ +import QtQml 2.0 + +QtObject { + id: root + + property string tested + signal testMe(int param1, string param2) + + property Connections c: Connections { + target: root + function onTestMe(param1, param2) { root.tested = param2 + param1 } + } +} diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp index dc29363fcf..07af519a3d 100644 --- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp +++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp @@ -42,29 +42,57 @@ public: private slots: void defaultValues(); void properties(); + + void connection_data() { prefixes(); } void connection(); + + void trimming_data() { prefixes(); } void trimming(); + + void targetChanged_data() { prefixes(); }; void targetChanged(); + void unknownSignals_data(); void unknownSignals(); + void errors_data(); void errors(); + + void rewriteErrors_data() { prefixes(); } void rewriteErrors(); + + void singletonTypeTarget_data() { prefixes(); } void singletonTypeTarget(); + + void enableDisable_QTBUG_36350_data() { prefixes(); } void enableDisable_QTBUG_36350(); + + void disabledAtStart_data() { prefixes(); } void disabledAtStart(); + + void clearImplicitTarget_data() { prefixes(); } void clearImplicitTarget(); void onWithoutASignal(); + + void noAcceleratedGlobalLookup_data() { prefixes(); } void noAcceleratedGlobalLookup(); private: QQmlEngine engine; + void prefixes(); }; tst_qqmlconnections::tst_qqmlconnections() { } +void tst_qqmlconnections::prefixes() +{ + QTest::addColumn<QString>("prefix"); + QTest::newRow("functions") << QString("functions"); + QTest::newRow("bindings") << QString("bindings"); +} + void tst_qqmlconnections::defaultValues() { QQmlEngine engine; @@ -93,8 +121,9 @@ void tst_qqmlconnections::properties() void tst_qqmlconnections::connection() { + QFETCH(QString, prefix); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("test-connection.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); QVERIFY(item != nullptr); @@ -110,8 +139,9 @@ void tst_qqmlconnections::connection() void tst_qqmlconnections::trimming() { + QFETCH(QString, prefix); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("trimming.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/trimming.qml")); QObject *object = c.create(); QVERIFY(object != nullptr); @@ -131,8 +161,9 @@ void tst_qqmlconnections::trimming() // Confirm that target can be changed by one of our signal handlers void tst_qqmlconnections::targetChanged() { + QFETCH(QString, prefix); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("connection-targetchange.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/connection-targetchange.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); QVERIFY(item != nullptr); @@ -158,10 +189,15 @@ void tst_qqmlconnections::unknownSignals_data() QTest::addColumn<QString>("file"); QTest::addColumn<QString>("error"); - QTest::newRow("basic") << "connection-unknownsignals.qml" << ":6:30: QML Connections: Cannot assign to non-existent property \"onFooBar\""; - QTest::newRow("parent") << "connection-unknownsignals-parent.qml" << ":4:30: QML Connections: Cannot assign to non-existent property \"onFooBar\""; - QTest::newRow("ignored") << "connection-unknownsignals-ignored.qml" << ""; // should be NO error - QTest::newRow("notarget") << "connection-unknownsignals-notarget.qml" << ""; // should be NO error + QTest::newRow("functions/basic") << "functions/connection-unknownsignals.qml" << ":6:30: QML Connections: Detected function \"onFooBar\" in Connections element. This is probably intended to be a signal handler but no signal of the target matches the name."; + QTest::newRow("functions/parent") << "functions/connection-unknownsignals-parent.qml" << ":4:30: QML Connections: Detected function \"onFooBar\" in Connections element. This is probably intended to be a signal handler but no signal of the target matches the name."; + QTest::newRow("functions/ignored") << "functions/connection-unknownsignals-ignored.qml" << ""; // should be NO error + QTest::newRow("functions/notarget") << "functions/connection-unknownsignals-notarget.qml" << ""; // should be NO error + + QTest::newRow("bindings/basic") << "bindings/connection-unknownsignals.qml" << ":6:30: QML Connections: Cannot assign to non-existent property \"onFooBar\""; + QTest::newRow("bindings/parent") << "bindings/connection-unknownsignals-parent.qml" << ":4:30: QML Connections: Cannot assign to non-existent property \"onFooBar\""; + QTest::newRow("bindings/ignored") << "bindings/connection-unknownsignals-ignored.qml" << ""; // should be NO error + QTest::newRow("bindings/notarget") << "bindings/connection-unknownsignals-notarget.qml" << ""; // should be NO error } void tst_qqmlconnections::unknownSignals() @@ -239,10 +275,11 @@ private: void tst_qqmlconnections::rewriteErrors() { + QFETCH(QString, prefix); qmlRegisterType<TestObject>("Test", 1, 0, "TestObject"); { QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("rewriteError-unnamed.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-unnamed.qml")); QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal uses unnamed parameter followed by named parameter.").toLatin1()); TestObject *obj = qobject_cast<TestObject*>(c.create()); QVERIFY(obj != nullptr); @@ -254,7 +291,7 @@ void tst_qqmlconnections::rewriteErrors() { QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("rewriteError-global.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-global.qml")); QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal parameter \"parseInt\" hides global variable.").toLatin1()); TestObject *obj = qobject_cast<TestObject*>(c.create()); QVERIFY(obj != nullptr); @@ -305,8 +342,9 @@ static QObject *module_api_factory(QQmlEngine *engine, QJSEngine *scriptEngine) // QTBUG-20937 void tst_qqmlconnections::singletonTypeTarget() { + QFETCH(QString, prefix); qmlRegisterSingletonType<MyTestSingletonType>("MyTestSingletonType", 1, 0, "Api", module_api_factory); - QQmlComponent component(&engine, testFileUrl("singletontype-target.qml")); + QQmlComponent component(&engine, testFileUrl(prefix + "/singletontype-target.qml")); QObject *object = component.create(); QVERIFY(object != nullptr); @@ -331,8 +369,9 @@ void tst_qqmlconnections::singletonTypeTarget() void tst_qqmlconnections::enableDisable_QTBUG_36350() { + QFETCH(QString, prefix); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("test-connection.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); QVERIFY(item != nullptr); @@ -358,8 +397,9 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350() void tst_qqmlconnections::disabledAtStart() { + QFETCH(QString, prefix); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("disabled-at-start.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/disabled-at-start.qml")); QObject * const object = c.create(); QVERIFY(object != nullptr); @@ -376,8 +416,9 @@ void tst_qqmlconnections::disabledAtStart() //QTBUG-56499 void tst_qqmlconnections::clearImplicitTarget() { + QFETCH(QString, prefix); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("test-connection-implicit.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection-implicit.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); QVERIFY(item != nullptr); @@ -421,14 +462,15 @@ signals: void tst_qqmlconnections::noAcceleratedGlobalLookup() { + QFETCH(QString, prefix); qRegisterMetaType<Proxy::MyEnum>(); qmlRegisterType<Proxy>("test.proxy", 1, 0, "Proxy"); QQmlEngine engine; - QQmlComponent c(&engine, testFileUrl("override-proxy-type.qml")); + QQmlComponent c(&engine, testFileUrl(prefix + "/override-proxy-type.qml")); QVERIFY(c.isReady()); QScopedPointer<QObject> object(c.create()); const QVariant val = object->property("testEnum"); - QCOMPARE(val.type(), QMetaType::Int); + QCOMPARE(val.type(), QVariant::Int); QCOMPARE(val.toInt(), int(Proxy::EnumValue)); } diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index cb4bee0d3a..6754f22049 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -71,6 +71,7 @@ private slots: void outerContextObject(); void contextObjectHierarchy(); + void destroyContextProperty(); private: QQmlEngine engine; @@ -892,6 +893,31 @@ void tst_qqmlcontext::contextObjectHierarchy() }); } +void tst_qqmlcontext::destroyContextProperty() +{ + QScopedPointer<QQmlContext> context; + QScopedPointer<QObject> objectThatOutlivesEngine(new QObject); + { + QQmlEngine engine; + context.reset(new QQmlContext(&engine)); + + { + QObject object; + context->setContextProperty(QLatin1String("a"), &object); + QCOMPARE(qvariant_cast<QObject *>(context->contextProperty(QLatin1String("a"))), &object); + } + + QCOMPARE(qvariant_cast<QObject *>(context->contextProperty(QLatin1String("a"))), nullptr); + context->setContextProperty(QLatin1String("b"), objectThatOutlivesEngine.data()); + } + + // dropDestroyedObject() should not crash, even if the engine is gone. + objectThatOutlivesEngine.reset(); + + // We're not allowed to call context->contextProperty("b") anymore. + // TODO: Or are we? +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" diff --git a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp index 99cabb4b09..13e4d4c53b 100644 --- a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp +++ b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp @@ -71,7 +71,7 @@ void tst_qqmlcpputils::fastConnect() { MyObject obj; - qmlobject_connect(&obj, MyObject, SIGNAL(signal1()), &obj, MyObject, SLOT(slot1())) + qmlobject_connect(&obj, MyObject, SIGNAL(signal1()), &obj, MyObject, SLOT(slot1())); obj.signal1(); QCOMPARE(obj.slotCount, 1); diff --git a/tests/auto/qml/qqmldirparser/qqmldirparser.pro b/tests/auto/qml/qqmldirparser/qqmldirparser.pro index dda74b1ef9..b5373a6e8f 100644 --- a/tests/auto/qml/qqmldirparser/qqmldirparser.pro +++ b/tests/auto/qml/qqmldirparser/qqmldirparser.pro @@ -6,3 +6,5 @@ macx:CONFIG -= app_bundle SOURCES += tst_qqmldirparser.cpp include (../../shared/util.pri) + +TESTDATA = data/* diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp index 3643ca65c6..1e690e38dd 100644 --- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp +++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp @@ -32,6 +32,7 @@ #include <QObject> #include <QQmlEngine> #include <QQmlComponent> +#include <private/qqmljsdiagnosticmessage_p.h> #include <private/qqmldirparser_p.h> #include <QDebug> @@ -56,12 +57,21 @@ tst_qqmldirparser::tst_qqmldirparser() namespace { - QStringList toStringList(const QList<QQmlError> &errors) + QStringList toStringList(const QList<QQmlJS::DiagnosticMessage> &errors) { QStringList rv; - foreach (const QQmlError &e, errors) - rv.append(e.toString()); + for (const QQmlJS::DiagnosticMessage &e : errors) { + QString errorString = QLatin1String("qmldir"); + if (e.line > 0) { + errorString += QLatin1Char(':') + QString::number(e.line); + if (e.column > 0) + errorString += QLatin1Char(':') + QString::number(e.column); + } + + errorString += QLatin1String(": ") + e.message; + rv.append(errorString); + } return rv; } diff --git a/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml new file mode 100644 index 0000000000..66bb642f34 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml @@ -0,0 +1,22 @@ +import QtQml 2.12 + +QtObject { + id: root + property bool test1: false; + property bool test2: false; + property bool test3: false; + property bool done: false; + function *gen() { + yield 1 + yield 2 + yield 3 + } + + Component.onCompleted: { + let it = root.gen(); + root.test1 = (it.next().value == 1); + root.test2 = (it.next().value == 2); + root.test3 = (it.next().value == 3); + root.done = it.next().done; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml new file mode 100644 index 0000000000..b22f8ab71e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + regularExpression: "[a-zA-z]" +} diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml new file mode 100644 index 0000000000..6f31ffd305 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + regularExpression: /[a-zA-z]/ +} diff --git a/tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml b/tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml new file mode 100644 index 0000000000..0a75ea6f36 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 + +QtObject { + property var field: { "key": "value"}; + property list<QtObject> mylist: [ + QtObject {id: a}, + QtObject {id: b} + ]; + property var object: QtObject {}; +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml b/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml index 54d29dfc94..9fdb7f92f3 100644 --- a/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml +++ b/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml @@ -1,4 +1,5 @@ import Qt.test 1.0 +import QtQuick 2.0 // We need the the QtQuick color provider for colorProperty MyQmlObject { diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index 4547a74470..3233e7f105 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -33,6 +33,7 @@ #include <QtQml/qqmlexpression.h> #include <QtCore/qpoint.h> #include <QtCore/qsize.h> +#include <QtCore/qregularexpression.h> #include <QtQml/qqmllist.h> #include <QtCore/qrect.h> #include <QtGui/qmatrix.h> @@ -49,7 +50,6 @@ #include <QtQml/qqmlcomponent.h> #include <private/qqmlengine_p.h> -#include <private/qv8engine_p.h> #include <private/qv4qobjectwrapper_p.h> class MyQmlAttachedObject : public QObject @@ -101,6 +101,7 @@ class MyQmlObject : public QObject Q_PROPERTY(QQmlListProperty<QObject> objectListProperty READ objectListProperty CONSTANT) Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty) Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp) + Q_PROPERTY(QRegularExpression regularExpression READ regularExpression WRITE setRegularExpression) Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false) Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged) Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged) @@ -170,6 +171,12 @@ public: QRegExp regExp() { return m_regExp; } void setRegExp(const QRegExp ®Exp) { m_regExp = regExp; } + QRegularExpression regularExpression() { return m_regularExpression; } + void setRegularExpression(const QRegularExpression ®ularExpression) + { + m_regularExpression = regularExpression; + } + int console() const { return 11; } int nonscriptable() const { return 0; } @@ -270,6 +277,7 @@ private: int m_value; int m_resetProperty; QRegExp m_regExp; + QRegularExpression m_regularExpression; QVariant m_variant; QJSValue m_qjsvalue; int m_intProperty; @@ -788,11 +796,11 @@ public: Q_INVOKABLE void method_real(qreal a) { invoke(10); m_actuals << a; } Q_INVOKABLE void method_QString(QString a) { invoke(11); m_actuals << a; } Q_INVOKABLE void method_QPointF(QPointF a) { invoke(12); m_actuals << a; } - Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << qVariantFromValue(a); } - Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << qVariantFromValue(a); } - Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); } + Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << QVariant::fromValue(a); } + Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << QVariant::fromValue(a); } + Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << QVariant::fromValue(b); } Q_INVOKABLE void method_QByteArray(QByteArray value) { invoke(29); m_actuals << value; } - Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << qVariantFromValue(b); return b.call(); } + Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << QVariant::fromValue(b); return b.call(); } Q_INVOKABLE QJSValue method_intQJSValue(int a, int b) { m_actuals << a << b; return QJSValue();} // Should never be called. Q_INVOKABLE void method_overload(int a) { invoke(16); m_actuals << a; } @@ -1090,13 +1098,11 @@ class MyItemUsingRevisionedObject : public QObject Q_PROPERTY(MyRevisionedClass *revisioned READ revisioned) public: - MyItemUsingRevisionedObject() { - m_revisioned = new MyRevisionedClass; - } + MyItemUsingRevisionedObject() : m_revisioned (new MyRevisionedClass) {} - MyRevisionedClass *revisioned() const { return m_revisioned; } + MyRevisionedClass *revisioned() const { return m_revisioned.get(); } private: - MyRevisionedClass *m_revisioned; + QScopedPointer<MyRevisionedClass> m_revisioned; }; QML_DECLARE_TYPE(MyRevisionedBaseClassRegistered) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 7a9c611269..8824dfd019 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -47,6 +47,7 @@ #include <private/qv4alloca_p.h> #include <private/qv4runtime_p.h> #include <private/qv4object_p.h> +#include <private/qv4script_p.h> #include <private/qqmlcomponentattached_p.h> #include <private/qv4objectiterator_p.h> #include <private/qqmlabstractbinding_p.h> @@ -236,6 +237,7 @@ private slots: void functionAssignment_afterBinding(); void eval(); void function(); + void topLevelGeneratorFunction(); void qtbug_10696(); void qtbug_11606(); void qtbug_11600(); @@ -376,6 +378,7 @@ private slots: void hugeRegexpQuantifiers(); void singletonTypeWrapperLookup(); void getThisObject(); + void semicolonAfterProperty(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -1770,10 +1773,10 @@ void tst_qqmlecmascript::componentCreation() } QQmlComponent component(&engine, testUrl); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); QVERIFY(object != nullptr); - QMetaObject::invokeMethod(object, method.toUtf8()); + QMetaObject::invokeMethod(object.get(), method.toUtf8()); QQmlComponent *created = object->componentProperty(); if (creationError.isEmpty()) { @@ -1781,7 +1784,7 @@ void tst_qqmlecmascript::componentCreation() QObject *expectedParent = reinterpret_cast<QObject *>(quintptr(-1)); if (createdParent == QLatin1String("obj")) { - expectedParent = object; + expectedParent = object.get(); } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) { expectedParent = nullptr; } @@ -2506,6 +2509,13 @@ void tst_qqmlecmascript::regExpBug() delete object; } + { + QQmlComponent component(&engine, testFileUrl("regularExpression.qml")); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); + QVERIFY(!object.isNull()); + QCOMPARE(object->regularExpression().pattern(), QLatin1String("[a-zA-z]")); + } + //QTBUG-23068 { QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString()); @@ -2515,6 +2525,18 @@ void tst_qqmlecmascript::regExpBug() QVERIFY(!object); QCOMPARE(component.errorString(), err); } + + { + const QString err = QString::fromLatin1("%1:6 Invalid property assignment: " + "regular expression expected; " + "use /pattern/ syntax\n") + .arg(testFileUrl("regularExpression.2.qml").toString()); + QQmlComponent component(&engine, testFileUrl("regularExpression.2.qml")); + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(!object); + QCOMPARE(component.errorString(), err); + } } static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source) @@ -2569,7 +2591,7 @@ static inline bool evaluate_value(QV4::ExecutionEngine *v4, const QV4::Value &o, scope.engine->catchException(); return false; } - return QV4::Runtime::method_strictEqual(value, result); + return QV4::Runtime::StrictEqual::call(value, result); } static inline QV4::ReturnedValue evaluate(QV4::ExecutionEngine *v4, const QV4::Value &o, @@ -2899,35 +2921,35 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); + QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); + QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); + QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); + QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)o)); + QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)o)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", QV4::Primitive::undefinedValue())); @@ -3182,13 +3204,13 @@ void tst_qqmlecmascript::callQtInvokables() void tst_qqmlecmascript::resolveClashingProperties() { - ClashingNames *o = new ClashingNames(); + QScopedPointer<ClashingNames> o(new ClashingNames()); QQmlEngine qmlengine; QV4::ExecutionEngine *engine = qmlengine.handle(); QV4::Scope scope(engine); - QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o)); + QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o.get())); QV4::ObjectIterator it(scope, object->as<QV4::Object>(), QV4::ObjectIterator::EnumerableOnly); QV4::ScopedValue name(scope); QV4::ScopedValue value(scope); @@ -3203,7 +3225,7 @@ void tst_qqmlecmascript::resolveClashingProperties() QString key = name->toQStringNoThrow(); if (key == QLatin1String("clashes")) { value = v; - QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value)); + QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value)); QString type = typeString->toQStringNoThrow(); if (type == QLatin1String("boolean")) { QVERIFY(!seenProperty); @@ -6151,7 +6173,7 @@ void tst_qqmlecmascript::variants() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("variants.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid); @@ -6160,13 +6182,13 @@ void tst_qqmlecmascript::variants() QCOMPARE(object->property("doubleVariant").type(), QVariant::Double); QVariant result; - QMetaObject::invokeMethod(object, "checkNull", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(object.get(), "checkNull", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - QMetaObject::invokeMethod(object, "checkUndefined", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(object.get(), "checkUndefined", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - QMetaObject::invokeMethod(object, "checkNumber", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(object.get(), "checkNumber", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); } @@ -6408,6 +6430,28 @@ void tst_qqmlecmascript::function() delete o; } +// QTBUG-77096 +void tst_qqmlecmascript::topLevelGeneratorFunction() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("generatorFunction.qml")); + + QScopedPointer<QObject> o {component.create()}; + QVERIFY(o != nullptr); + + // check that generator works correctly in QML + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("done").toBool(), true); + + // check that generator is accessible from C++ + QVariant returnedValue; + QMetaObject::invokeMethod(o.get(), "gen", Q_RETURN_ARG(QVariant, returnedValue)); + auto it = returnedValue.value<QJSValue>(); + QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1); +} + // Test the "Qt.include" method void tst_qqmlecmascript::include() { @@ -7034,12 +7078,12 @@ void tst_qqmlecmascript::realToInt() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("realToInt.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); - QMetaObject::invokeMethod(object, "test1"); + QMetaObject::invokeMethod(object.get(), "test1"); QCOMPARE(object->value(), int(4)); - QMetaObject::invokeMethod(object, "test2"); + QMetaObject::invokeMethod(object.get(), "test2"); QCOMPARE(object->value(), int(7)); } @@ -7048,7 +7092,7 @@ void tst_qqmlecmascript::urlProperty() QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); object->setStringProperty("http://qt-project.org"); QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html")); @@ -7063,7 +7107,7 @@ void tst_qqmlecmascript::urlPropertyWithEncoding() QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); object->setStringProperty("http://qt-project.org"); const QUrl encoded = QUrl::fromEncoded("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); @@ -7098,7 +7142,7 @@ void tst_qqmlecmascript::dynamicString() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("dynamicString.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("stringProperty").toString(), QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!")); @@ -7108,7 +7152,7 @@ void tst_qqmlecmascript::deleteLaterObjectMethodCall() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -7116,7 +7160,7 @@ void tst_qqmlecmascript::automaticSemicolon() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -7124,7 +7168,7 @@ void tst_qqmlecmascript::compatibilitySemicolon() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("compatibilitySemicolon.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -7132,7 +7176,7 @@ void tst_qqmlecmascript::incrDecrSemicolon1() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon1.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -7140,7 +7184,7 @@ void tst_qqmlecmascript::incrDecrSemicolon2() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon2.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -7156,7 +7200,7 @@ void tst_qqmlecmascript::unaryExpression() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("unaryExpression.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -7296,7 +7340,7 @@ void tst_qqmlecmascript::switchStatement() QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7319,7 +7363,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7342,7 +7386,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7369,7 +7413,7 @@ void tst_qqmlecmascript::switchStatement() QString warning = component.url().toString() + ":4:5: Unable to assign [undefined] to int"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7393,7 +7437,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7416,7 +7460,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7444,7 +7488,7 @@ void tst_qqmlecmascript::withStatement() { QUrl url = testFileUrl("withStatement.1.qml"); QQmlComponent component(&engine, url); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->value(), 123); @@ -7456,7 +7500,7 @@ void tst_qqmlecmascript::tryStatement() QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->value(), 123); @@ -7464,7 +7508,7 @@ void tst_qqmlecmascript::tryStatement() { QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->value(), 321); @@ -7472,7 +7516,7 @@ void tst_qqmlecmascript::tryStatement() { QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); QVERIFY(object->qjsvalue().isUndefined()); @@ -7480,7 +7524,7 @@ void tst_qqmlecmascript::tryStatement() { QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); QVERIFY(object != nullptr); QVERIFY(object->qjsvalue().isUndefined()); @@ -7621,7 +7665,7 @@ void tst_qqmlecmascript::onDestruction() // component instance. This shouldn't crash. QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("onDestruction.qml")); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); QVERIFY(obj != nullptr); QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } @@ -7962,19 +8006,19 @@ void tst_qqmlecmascript::dateParse() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("date.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); if (object == nullptr) qDebug() << component.errorString(); QVERIFY(object != nullptr); QVariant q; - QMetaObject::invokeMethod(object, "test_is_invalid_jsDateTime", Q_RETURN_ARG(QVariant, q)); + QMetaObject::invokeMethod(object.get(), "test_is_invalid_jsDateTime", Q_RETURN_ARG(QVariant, q)); QVERIFY(q.toBool()); - QMetaObject::invokeMethod(object, "test_is_invalid_qtDateTime", Q_RETURN_ARG(QVariant, q)); + QMetaObject::invokeMethod(object.get(), "test_is_invalid_qtDateTime", Q_RETURN_ARG(QVariant, q)); QVERIFY(q.toBool()); - QMetaObject::invokeMethod(object, "test_rfc2822_date", Q_RETURN_ARG(QVariant, q)); + QMetaObject::invokeMethod(object.get(), "test_rfc2822_date", Q_RETURN_ARG(QVariant, q)); QCOMPARE(q.toLongLong(), 1379512851000LL); } @@ -7983,14 +8027,14 @@ void tst_qqmlecmascript::utcDate() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("utcdate.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); if (object == nullptr) qDebug() << component.errorString(); QVERIFY(object != nullptr); QVariant q; QVariant val = QString::fromLatin1("2014-07-16T23:30:31"); - QMetaObject::invokeMethod(object, "check_utc", Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, val)); + QMetaObject::invokeMethod(object.get(), "check_utc", Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, val)); QVERIFY(q.toBool()); } @@ -7999,20 +8043,20 @@ void tst_qqmlecmascript::negativeYear() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("negativeyear.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); if (object == nullptr) qDebug() << component.errorString(); QVERIFY(object != nullptr); QVariant q; - QMetaObject::invokeMethod(object, "check_negative_tostring", Q_RETURN_ARG(QVariant, q)); + QMetaObject::invokeMethod(object.get(), "check_negative_tostring", Q_RETURN_ARG(QVariant, q)); // Only check for the year. We hope that every language writes the year in arabic numerals and // in relation to a specific dude's date of birth. We also hope that no language adds a "-2001" // junk string somewhere in the middle. QVERIFY(q.toString().indexOf(QStringLiteral("-2001")) != -1); - QMetaObject::invokeMethod(object, "check_negative_toisostring", Q_RETURN_ARG(QVariant, q)); + QMetaObject::invokeMethod(object.get(), "check_negative_toisostring", Q_RETURN_ARG(QVariant, q)); QCOMPARE(q.toString().left(16), QStringLiteral("result: -002000-")); } @@ -8055,6 +8099,7 @@ void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy() QCOMPARE(spy1.count(), 1); QCOMPARE(spy2.count(), 1); + deleteObject.deleteNestedObject(); delete object; } @@ -8066,7 +8111,7 @@ void tst_qqmlecmascript::updateCall() QString file("updateCall.qml"); QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(file)); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } @@ -8077,7 +8122,7 @@ void tst_qqmlecmascript::numberParsing() QString file("numberParsing.%1.qml"); file = file.arg(i); QQmlComponent component(&engine, testFileUrl(file)); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); } for (int i = 1; i < 3; ++i) { @@ -8260,8 +8305,8 @@ void tst_qqmlecmascript::idsAsLValues() QString err = QString(QLatin1String("%1:5: Error: left-hand side of assignment operator is not an lvalue")).arg(testFileUrl("idAsLValue.qml").toString()); QQmlComponent component(&engine, testFileUrl("idAsLValue.qml")); QTest::ignoreMessage(QtWarningMsg, qPrintable(err)); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(!object); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!qobject_cast<MyQmlObject*>(object.get())); } void tst_qqmlecmascript::qtbug_34792() @@ -8334,7 +8379,9 @@ void tst_qqmlecmascript::varPropertyAccessOnObjectWithInvalidContext() void tst_qqmlecmascript::importedScriptsAccessOnObjectWithInvalidContext() { QQmlEngine engine; - QQmlComponent component(&engine, testFileUrl("importedScriptsAccessOnObjectWithInvalidContext.qml")); + const QUrl url = testFileUrl("importedScriptsAccessOnObjectWithInvalidContext.qml"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + ":29: TypeError: Cannot read property 'Foo' of null")); + QQmlComponent component(&engine, url); QScopedPointer<QObject> obj(component.create()); if (obj.isNull()) qDebug() << component.errors().first().toString(); @@ -8474,10 +8521,10 @@ void tst_qqmlecmascript::readUnregisteredQObjectProperty() qmlRegisterType<ObjectContainer>("Test", 1, 0, "ObjectContainer"); QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("accessUnregisteredQObjectProperty.qml")); - QObject *root = component.create(); + QScopedPointer<QObject> root(component.create()); QVERIFY(root); - QMetaObject::invokeMethod(root, "readProperty"); + QMetaObject::invokeMethod(root.get(), "readProperty"); QCOMPARE(root->property("container").value<ObjectContainer*>()->mGetterCalled, true); } @@ -8486,10 +8533,10 @@ void tst_qqmlecmascript::writeUnregisteredQObjectProperty() qmlRegisterType<ObjectContainer>("Test", 1, 0, "ObjectContainer"); QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("accessUnregisteredQObjectProperty.qml")); - QObject *root = component.create(); + QScopedPointer<QObject> root(component.create()); QVERIFY(root); - QMetaObject::invokeMethod(root, "writeProperty"); + QMetaObject::invokeMethod(root.get(), "writeProperty"); QCOMPARE(root->property("container").value<ObjectContainer*>()->mSetterCalled, true); } @@ -9112,8 +9159,8 @@ void tst_qqmlecmascript::singletonTypeWrapperLookup() }); auto cleanup = qScopeGuard([&]() { - qmlUnregisterType(singletonTypeId1); - qmlUnregisterType(singletonTypeId2); + QQmlMetaType::unregisterType(singletonTypeId1); + QQmlMetaType::unregisterType(singletonTypeId2); }); QQmlComponent component(&engine, testFileUrl("SingletonLookupTest.qml")); @@ -9141,6 +9188,16 @@ void tst_qqmlecmascript::getThisObject() QTRY_COMPARE(qvariant_cast<QObject *>(test->property("self")), test.data()); } +// QTBUG-77954 +void tst_qqmlecmascript::semicolonAfterProperty() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("semicolonAfterProperty.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> test(component.create()); + QVERIFY(!test.isNull()); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmlengine/qqmlengine.pro b/tests/auto/qml/qqmlengine/qqmlengine.pro index d2eb92bfd5..8946201ea0 100644 --- a/tests/auto/qml/qqmlengine/qqmlengine.pro +++ b/tests/auto/qml/qqmlengine/qqmlengine.pro @@ -4,6 +4,8 @@ macx:CONFIG -= app_bundle include (../../shared/util.pri) +TESTDATA = data/* + SOURCES += tst_qqmlengine.cpp QT += core-private gui-private qml-private network testlib diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 0cb6753020..64f167b47e 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -43,6 +43,7 @@ #include <QQmlIncubationController> #include <QTemporaryDir> #include <private/qqmlengine_p.h> +#include <private/qqmltypedata_p.h> #include <QQmlAbstractUrlInterceptor> class tst_qqmlengine : public QQmlDataTest @@ -427,7 +428,7 @@ void tst_qqmlengine::trimComponentCache() engine.setIncubationController(&componentCache); QQmlComponent component(&engine, testFileUrl(file)); - QVERIFY(component.isReady()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); @@ -741,13 +742,17 @@ public: CustomSelector(const QUrl &base):m_base(base){} virtual QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType d) { - if (url.scheme() != QStringLiteral("file")) + if ((url.scheme() != QStringLiteral("file") && url.scheme() != QStringLiteral("qrc")) + || url.path().contains("QtQml")) return url; if (!m_interceptionPoints.contains(d)) return url; - if (url.path().endsWith("Test.2/qmldir"))//Special case - return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir"); + if (url.path().endsWith("Test.2/qmldir")) {//Special case + QUrl url = m_base; + url.setPath(m_base.path() + "interception/module/intercepted/qmldir"); + return url; + } // Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now if (url.path().endsWith("intercepted/qmldir")) return url; @@ -835,7 +840,7 @@ void tst_qqmlengine::urlInterceptor() QFETCH(QString, expectedAbsoluteUrl); QQmlEngine e; - e.setImportPathList(QStringList() << testFileUrl("interception/imports").toLocalFile()); + e.addImportPath(testFileUrl("interception/imports").url()); CustomSelector cs(testFileUrl("")); cs.m_interceptionPoints = interceptionPoint; e.setUrlInterceptor(&cs); @@ -935,7 +940,7 @@ void tst_qqmlengine::cppSignalAndEval() { ObjectCaller objectCaller; QQmlEngine engine; - engine.rootContext()->setContextProperty(QLatin1Literal("CallerCpp"), &objectCaller); + engine.rootContext()->setContextProperty(QLatin1String("CallerCpp"), &objectCaller); QQmlComponent c(&engine); c.setData("import QtQuick 2.9\n" "Item {\n" @@ -1015,6 +1020,65 @@ void tst_qqmlengine::singletonInstance() } { + int data = 30; + auto id = qmlRegisterSingletonType<CppSingleton>("Qt.test",1,0,"CapturingLambda",[data](QQmlEngine*, QJSEngine*){ // register qobject singleton with capturing lambda + auto o = new CppSingleton; + o->setProperty("data", data); + return o; + }); + QJSValue value = engine.singletonInstance<QJSValue>(id); + QVERIFY(!value.isUndefined()); + QVERIFY(value.isQObject()); + QObject *instance = value.toQObject(); + QVERIFY(instance); + QCOMPARE(instance->metaObject()->className(), "CppSingleton"); + QCOMPARE(instance->property("data"), data); + } + { + qmlRegisterSingletonType<CppSingleton>("Qt.test",1,0,"NotAmbiguous", [](QQmlEngine* qeng, QJSEngine* jeng) -> QObject* {return CppSingleton::create(qeng, jeng);}); // test that overloads for qmlRegisterSingleton are not ambiguous + } + { + // Register QObject* directly + CppSingleton single; + int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", + &single); + QQmlEngine engine2; + CppSingleton *singlePtr = engine2.singletonInstance<CppSingleton *>(id); + QVERIFY(singlePtr); + QCOMPARE(&single, singlePtr); + QVERIFY(engine2.objectOwnership(singlePtr) == QQmlEngine::CppOwnership); + } + + { + CppSingleton single; + QQmlEngine engineA; + QQmlEngine engineB; + int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single); + auto singlePtr = engineA.singletonInstance<CppSingleton *>(id); + QVERIFY(singlePtr); + singlePtr = engineA.singletonInstance<CppSingleton *>(id); // accessing the singleton multiple times from the same engine is fine + QVERIFY(singlePtr); + QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer."); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Singleton registered by registerSingletonInstance must only be accessed from one engine"); + QCOMPARE(&single, singlePtr); + auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id); + QVERIFY(!noSinglePtr); + } + + { + CppSingleton single; + QThread newThread {}; + single.moveToThread(&newThread); + QCOMPARE(single.thread(), &newThread); + QQmlEngine engineB; + int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single); + QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer."); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Registered object must live in the same thread as the engine it was registered with"); + auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id); + QVERIFY(!noSinglePtr); + } + + { // Invalid types QJSValue value; value = engine.singletonInstance<QJSValue>(-4711); @@ -1043,6 +1107,16 @@ void tst_qqmlengine::singletonInstance() SomeQObjectClass * instance = engine.singletonInstance<SomeQObjectClass*>(cppSingletonTypeId); QVERIFY(!instance); } + + { + // deleted object + auto dayfly = new QObject{}; + auto id = qmlRegisterSingletonInstance("Vanity", 1, 0, "Dayfly", dayfly); + delete dayfly; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: The registered singleton has already been deleted. Ensure that it outlives the engine."); + QObject *instance = engine.singletonInstance<QObject*>(id); + QVERIFY(!instance); + } } void tst_qqmlengine::aggressiveGc() diff --git a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp index 7a7185e909..f282e60417 100644 --- a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp +++ b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp @@ -195,7 +195,7 @@ void tst_qqmlerror::debug() } { - QUrl url(dataDirectoryUrl().resolved(QUrl("test.txt"))); + QUrl url = testFileUrl("test.txt"); QQmlError error; error.setUrl(url); error.setDescription("An Error"); diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp index 1decc04ad2..0ba29d6b6a 100644 --- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp +++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlfile.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlscriptstring.h> @@ -125,10 +126,12 @@ void tst_qqmlexpression::expressionFromDataComponent() QQmlEngine engine; QQmlComponent c(&engine); - QUrl url = testFileUrl("expressionFromDataComponent.qml"); + const QString fn(QLatin1String("expressionFromDataComponent.qml")); + QUrl url = testFileUrl(fn); + QString path = testFile(fn); { - QFile f(url.toLocalFile()); + QFile f(path); QVERIFY(f.open(QIODevice::ReadOnly)); c.setData(f.readAll(), url); } diff --git a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp index 341a49bf09..16b8fe578d 100644 --- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp +++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp @@ -47,18 +47,21 @@ class tst_qqmlextensionplugin : public QObject { Q_OBJECT - bool isDuplicate(QString file, const QList<QString> & files) { -#ifndef DEBUG_SUFFIX - Q_UNUSED(file) - Q_UNUSED(files) - return false; -#else + static QStringList removeDuplicates(QStringList files) { +#ifdef DEBUG_SUFFIX + const auto isDuplicate = [files] (QString file) { # ifdef QT_DEBUG - return !file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(SUFFIX, DEBUG_SUFFIX)); + return !file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(SUFFIX, DEBUG_SUFFIX)); # else - return file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(DEBUG_SUFFIX, SUFFIX)); + return file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(DEBUG_SUFFIX, SUFFIX)); # endif + }; + + files.erase(std::remove_if(files.begin(), files.end(), isDuplicate), + files.end()); + #endif + return files; } public: @@ -84,12 +87,7 @@ void tst_qqmlextensionplugin::iidCheck_data() } } - for (QMutableListIterator<QString> it(files); it.hasNext(); ) { - QString file = it.next(); - if (isDuplicate(file, files)) { - it.remove(); - } - } + files = removeDuplicates(std::move(files)); QTest::addColumn<QString>("filePath"); foreach (const QString &file, files) { diff --git a/tests/auto/qml/qqmlimport/qqmlimport.pro b/tests/auto/qml/qqmlimport/qqmlimport.pro index c8b0f68d9f..ba80547f4e 100644 --- a/tests/auto/qml/qqmlimport/qqmlimport.pro +++ b/tests/auto/qml/qqmlimport/qqmlimport.pro @@ -6,3 +6,8 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlimport.cpp include (../../shared/util.pri) + +TESTDATA = data/* \ + MyPluginSupported/* \ + MyPluginUnsupported/* \ + FormFromQmlDir/* diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index a9657eba1d..ca1e52ad2c 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -78,7 +78,7 @@ void tst_QQmlImport::testDesignerSupported() QVERIFY(window->errors().isEmpty()); QString warningString("%1:30:1: module does not support the designer \"MyPluginUnsupported\" \n import MyPluginUnsupported 1.0\r \n ^ "); -#ifndef Q_OS_WIN +#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) warningString.remove('\r'); #endif warningString = warningString.arg(testFileUrl("testfile_unsupported.qml").toString()); @@ -130,6 +130,9 @@ void tst_QQmlImport::uiFormatLoading() void tst_QQmlImport::importPathOrder() { +#ifdef Q_OS_ANDROID + QSKIP("QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) returns bogus path on Android, but its nevertheless unusable."); +#endif QStringList expectedImportPaths; QString appDirPath = QCoreApplication::applicationDirPath(); QString qml2Imports = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); diff --git a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro index 542ec44736..e8ed8e91b1 100644 --- a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro +++ b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro @@ -9,4 +9,4 @@ include (../../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private testlib +QT += core-private gui-private qml-private testlib qmlmodels-private diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp index a66f13e6bb..9c5e09c77c 100644 --- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp +++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp @@ -31,7 +31,7 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> -#include <QtQml/private/qqmlinstantiator_p.h> +#include <QtQmlModels/private/qqmlinstantiator_p.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlincubator.h> #include "../../shared/util.h" diff --git a/tests/auto/qml/qqmllanguage/data/alias.17.qml b/tests/auto/qml/qqmllanguage/data/alias.17.qml new file mode 100644 index 0000000000..a76dd120b6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.17.qml @@ -0,0 +1,31 @@ +import QtQuick 2.12 + +Item { + id: root + anchors.fill: parent + width: 100 + height: 100 + property bool success: checkValue === aliasUser.topMargin + property int checkValue: 42 + Rectangle { + id: myItem + objectName: "myItem" + color: "blue" + anchors.topMargin: root.checkValue + width: 50 + height: 50 + Text {text: "source:\n" + myItem.anchors.topMargin} + } + + Rectangle { + property alias topMargin: myItem.anchors.topMargin + id: aliasUser + objectName: "aliasUser" + color: "red" + anchors.left: myItem.right + width: 50 + height: 50 + Text {objectName: "myText"; text: "alias:\n" + aliasUser.topMargin} + } +} + diff --git a/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt b/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt diff --git a/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml b/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml new file mode 100644 index 0000000000..3567cdb5a3 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml @@ -0,0 +1,6 @@ +import QML 1.0 +QtObject { + property Component component: Component { + QtObject {} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt index 30748234bc..5a144f2db5 100644 --- a/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt @@ -1 +1 @@ -3:5:Invalid grouped property access +3:5:Invalid grouped property access: Property "value" with primitive type "int". diff --git a/tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml b/tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml new file mode 100644 index 0000000000..26931a4f10 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 as QQ +import QtQml 2.0 as Core +QQ.Item { + id: root + function returnItem() : Core.QtObject { return root; } + function takeString(arg: string) { return arg; } +} diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt index 92ce4c649f..8dca84b34e 100644 --- a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt @@ -1,2 +1 @@ 5:1:TetZ$ is not a type --1:-1:Invalid QML type name "TetZ$" diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml b/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml Binary files differindex e726f6783c..f164ec98ea 100644 --- a/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml +++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt new file mode 100644 index 0000000000..da17dc5599 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt @@ -0,0 +1,2 @@ +3:2:Unexpected token `version number' +1:1:Expected a qualified name id or a string literal diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.3.qml b/tests/auto/qml/qqmllanguage/data/fuzzed.3.qml Binary files differnew file mode 100644 index 0000000000..6861ebf8a9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/fuzzed.3.qml diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt index 810fd31b41..5deec4ccf9 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt @@ -1 +1 @@ -5:5:Invalid grouped property access +5:5:Invalid grouped property access: Property "o" with primitive type "int". diff --git a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt index 945dacf8ab..887d87b9fb 100644 --- a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt @@ -1 +1 @@ -4:18:Can not assign value of type "int" to property "x", expecting an object +4:18:Cannot assign value of type "MyTypeObject" to property "x", expecting "int" diff --git a/tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt new file mode 100644 index 0000000000..1655f9264a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt @@ -0,0 +1 @@ +3:23:Cannot assign to property of unknown type "SomethingUnknown*". diff --git a/tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml new file mode 100644 index 0000000000..c22fd65350 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml @@ -0,0 +1,4 @@ +import Test 1.0 +MyQmlObject { + somethingUnknown: SomethingKnown {} +} diff --git a/tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml b/tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml new file mode 100644 index 0000000000..6dee30de95 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml @@ -0,0 +1,19 @@ +import QtQml 2.0 + +QtObject { + id: root + + property bool success: false + + signal testSignal(param: bool) + + function handleTestSignal(param) { + success = param + } + + Component.onCompleted: { + success = false; + root.testSignal.connect(handleTestSignal) + root.testSignal(true); + } +} diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt new file mode 100644 index 0000000000..b316acae30 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt @@ -0,0 +1 @@ +5:14:Type annotations are not permitted in variable declarations diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml new file mode 100644 index 0000000000..655fe4c226 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + function notYet() { + var x: string = "ko" + return x + } +} diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.pro b/tests/auto/qml/qqmllanguage/qqmllanguage.pro index 3e88f3f0db..724a27320c 100644 --- a/tests/auto/qml/qqmllanguage/qqmllanguage.pro +++ b/tests/auto/qml/qqmllanguage/qqmllanguage.pro @@ -17,3 +17,5 @@ include (../../shared/util.pri) OTHER_FILES += \ data/readonlyObjectProperty.qml + +android: RESOURCES += qqmllanguage.qrc diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.qrc b/tests/auto/qml/qqmllanguage/qqmllanguage.qrc new file mode 100644 index 0000000000..f5212ac75c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/qqmllanguage.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> +<file alias="data/I18nTypeÁâãäå.qml">data/I18nType30.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index f9a6ee8e5a..6956533196 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -27,6 +27,8 @@ ****************************************************************************/ #include "testtypes.h" +#include <private/qv4qmlcontext_p.h> + static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) @@ -60,6 +62,7 @@ void registerTypes() qmlRegisterType<MyRevisionedClass,1>("Test",1,1,"MyRevisionedClass"); qmlRegisterType<MyRevisionedIllegalOverload>("Test",1,0,"MyRevisionedIllegalOverload"); qmlRegisterType<MyRevisionedLegalOverload>("Test",1,0,"MyRevisionedLegalOverload"); + qmlRegisterType<SomethingKnown>("Test",1,0,"SomethingKnown"); // Register the uncreatable base class qmlRegisterRevision<MyRevisionedBaseClassRegistered,1>("Test",1,1); @@ -125,7 +128,7 @@ QVariant myCustomVariantTypeConverter(const QString &data) } -void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { CustomBinding *customBinding = qobject_cast<CustomBinding*>(object); Q_ASSERT(customBinding); @@ -154,7 +157,7 @@ void CustomBinding::componentComplete() } } -void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { if (bindings.count() != 1) { error(bindings.first(), QStringLiteral("Custom parser invoked incorrectly for unit test")); @@ -184,7 +187,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::Compil } } -void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings) +void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings) { SimpleObjectWithCustomParser *o = qobject_cast<SimpleObjectWithCustomParser*>(object); Q_ASSERT(o); diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index bb6e9582c2..1aab24841a 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -89,6 +89,14 @@ private: int m_value2; }; +class SomethingUnknown : public QObject { + Q_OBJECT +}; + +class SomethingKnown : public SomethingUnknown { + Q_OBJECT +}; + class MyQmlObject : public QObject, public MyInterface { Q_OBJECT @@ -104,6 +112,7 @@ class MyQmlObject : public QObject, public MyInterface Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal) Q_PROPERTY(int nonScriptable READ nonScriptable WRITE setNonScriptable SCRIPTABLE false) Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged) + Q_PROPERTY(SomethingUnknown* somethingUnknown READ somethingUnknown WRITE setSomethingUnknown NOTIFY somethingUnknownChanged) Q_INTERFACES(MyInterface) public: @@ -151,6 +160,9 @@ public: int childAddedEventCount() const { return m_childAddedEventCount; } + SomethingUnknown* somethingUnknown() const { return nullptr; } + void setSomethingUnknown(SomethingUnknown* something) { Q_UNUSED(something); } + public slots: void basicSlot() { qWarning("MyQmlObject::basicSlot"); } void basicSlotWithArgs(int v) { qWarning("MyQmlObject::basicSlotWithArgs(%d)", v); } @@ -162,6 +174,7 @@ signals: void oddlyNamedNotifySignal(); void signalWithDefaultArg(int parameter = 5); void qjsvalueChanged(); + void somethingUnknownChanged(); protected: virtual bool event(QEvent *event); @@ -781,15 +794,15 @@ class MyCustomParserType : public QObject class MyCustomParserTypeParser : public QQmlCustomParser { public: - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} }; class EnumSupportingCustomParser : public QQmlCustomParser { public: - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} }; class MyParserStatus : public QObject, public QQmlParserStatus @@ -1275,15 +1288,15 @@ public: void setTarget(QObject *newTarget) { m_target = newTarget; } QPointer<QObject> m_target; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding*> bindings; QByteArray m_bindingData; }; class CustomBindingParser : public QQmlCustomParser { - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); }; class SimpleObjectWithCustomParser : public QObject @@ -1328,8 +1341,8 @@ private: class SimpleObjectCustomParser : public QQmlCustomParser { - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); }; class RootObjectInCreationTester : public QObject diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 63fc55ad0f..fae74f1f25 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -208,6 +208,7 @@ private slots: void remoteLoadCrash(); void signalWithDefaultArg(); void signalParameterTypes(); + void functionParameterTypes(); // regression tests for crashes void crash1(); @@ -242,12 +243,11 @@ private slots: void compositeSingletonModuleQualified(); void compositeSingletonInstantiateError(); void compositeSingletonDynamicPropertyError(); - void compositeSingletonDynamicSignal(); + void compositeSingletonDynamicSignalAndJavaScriptPragma(); void compositeSingletonQmlRegisterTypeError(); void compositeSingletonQmldirNoPragmaError(); void compositeSingletonQmlDirError(); void compositeSingletonRemote(); - void compositeSingletonJavaScriptPragma(); void compositeSingletonSelectors(); void compositeSingletonRegistered(); void compositeSingletonCircular(); @@ -369,7 +369,8 @@ private: void tst_qqmllanguage::cleanupTestCase() { - QVERIFY(QFile::remove(testFile(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")))); + if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) + QVERIFY(QFile::remove(testFile(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")))); } void tst_qqmllanguage::insertedSemicolon_data() @@ -620,11 +621,29 @@ void tst_qqmllanguage::errors_data() QTest::newRow("fuzzed.1") << "fuzzed.1.qml" << "fuzzed.1.errors.txt" << false; QTest::newRow("fuzzed.2") << "fuzzed.2.qml" << "fuzzed.2.errors.txt" << false; -} + QTest::newRow("fuzzed.3") << "fuzzed.3.qml" << "fuzzed.3.errors.txt" << false; + + QTest::newRow("bareQmlImport") << "bareQmlImport.qml" << "bareQmlImport.errors.txt" << false; + + QTest::newRow("typeAnnotations.2") << "typeAnnotations.2.qml" << "typeAnnotations.2.errors.txt" << false; + QTest::newRow("propertyUnknownType") << "propertyUnknownType.qml" << "propertyUnknownType.errors.txt" << false; +} void tst_qqmllanguage::errors() { +#ifdef Q_OS_ANDROID + if (qstrcmp(QTest::currentDataTag(), "fuzzed.2") == 0) { + QSKIP("Gives different errors on Android"); + /* Only gives one error on Android: + + qrc:/data/fuzzed.2.qml:1:1: " + import" + ^ + So, it seems to complain about the first import (which is understandable) + */ + } +#endif QFETCH(QString, file); QFETCH(QString, errorFile); QFETCH(bool, create); @@ -1458,8 +1477,8 @@ void tst_qqmllanguage::dynamicObjectProperties() QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QCOMPARE(object->property("objectProperty"), qVariantFromValue((QObject*)nullptr)); - QVERIFY(object->property("objectProperty2") != qVariantFromValue((QObject*)nullptr)); + QCOMPARE(object->property("objectProperty"), QVariant::fromValue((QObject*)nullptr)); + QVERIFY(object->property("objectProperty2") != QVariant::fromValue((QObject*)nullptr)); } { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.2.qml")); @@ -1467,7 +1486,7 @@ void tst_qqmllanguage::dynamicObjectProperties() QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QVERIFY(object->property("objectProperty") != qVariantFromValue((QObject*)nullptr)); + QVERIFY(object->property("objectProperty") != QVariant::fromValue((QObject*)nullptr)); } } @@ -1736,7 +1755,7 @@ void tst_qqmllanguage::aliasProperties() // Write through alias MyQmlObject *v2 = new MyQmlObject(); v2->setParent(object.data()); - object->setProperty("aliasObject", qVariantFromValue(v2)); + object->setProperty("aliasObject", QVariant::fromValue(v2)); MyQmlObject *v3 = qvariant_cast<MyQmlObject *>(object->property("aliasObject")); QVERIFY(v3 != nullptr); @@ -1951,6 +1970,69 @@ void tst_qqmllanguage::aliasProperties() QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); } + + // Alias to grouped property + { + QQmlComponent component(&engine, testFileUrl("alias.17.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QVERIFY(object->property("success").toBool()); + } + + // Alias to grouped property updates + { + QQmlComponent component(&engine, testFileUrl("alias.17.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser")); + QVERIFY(aliasUser); + QQmlProperty checkValueProp(object.get(), "checkValue"); + QVERIFY(checkValueProp.isValid()); + checkValueProp.write(777); + QCOMPARE(object->property("checkValue").toInt(), 777); + QCOMPARE(aliasUser->property("topMargin").toInt(), 777); + } + + // Write to alias to grouped property + { + QQmlComponent component(&engine, testFileUrl("alias.17.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser")); + QVERIFY(aliasUser); + QQmlProperty topMarginProp {aliasUser, "topMargin"}; + QVERIFY(topMarginProp.isValid()); + topMarginProp.write(777); + QObject *myItem = object->findChild<QObject*>(QLatin1String("myItem")); + QVERIFY(myItem); + auto anchors = myItem->property("anchors").value<QObject*>(); + QVERIFY(anchors); + QCOMPARE(anchors->property("topMargin").toInt(), 777); + } + + // Binding to alias to grouped property gets updated + { + QQmlComponent component(&engine, testFileUrl("alias.17.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser")); + QVERIFY(aliasUser); + QQmlProperty topMarginProp {aliasUser, "topMargin"}; + QVERIFY(topMarginProp.isValid()); + topMarginProp.write(20); + QObject *myText = object->findChild<QObject*>(QLatin1String("myText")); + QVERIFY(myText); + auto text = myText->property("text").toString(); + QCOMPARE(text, "alias:\n20"); + } } // QTBUG-13374 Test that alias properties and signals can coexist @@ -2223,7 +2305,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = td->compilationUnit(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); compilationUnit->setUnitData(qmlUnit); const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); @@ -2233,9 +2315,9 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i; if (compilationUnit->stringAt(binding->propertyNameIndex) != QString("scriptProperty")) continue; - QCOMPARE(binding->valueAsScriptString(compilationUnit.data()), QString("intProperty")); + QCOMPARE(compilationUnit->bindingValueAsScriptString(binding), QString("intProperty")); const_cast<QV4::CompiledData::Binding*>(binding)->stringIndex = 0; // empty string index - QVERIFY(binding->valueAsScriptString(compilationUnit.data()).isEmpty()); + QVERIFY(compilationUnit->bindingValueAsScriptString(binding).isEmpty()); break; } QVERIFY(i < rootObject->nBindings); @@ -2481,7 +2563,15 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QCOMPARE(QString(object->metaObject()->className()), type); + const QMetaObject *meta = object->metaObject(); + for (; meta; meta = meta->superClass()) { + const QString className(meta->className()); + if (!className.contains("_QMLTYPE_") && !className.contains("_QML_")) { + QCOMPARE(className, type); + break; + } + } + QVERIFY(meta != nullptr); } engine.setImportPathList(defaultImportPathList); @@ -2673,11 +2763,15 @@ void tst_qqmllanguage::importsLocal_data() "Test {}" << (!qmlCheckTypes()?"TestType":"") << (!qmlCheckTypes()?"":"Test is ambiguous. Found in org/qtproject/Test/ and in subdir/"); - QTest::newRow("file URL survives percent-encoding") - << "import \"" + QUrl::fromLocalFile(QDir::currentPath() + "/{subdir}").toString() + "\"\n" - "Test {}" - << "QQuickRectangle" - << ""; + + if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) { + // file URL doesn't work with qrc scheme + QTest::newRow("file URL survives percent-encoding") + << "import \"" + QUrl::fromLocalFile(QDir::currentPath() + "/{subdir}").toString() + "\"\n" + "Test {}" + << "QQuickRectangle" + << ""; + } } void tst_qqmllanguage::importsLocal() @@ -3436,7 +3530,11 @@ void tst_qqmllanguage::uncreatableTypesAsProperties() void tst_qqmllanguage::initTestCase() { QQmlDataTest::initTestCase(); - QVERIFY2(QDir::setCurrent(dataDirectory()), qPrintable("Could not chdir to " + dataDirectory())); + if (dataDirectoryUrl().scheme() == QLatin1String("qrc")) + engine.addImportPath(dataDirectory()); + else + QVERIFY2(QDir::setCurrent(dataDirectory()), qPrintable("Could not chdir to " + dataDirectory())); + defaultImportPathList = engine.importPathList(); @@ -3467,11 +3565,13 @@ void tst_qqmllanguage::initTestCase() // For POSIX, this will just be data/I18nType.qml, since POSIX is 7-bit // For iso8859-1 locale, this will just be data/I18nType?????.qml where ????? is 5 8-bit characters // For utf-8 locale, this will be data/I18nType??????????.qml where ?????????? is 5 8-bit characters, UTF-8 encoded - QFile in(testFileUrl(QLatin1String("I18nType30.qml")).toLocalFile()); - QVERIFY2(in.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(in.fileName(), in.errorString()))); - QFile out(testFileUrl(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")).toLocalFile()); - QVERIFY2(out.open(QIODevice::WriteOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(out.fileName(), out.errorString()))); - out.write(in.readAll()); + if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) { + QFile in(testFileUrl(QLatin1String("I18nType30.qml")).toLocalFile()); + QVERIFY2(in.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(in.fileName(), in.errorString()))); + QFile out(testFileUrl(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")).toLocalFile()); + QVERIFY2(out.open(QIODevice::WriteOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(out.fileName(), out.errorString()))); + out.write(in.readAll()); + } // Register a Composite Singleton. qmlRegisterSingletonType(testFileUrl("singleton/RegisteredCompositeSingletonType.qml"), "org.qtproject.Test", 1, 0, "RegisteredSingleton"); @@ -3694,6 +3794,38 @@ void tst_qqmllanguage::signalParameterTypes() QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); } + + // dynamic signal connections + { + QQmlComponent component(&engine, testFileUrl("signalParameterTypes.3.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); + QVERIFY(obj->property("success").toBool()); + } +} + +void tst_qqmllanguage::functionParameterTypes() +{ + QQmlComponent component(&engine, testFileUrl("functionParameterTypes.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY2(!obj.isNull(), qPrintable(component.errorString())); + const QMetaObject *metaObject = obj->metaObject(); + + { + QMetaMethod slot = metaObject->method(metaObject->indexOfSlot("returnItem()")); + QVERIFY(slot.isValid()); + QCOMPARE(slot.returnType(), QMetaType::type("QObject*")); + QObject *returnedPtr = nullptr; + slot.invoke(obj.data(), Qt::DirectConnection, Q_RETURN_ARG(QObject*, returnedPtr)); + QCOMPARE(returnedPtr, obj.data()); + } + + { + QMetaMethod slot = metaObject->method(metaObject->indexOfSlot("takeString(QString)")); + QVERIFY(slot.isValid()); + QCOMPARE(slot.parameterCount(), 1); + QCOMPARE(slot.parameterType(0), int(QMetaType::QString)); + } } // QTBUG-20639 @@ -3812,7 +3944,7 @@ void tst_qqmllanguage::scopedEnumsWithNameClash() { auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithNameClash>("ScopedEnumsWithNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); auto registryGuard = qScopeGuard([typeId]() { - qmlUnregisterType(typeId); + QQmlMetaType::unregisterType(typeId); }); QQmlEngine engine; @@ -3831,7 +3963,7 @@ void tst_qqmllanguage::scopedEnumsWithResolvedNameClash() { auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithResolvedNameClash>("ScopedEnumsWithResolvedNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); auto registryGuard = qScopeGuard([typeId]() { - qmlUnregisterType(typeId); + QQmlMetaType::unregisterType(typeId); }); QQmlEngine engine; @@ -3951,7 +4083,7 @@ void tst_qqmllanguage::objectDeletionNotify() void tst_qqmllanguage::scopedProperties() { - QQmlComponent component(&engine, testFile("scopedProperties.qml")); + QQmlComponent component(&engine, testFileUrl("scopedProperties.qml")); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -3960,7 +4092,7 @@ void tst_qqmllanguage::scopedProperties() void tst_qqmllanguage::deepProperty() { - QQmlComponent component(&engine, testFile("deepProperty.qml")); + QQmlComponent component(&engine, testFileUrl("deepProperty.qml")); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); QFont font = qvariant_cast<QFont>(qvariant_cast<QObject*>(o->property("someObject"))->property("font")); @@ -3978,14 +4110,16 @@ void tst_qqmllanguage::implicitImportsLast() if (engine.importPathList() == defaultImportPathList) engine.addImportPath(testFile("lib")); - QQmlComponent component(&engine, testFile("localOrderTest.qml")); + QQmlComponent component(&engine, testFileUrl("localOrderTest.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea"))); + QVERIFY(QString(object->metaObject()->superClass()->superClass()->className()) + .startsWith(QLatin1String("QQuickMouseArea"))); QObject* object2 = object->property("item").value<QObject*>(); QVERIFY(object2 != nullptr); - QCOMPARE(QString(object2->metaObject()->className()), QLatin1String("QQuickRectangle")); + QCOMPARE(QString(object2->metaObject()->superClass()->className()), + QLatin1String("QQuickRectangle")); engine.setImportPathList(defaultImportPathList); } @@ -3998,7 +4132,7 @@ void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* file if (!fileName || !propertyName) return; - QQmlComponent component(&engine, testFile(fileName)); + QQmlComponent component(&engine, testFileUrl(fileName)); VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); @@ -4040,7 +4174,7 @@ void verifyCompositeSingletonPropertyValues(QObject* o, const char* n1, int v1, // Reads values from a composite singleton type void tst_qqmllanguage::compositeSingletonProperties() { - QQmlComponent component(&engine, testFile("singletonTest1.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest1.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4087,14 +4221,14 @@ void tst_qqmllanguage::compositeSingletonDifferentEngine() // pragma Singleton in a non-type qml file fails void tst_qqmllanguage::compositeSingletonNonTypeError() { - QQmlComponent component(&engine, testFile("singletonTest4.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest4.qml")); VERIFY_ERRORS("singletonTest4.error.txt"); } // Loads the singleton using a namespace qualifier void tst_qqmllanguage::compositeSingletonQualifiedNamespace() { - QQmlComponent component(&engine, testFile("singletonTest5.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest5.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4119,7 +4253,7 @@ void tst_qqmllanguage::compositeSingletonModule() { engine.addImportPath(testFile("singleton/module")); - QQmlComponent component(&engine, testFile("singletonTest6.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest6.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4145,7 +4279,7 @@ void tst_qqmllanguage::compositeSingletonModuleVersioned() { engine.addImportPath(testFile("singleton/module")); - QQmlComponent component(&engine, testFile("singletonTest7.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest7.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4171,7 +4305,7 @@ void tst_qqmllanguage::compositeSingletonModuleQualified() { engine.addImportPath(testFile("singleton/module")); - QQmlComponent component(&engine, testFile("singletonTest8.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest8.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4195,27 +4329,46 @@ void tst_qqmllanguage::compositeSingletonModuleQualified() // Tries to instantiate a type with a pragma Singleton and fails void tst_qqmllanguage::compositeSingletonInstantiateError() { - QQmlComponent component(&engine, testFile("singletonTest9.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest9.qml")); VERIFY_ERRORS("singletonTest9.error.txt"); } // Having a composite singleton type as dynamic property type is allowed void tst_qqmllanguage::compositeSingletonDynamicPropertyError() { - QQmlComponent component(&engine, testFile("singletonTest10.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest10.qml")); VERIFY_ERRORS(0); } -// Having a composite singleton type as dynamic signal parameter succeeds -// (like C++ singleton) -void tst_qqmllanguage::compositeSingletonDynamicSignal() +void tst_qqmllanguage::compositeSingletonDynamicSignalAndJavaScriptPragma() { - QQmlComponent component(&engine, testFile("singletonTest11.qml")); - VERIFY_ERRORS(0); - QScopedPointer<QObject> o(component.create()); - QVERIFY(o != nullptr); + { + // Having a composite singleton type as dynamic signal parameter succeeds + // (like C++ singleton) - verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55); + QQmlComponent component(&engine, testFileUrl("singletonTest11.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); + + verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55); + } + { + // Load a composite singleton type and a javascript file that has .pragma library + // in it. This will make sure that the javascript .pragma does not get mixed with + // the pragma Singleton changes. + + QQmlComponent component(&engine, testFileUrl("singletonTest16.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); + + // The value1 that is read from the SingletonType was changed from 125 to 99 + // above. As the type is a singleton and + // the engine has not been destroyed, we just retrieve the old instance and + // the value is still 99. + verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333); + } } // Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it. @@ -4224,21 +4377,21 @@ void tst_qqmllanguage::compositeSingletonQmlRegisterTypeError() { qmlRegisterType(testFileUrl("singleton/registeredComposite/CompositeType.qml"), "CompositeSingletonTest", 1, 0, "RegisteredCompositeType"); - QQmlComponent component(&engine, testFile("singletonTest12.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest12.qml")); VERIFY_ERRORS("singletonTest12.error.txt"); } // Qmldir defines a type as a singleton, but the qml file does not have a pragma Singleton. void tst_qqmllanguage::compositeSingletonQmldirNoPragmaError() { - QQmlComponent component(&engine, testFile("singletonTest13.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest13.qml")); VERIFY_ERRORS("singletonTest13.error.txt"); } // Invalid singleton definition in the qmldir file results in an error void tst_qqmllanguage::compositeSingletonQmlDirError() { - QQmlComponent component(&engine, testFile("singletonTest14.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest14.qml")); VERIFY_ERRORS("singletonTest14.error.txt"); } @@ -4267,30 +4420,13 @@ void tst_qqmllanguage::compositeSingletonRemote() verifyCompositeSingletonPropertyValues(o.data(), "value1", 525, "value2", 355); } -// Load a composite singleton type and a javascript file that has .pragma library -// in it. This will make sure that the javascript .pragma does not get mixed with -// the pragma Singleton changes. -void tst_qqmllanguage::compositeSingletonJavaScriptPragma() -{ - QQmlComponent component(&engine, testFile("singletonTest16.qml")); - VERIFY_ERRORS(0); - QScopedPointer<QObject> o(component.create()); - QVERIFY(o != nullptr); - - // The value1 that is read from the SingletonType was changed from 125 to 99 - // in compositeSingletonDynamicSignal() above. As the type is a singleton and - // the engine has not been destroyed, we just retrieve the old instance and - // the value is still 99. - verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333); -} - // Reads values from a Singleton accessed through selectors. void tst_qqmllanguage::compositeSingletonSelectors() { QQmlEngine e2; QQmlFileSelector qmlSelector(&e2); qmlSelector.setExtraSelectors(QStringList() << "basicSelector"); - QQmlComponent component(&e2, testFile("singletonTest1.qml")); + QQmlComponent component(&e2, testFileUrl("singletonTest1.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4302,7 +4438,7 @@ void tst_qqmllanguage::compositeSingletonSelectors() // qmlRegisterSingletonType. void tst_qqmllanguage::compositeSingletonRegistered() { - QQmlComponent component(&engine, testFile("singletonTest17.qml")); + QQmlComponent component(&engine, testFileUrl("singletonTest17.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4312,7 +4448,7 @@ void tst_qqmllanguage::compositeSingletonRegistered() void tst_qqmllanguage::compositeSingletonCircular() { - QQmlComponent component(&engine, testFile("circularSingleton.qml")); + QQmlComponent component(&engine, testFileUrl("circularSingleton.qml")); VERIFY_ERRORS(0); QQmlTestMessageHandler messageHandler; @@ -4346,7 +4482,7 @@ void tst_qqmllanguage::singletonsHaveContextAndEngine() void tst_qqmllanguage::customParserBindingScopes() { - QQmlComponent component(&engine, testFile("customParserBindingScopes.qml")); + QQmlComponent component(&engine, testFileUrl("customParserBindingScopes.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4357,7 +4493,7 @@ void tst_qqmllanguage::customParserBindingScopes() void tst_qqmllanguage::customParserEvaluateEnum() { - QQmlComponent component(&engine, testFile("customParserEvaluateEnum.qml")); + QQmlComponent component(&engine, testFileUrl("customParserEvaluateEnum.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4365,7 +4501,7 @@ void tst_qqmllanguage::customParserEvaluateEnum() void tst_qqmllanguage::customParserProperties() { - QQmlComponent component(&engine, testFile("customParserProperties.qml")); + QQmlComponent component(&engine, testFileUrl("customParserProperties.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4379,7 +4515,7 @@ void tst_qqmllanguage::customParserProperties() void tst_qqmllanguage::customParserWithExtendedObject() { - QQmlComponent component(&engine, testFile("customExtendedParserProperties.qml")); + QQmlComponent component(&engine, testFileUrl("customExtendedParserProperties.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4397,7 +4533,7 @@ void tst_qqmllanguage::customParserWithExtendedObject() void tst_qqmllanguage::nestedCustomParsers() { - QQmlComponent component(&engine, testFile("nestedCustomParsers.qml")); + QQmlComponent component(&engine, testFileUrl("nestedCustomParsers.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4411,7 +4547,7 @@ void tst_qqmllanguage::nestedCustomParsers() void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() { - QQmlComponent component(&engine, testFile("preservePropertyCacheOnGroupObjects.qml")); + QQmlComponent component(&engine, testFileUrl("preservePropertyCacheOnGroupObjects.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4430,7 +4566,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() void tst_qqmllanguage::propertyCacheInSync() { - QQmlComponent component(&engine, testFile("propertyCacheInSync.qml")); + QQmlComponent component(&engine, testFileUrl("propertyCacheInSync.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4450,7 +4586,7 @@ void tst_qqmllanguage::propertyCacheInSync() void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() { - QQmlComponent component(&engine, testFile("rootObjectInCreationNotForSubObjects.qml")); + QQmlComponent component(&engine, testFileUrl("rootObjectInCreationNotForSubObjects.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(!o.isNull()); @@ -4476,7 +4612,7 @@ void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() // QTBUG-63036 void tst_qqmllanguage::lazyDeferredSubObject() { - QQmlComponent component(&engine, testFile("lazyDeferredSubObject.qml")); + QQmlComponent component(&engine, testFileUrl("lazyDeferredSubObject.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -4491,7 +4627,7 @@ void tst_qqmllanguage::lazyDeferredSubObject() // QTBUG-63200 void tst_qqmllanguage::deferredProperties() { - QQmlComponent component(&engine, testFile("deferredProperties.qml")); + QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -4608,7 +4744,7 @@ static void testExecuteDeferredOnce(const QQmlProperty &property) void tst_qqmllanguage::executeDeferredPropertiesOnce() { - QQmlComponent component(&engine, testFile("deferredProperties.qml")); + QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -4708,7 +4844,7 @@ void tst_qqmllanguage::deleteSingletons() QPointer<QObject> singleton; { QQmlEngine tmpEngine; - QQmlComponent component(&tmpEngine, testFile("singletonTest5.qml")); + QQmlComponent component(&tmpEngine, testFileUrl("singletonTest5.qml")); VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); QVERIFY(o != nullptr); @@ -4735,7 +4871,7 @@ void tst_qqmllanguage::arrayBuffer_data() void tst_qqmllanguage::arrayBuffer() { QFETCH(QString, file); - QQmlComponent component(&engine, testFile(file)); + QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); @@ -4969,7 +5105,6 @@ void tst_qqmllanguage::instanceof() if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") || QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle")) - QEXPECT_FAIL("", "QTBUG-58477: QML type rules are a little lax", Continue); QCOMPARE(returnValue, expectedValue.toBool()); } else { QVERIFY(expr.hasError()); @@ -5005,7 +5140,8 @@ void tst_qqmllanguage::accessDeletedObject() { QQmlEngine engine; - engine.rootContext()->setContextProperty("objectCreator", new ObjectCreator); + QScopedPointer<ObjectCreator> creator(new ObjectCreator); + engine.rootContext()->setContextProperty("objectCreator", creator.get()); QQmlComponent component(&engine, testFileUrl("accessDeletedObject.qml")); VERIFY_ERRORS(0); diff --git a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro index 4ada590a2a..62ad85547e 100644 --- a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro +++ b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro @@ -4,4 +4,4 @@ macx:CONFIG -= app_bundle SOURCES += tst_qqmllistcompositor.cpp -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private diff --git a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro index 8e3aed0baf..4d44d6b22b 100644 --- a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro +++ b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro @@ -8,4 +8,4 @@ include (../../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 771f3e5c4e..75a932b6f4 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -30,7 +30,7 @@ #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickanimation_p.h> #include <QtQml/private/qqmlengine_p.h> -#include <QtQml/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qqmlexpression_p.h> #include <QQmlComponent> @@ -128,6 +128,8 @@ private slots: void qobjectTrackerForDynamicModelObjects(); void crash_append_empty_array(); void dynamic_roles_crash_QTBUG_38907(); + void nestedListModelIteration(); + void undefinedAppendShouldCauseError(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -760,11 +762,11 @@ void tst_qqmllistmodel::set() RUNEXPR("model.set(0, {test:true})"); QCOMPARE(RUNEXPR("model.get(0).test").toBool(), true); // triggers creation of model cache - QCOMPARE(model.data(0, 0), qVariantFromValue(true)); + QCOMPARE(model.data(0, 0), QVariant::fromValue(true)); RUNEXPR("model.set(0, {test:false})"); QCOMPARE(RUNEXPR("model.get(0).test").toBool(), false); // tests model cache is updated - QCOMPARE(model.data(0, 0), qVariantFromValue(false)); + QCOMPARE(model.data(0, 0), QVariant::fromValue(false)); QString warning = QString::fromLatin1("<Unknown File>: Can't create role for unsupported data type"); if (isValidErrorMessage(warning, dynamicRoles)) @@ -1667,6 +1669,61 @@ void tst_qqmllistmodel::dynamic_roles_crash_QTBUG_38907() QVERIFY(retVal.toBool()); } +void tst_qqmllistmodel::nestedListModelIteration() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + QTest::ignoreMessage(QtMsgType::QtDebugMsg ,R"({"subItems":[{"a":1,"b":0,"c":0},{"a":0,"b":2,"c":0},{"a":0,"b":0,"c":3}]})"); + component.setData( + R"(import QtQuick 2.5 + Item { + visible: true + width: 640 + height: 480 + ListModel { + id : model + } + Component.onCompleted: { + var tempData = { + subItems: [{a: 1}, {b: 2}, {c: 3}] + } + model.insert(0, tempData) + console.log(JSON.stringify(model.get(0))) + } + })", + QUrl()); + QScopedPointer<QObject>(component.create()); +} + +// QTBUG-63569 +void tst_qqmllistmodel::undefinedAppendShouldCauseError() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + R"(import QtQuick 2.5 + Item { + width: 640 + height: 480 + ListModel { + id : model + } + Component.onCompleted: { + var tempData = { + faulty: undefined + } + model.insert(0, tempData) + tempData.faulty = null + model.insert(0, tempData) + } + })", + QUrl()); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: faulty is undefined. Adding an object with a undefined member does not create a role for it."); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: faulty is null. Adding an object with a null member does not create a role for it."); + QScopedPointer<QObject>(component.create()); +} + + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" diff --git a/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro b/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro index 9e1cea9867..de58c0c075 100644 --- a/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro +++ b/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro @@ -8,4 +8,4 @@ include (../../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp index 21b0508e4d..b5e8800d0e 100644 --- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp +++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp @@ -29,7 +29,7 @@ #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQml/private/qqmlengine_p.h> -#include <QtQml/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qqmlexpression_p.h> #include <QQmlComponent> @@ -162,7 +162,7 @@ QQuickItem *tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQm QQuickItem *item = qobject_cast<QQuickItem*>(component->create()); QQmlEngine::setContextForObject(model, eng->rootContext()); if (item) - item->setProperty("model", qVariantFromValue(model)); + item->setProperty("model", QVariant::fromValue(model)); return item; } diff --git a/tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml b/tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml new file mode 100644 index 0000000000..ff80f3cf85 --- /dev/null +++ b/tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml @@ -0,0 +1,6 @@ +import QtQml 2.2 +import Test 1.0 +Calendar { + locale: Qt.locale('en_GB') + property var testLocale +} diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index eb6eb62648..a90749208c 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -32,6 +32,8 @@ #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlcontext.h> #include <QtCore/QDateTime> +#include <QtCore/qscopeguard.h> +#include <QtCore/qscopedpointer.h> #include <qcolor.h> #include "../../shared/util.h" @@ -1214,10 +1216,9 @@ private: void tst_qqmllocale::localeAsCppProperty() { - QQmlComponent component(&engine); qmlRegisterType<Calendar>("Test", 1, 0, "Calendar"); - component.setData("import QtQml 2.2\nimport Test 1.0\nCalendar { locale: Qt.locale('en_GB'); property var testLocale }", QUrl()); - QVERIFY(!component.isError()); + QQmlComponent component(&engine, testFileUrl("localeAsCppProperty.qml")); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); QTRY_VERIFY(component.isReady()); Calendar *item = qobject_cast<Calendar*>(component.create()); @@ -1271,13 +1272,21 @@ void tst_qqmllocale::timeZoneUpdated() // Set the timezone to Brisbane time, AEST-10:00 setTimeZone(QByteArray("Australia/Brisbane")); + QScopedPointer<QObject> obj; + auto cleanup = qScopeGuard([&original, &obj] { + // Restore to original time zone + setTimeZone(original); + QMetaObject::invokeMethod(obj.data(), "resetTimeZone"); + }); + DateFormatter formatter; QQmlEngine e; e.rootContext()->setContextObject(&formatter); QQmlComponent c(&e, testFileUrl("timeZoneUpdated.qml")); - QScopedPointer<QObject> obj(c.create()); + QVERIFY2(!c.isError(), qPrintable(c.errorString())); + obj.reset(c.create()); QVERIFY(obj); QVERIFY(obj->property("success").toBool()); @@ -1285,11 +1294,6 @@ void tst_qqmllocale::timeZoneUpdated() setTimeZone(QByteArray("Asia/Kolkata")); QMetaObject::invokeMethod(obj.data(), "check"); - - // Reset to original time - setTimeZone(original); - QMetaObject::invokeMethod(obj.data(), "resetTimeZone"); - QVERIFY(obj->property("success").toBool()); } #endif diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index a7805922a5..76185a97e0 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -36,7 +36,6 @@ #include <private/qqmlmetatype_p.h> #include <private/qqmlpropertyvalueinterceptor_p.h> #include <private/qqmlengine_p.h> -#include <private/qhashedstring_p.h> #include "../../shared/util.h" class tst_qqmlmetatype : public QQmlDataTest @@ -401,7 +400,7 @@ void tst_qqmlmetatype::unregisterCustomType() QCOMPARE(enumVal.type(), QVariant::Int); QCOMPARE(enumVal.toInt(), 1); } - qmlUnregisterType(controllerId); + QQmlMetaType::unregisterType(controllerId); { QQmlEngine engine; QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); @@ -424,7 +423,7 @@ void tst_qqmlmetatype::unregisterCustomType() QCOMPARE(enumVal.type(), QVariant::Int); QCOMPARE(enumVal.toInt(), 111); } - qmlUnregisterType(controllerId); + QQmlMetaType::unregisterType(controllerId); { QQmlEngine engine; QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); @@ -493,7 +492,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType() QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); } - qmlUnregisterType(staticProviderId); + QQmlMetaType::unregisterType(staticProviderId); { QQmlEngine engine; staticProviderId = qmlRegisterSingletonType<StaticProvider2>("mytypes", 1, 0, "StaticProvider", createStaticProvider2); @@ -509,7 +508,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType() QCOMPARE(stringVal.type(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2")); } - qmlUnregisterType(staticProviderId); + QQmlMetaType::unregisterType(staticProviderId); { QQmlEngine engine; staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1); @@ -535,7 +534,7 @@ void tst_qqmlmetatype::normalizeUrls() QVERIFY(QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); QUrl normalizedURL("qrc:/tstqqmlmetatype/data/CompositeType.qml"); QVERIFY(QQmlMetaType::qmlType(normalizedURL, /*includeNonFileImports=*/true).isValid()); - qmlUnregisterType(registrationId); + QQmlMetaType::unregisterType(registrationId); QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); } diff --git a/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml b/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml new file mode 100644 index 0000000000..2fc2e9f076 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml @@ -0,0 +1,6 @@ +import org.qtproject.ModuleWithQmlSingleton 1.0 +import QtQuick 2.0 + +Item { + Component.onCompleted: MySingleton +} diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml new file mode 100644 index 0000000000..9789be8191 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml @@ -0,0 +1,16 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + property Loader _loader: Loader { + source: "internal/InternalType.qml" + } + + Component.onCompleted: { + if (tracker.objectName === "first") + tracker.objectName = "second" + else + tracker.objectName = "first" + //console.log("created singleton", this) + } +} diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml new file mode 100644 index 0000000000..9be34eb061 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick 2.0 +import org.qtproject.ModuleWithQmlSingleton 1.0 +import "." + +QtObject {} diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml new file mode 100644 index 0000000000..4a8badefd2 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import ".." + +QtObject { + Component.onCompleted: MySingleton +} diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro new file mode 100644 index 0000000000..b16e0743c8 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +CONFIG += plugin +SOURCES = plugin.cpp +QT = core qml +DESTDIR = ../imports/org/qtproject/ModuleWithQmlSingleton + +QT += core-private gui-private qml-private + +IMPORT_FILES = \ + qmldir \ + MySingleton.qml \ + MySingleton2.qml + +include (../../../shared/imports.pri) + +subfiles.files = internal/InternalType.qml +subfiles.path = $$DESTDIR/internal +COPIES += subfiles diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp new file mode 100644 index 0000000000..6329927c34 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +#include <QtQml/qqmlextensionplugin.h> +#include <QtQml/qqml.h> +#include <QDir> +#include <QDebug> + +class MyPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + MyPlugin() {} + + void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == "org.qtproject.ModuleWithQmlSingleton"); + qmlRegisterSingletonType(baseUrl().resolved(QUrl("ModuleWithQmlSingleton/MySingleton.qml")), uri, 1, 0, "MySingleton"); + qmlRegisterSingletonType(baseUrl().resolved(QUrl("ModuleWithQmlSingleton/MySingleton2.qml")), uri, 1, 0, "MySingleton2"); + } +}; + +#include "plugin.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir new file mode 100644 index 0000000000..3483f80ab1 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir @@ -0,0 +1,2 @@ +module org.qtproject.ModuleWithQmlSingleton +plugin moduleWithQmlSingleton diff --git a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro index ae13a041cc..44b3ab14e6 100644 --- a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro @@ -20,7 +20,8 @@ SUBDIRS =\ plugin/childplugin\ plugin.2/childplugin\ plugin.2.1/childplugin\ - plugin.2.2 + plugin.2.2\ + moduleWithQmlSingleton tst_qqmlmoduleplugin_pro.depends += plugin SUBDIRS += tst_qqmlmoduleplugin.pro diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index 97ca3fa1de..f89cc9f24a 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -78,6 +78,7 @@ private slots: void importsChildPlugin2(); void importsChildPlugin21(); void parallelPluginImport(); + void multiSingleton(); private: QString m_importsDirectory; @@ -99,7 +100,7 @@ public: qmlRegisterModule(uri, 1, 0); } - void initializeEngine(QQmlEngine *engine, const char *uri) override + void initializeEngine(QQmlEngine *, const char *) override { initializeEngineEntered.lock(); leavingInitializeEngine.lock(); @@ -772,6 +773,20 @@ void tst_qqmlmoduleplugin::parallelPluginImport() worker.wait(); } +void tst_qqmlmoduleplugin::multiSingleton() +{ + QQmlEngine engine; + QObject obj; + engine.rootContext()->setContextProperty("tracker", &obj); + engine.addImportPath(m_importsDirectory); + QQmlComponent component(&engine, testFileUrl("multiSingleton.qml")); + QObject *object = component.create(); + QVERIFY(object != nullptr); + QCOMPARE(obj.objectName(), QLatin1String("first")); + delete object; +} + + QTEST_MAIN(tst_qqmlmoduleplugin) #include "tst_qqmlmoduleplugin.moc" diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index a5332c8860..de762d66c5 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -342,6 +342,9 @@ void tst_qqmlnotifier::lotsOfBindings() void tst_qqmlnotifier::deleteFromHandler() { +#ifdef Q_OS_ANDROID + QSKIP("Android seems to have problems with QProcess"); +#endif #if !QT_CONFIG(process) QSKIP("Need QProcess support to test qFatal."); #else diff --git a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro index 88bb630e29..5746ff754a 100644 --- a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro +++ b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro @@ -5,4 +5,4 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlobjectmodel.cpp QT += qml testlib -QT += core-private qml-private +QT += core-private qml-private qmlmodels-private diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp index fb63d811a8..6691fa43a0 100644 --- a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp +++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp @@ -25,8 +25,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <QtQml/private/qqmlobjectmodel_p.h> -#include <QtQml/private/qqmlchangeset_p.h> +#include <QtQmlModels/private/qqmlobjectmodel_p.h> +#include <QtQmlModels/private/qqmlchangeset_p.h> #include <QtTest/qsignalspy.h> #include <QtTest/qtest.h> diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js new file mode 100644 index 0000000000..f660edb69e --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js @@ -0,0 +1,3 @@ +(function () : string { + return "ko" +}) diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js new file mode 100644 index 0000000000..ab85d90880 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js @@ -0,0 +1,6 @@ + +class Foo { + member(param: string) { + return "ko" + } +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js new file mode 100644 index 0000000000..a7da9e0ca7 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js @@ -0,0 +1,6 @@ + +class Foo { + member(): string { + return "ko" + } +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js new file mode 100644 index 0000000000..4d6021e835 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js @@ -0,0 +1,4 @@ + +function x() : string { + return "ok" +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js new file mode 100644 index 0000000000..33f2abbb61 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js @@ -0,0 +1,3 @@ +(function x() : string { + return "ko" +}) diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js new file mode 100644 index 0000000000..2c23628e3f --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js @@ -0,0 +1,3 @@ + +function test(x: string, y: string) { +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js new file mode 100644 index 0000000000..a6cc00e38a --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js @@ -0,0 +1,3 @@ + +for (var i: int = 0; i < 100; ++i) { +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js new file mode 100644 index 0000000000..24d5acce98 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js @@ -0,0 +1,5 @@ + +let y = [1, 2, 3]; + +for (let x: int of y) { +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml new file mode 100644 index 0000000000..f435bf1b25 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml @@ -0,0 +1,9 @@ +import QtQuick 2.12 as MyQuick +MyQuick.Item { + function factory(param: string) : MyQuick.Item { + function nested(foo: string) { + return this + } + return nested() + } +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js new file mode 100644 index 0000000000..bf332ac7a8 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js @@ -0,0 +1,2 @@ + +var x: string = "ko" diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml new file mode 100644 index 0000000000..79a9ede3d4 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + function test() : bool { return true; } +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml new file mode 100644 index 0000000000..f58e4b92d9 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + function test() : double { return 0; } +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml new file mode 100644 index 0000000000..267ad7191f --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + function test() : int { return 0; } +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml new file mode 100644 index 0000000000..9973819ad2 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + function test() : real { return 0; } +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml new file mode 100644 index 0000000000..e632ec7154 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + function test(s: string) {} +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml new file mode 100644 index 0000000000..ebf3f32561 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + function test(u: url) {} +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml b/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml new file mode 100644 index 0000000000..d80b5e3f87 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 as MyQuick +MyQuick.Item { + function factory() : list<MyQuick.Item> { + } +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml b/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml new file mode 100644 index 0000000000..cd5c1f51e5 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 as MyQuick +MyQuick.Item { + function factory(param: string) : MyQuick.Item { + return this + } +} diff --git a/tests/auto/qml/qqmlparser/qqmlparser.pro b/tests/auto/qml/qqmlparser/qqmlparser.pro index 74cb620f06..d8e4b0dd06 100644 --- a/tests/auto/qml/qqmlparser/qqmlparser.pro +++ b/tests/auto/qml/qqmlparser/qqmlparser.pro @@ -7,3 +7,7 @@ SOURCES += tst_qqmlparser.cpp DEFINES += SRCDIR=\\\"$$PWD\\\" cross_compile: DEFINES += QTEST_CROSS_COMPILED + +TESTDATA = data/* + +include (../../shared/util.pri) diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index f16e96a385..9d8818d01e 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -32,12 +32,14 @@ #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsast_p.h> +#include "../../shared/util.h" + #include <qtest.h> #include <QDir> #include <QDebug> #include <cstdlib> -class tst_qqmlparser : public QObject +class tst_qqmlparser : public QQmlDataTest { Q_OBJECT public: @@ -55,6 +57,11 @@ private slots: void templateLiteral(); void leadingSemicolonInClass(); void templatedReadonlyProperty(); + void qmlImportInJSRequiresFullVersion(); + void typeAnnotations_data(); + void typeAnnotations(); + void disallowedTypeAnnotations_data(); + void disallowedTypeAnnotations(); private: QStringList excludedDirs; @@ -87,7 +94,7 @@ public: qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn << "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn; if (node->lastSourceLocation().end() > parentEnd) - qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn + qDebug() << "last source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn << "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn; QVERIFY(node->firstSourceLocation().begin() >= parentBegin); @@ -113,6 +120,27 @@ public: } }; +struct TypeAnnotationObserver: public AST::Visitor +{ + bool typeAnnotationSeen = false; + + void operator()(AST::Node *node) + { + AST::Node::accept(node, this); + } + + virtual bool visit(AST::TypeAnnotation *) + { + typeAnnotationSeen = true; + return true; + } + + void throwRecursionDepthError() final + { + QFAIL("Maximum statement or expression depth exceeded"); + } +}; + } tst_qqmlparser::tst_qqmlparser() @@ -121,6 +149,7 @@ tst_qqmlparser::tst_qqmlparser() void tst_qqmlparser::initTestCase() { + QQmlDataTest::initTestCase(); // Add directories you want excluded here // These snippets are not expected to run on their own. @@ -299,6 +328,116 @@ void tst_qqmlparser::templatedReadonlyProperty() QVERIFY(parser.parse()); } +void tst_qqmlparser::qmlImportInJSRequiresFullVersion() +{ + { + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String(".import Test 1.0 as T"), 0, false); + QQmlJS::Parser parser(&engine); + bool b = parser.parseProgram(); + qDebug() << parser.errorMessage(); + QVERIFY(b); + } + { + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String(".import Test 1 as T"), 0, false); + QQmlJS::Parser parser(&engine); + QVERIFY(!parser.parseProgram()); + } + { + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String(".import Test 1 as T"), 0, false); + QQmlJS::Parser parser(&engine); + QVERIFY(!parser.parseProgram()); + } + { + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String(".import Test as T"), 0, false); + QQmlJS::Parser parser(&engine); + QVERIFY(!parser.parseProgram()); + } +} + +void tst_qqmlparser::typeAnnotations_data() +{ + QTest::addColumn<QString>("file"); + + QString tests = dataDirectory() + "/typeannotations/"; + + QStringList files; + files << findFiles(QDir(tests)); + + for (const QString &file: qAsConst(files)) + QTest::newRow(qPrintable(file)) << file; +} + +void tst_qqmlparser::typeAnnotations() +{ + using namespace QQmlJS; + + QFETCH(QString, file); + + QString code; + + QFile f(file); + if (f.open(QFile::ReadOnly)) + code = QString::fromUtf8(f.readAll()); + + const bool qmlMode = file.endsWith(QLatin1String(".qml")); + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(code, 1, qmlMode); + Parser parser(&engine); + bool ok = qmlMode ? parser.parse() : parser.parseProgram(); + QVERIFY(ok); + + check::TypeAnnotationObserver observer; + observer(parser.rootNode()); + + QVERIFY(observer.typeAnnotationSeen); +} + +void tst_qqmlparser::disallowedTypeAnnotations_data() +{ + QTest::addColumn<QString>("file"); + + QString tests = dataDirectory() + "/disallowedtypeannotations/"; + + QStringList files; + files << findFiles(QDir(tests)); + + for (const QString &file: qAsConst(files)) + QTest::newRow(qPrintable(file)) << file; +} + +void tst_qqmlparser::disallowedTypeAnnotations() +{ + using namespace QQmlJS; + + QFETCH(QString, file); + + QString code; + + QFile f(file); + if (f.open(QFile::ReadOnly)) + code = QString::fromUtf8(f.readAll()); + + const bool qmlMode = file.endsWith(QLatin1String(".qml")); + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(code, 1, qmlMode); + Parser parser(&engine); + bool ok = qmlMode ? parser.parse() : parser.parseProgram(); + QVERIFY(!ok); + QVERIFY2(parser.errorMessage().startsWith("Type annotations are not permitted "), qPrintable(parser.errorMessage())); +} + QTEST_MAIN(tst_qqmlparser) #include "tst_qqmlparser.moc" diff --git a/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml b/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml new file mode 100644 index 0000000000..4e72a75f42 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml @@ -0,0 +1,27 @@ +import QtQuick 2.12 +import io.qt.bugreports 1.0 +Item { + InterfaceConsumer { + objectName: "a1" + i: A { + property int i: 42 + } + } + + InterfaceConsumer { + objectName: "a2" + property A a: A { + property int i: 43 + } + i: a + } + + InterfaceConsumer { + objectName: "a3" + property A a: A { + id : aa + property int i: 44 + } + i: aa + } +} diff --git a/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml new file mode 100644 index 0000000000..4fffc7aead --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml @@ -0,0 +1,22 @@ +import QtQuick 2.12 + +Item { + id: root + + width: 640 + height: 480 + + property bool toggle: false + property Item bound + property string message: "defined" + + readonly property Item item: root.toggle ? root : null + + Binding { target: root; property: "bound"; value: item} + + function tog() { + console.info(root.bound ? root.bound.message: "undefined") + root.toggle = !root.toggle + return 42; + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index ed213cd01a..a15c00ad62 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -35,8 +35,12 @@ #include <private/qqmlboundsignal_p.h> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif #include <QtCore/private/qobject_p.h> #include "../../shared/util.h" +#include "qobject.h" #include <QtQml/QQmlPropertyMap> #include <QDebug> @@ -145,6 +149,8 @@ private slots: void deeplyNestedObject(); void readOnlyDynamicProperties(); void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem(); + void nullPropertyBinding(); + void interfaceBinding(); void floatToStringPrecision_data(); void floatToStringPrecision(); @@ -1627,7 +1633,7 @@ void tst_qqmlproperty::writeObjectToList() MyQmlObject *object = new MyQmlObject; QQmlProperty prop(container, "children"); - prop.write(qVariantFromValue(object)); + prop.write(QVariant::fromValue(object)); QCOMPARE(list.count(), 1); QCOMPARE(list.at(0), qobject_cast<QObject*>(object)); } @@ -1644,13 +1650,13 @@ void tst_qqmlproperty::writeListToList() QList<QObject*> objList; objList << new MyQmlObject() << new MyQmlObject() << new MyQmlObject() << new MyQmlObject(); QQmlProperty prop(container, "children"); - prop.write(qVariantFromValue(objList)); + prop.write(QVariant::fromValue(objList)); QCOMPARE(list.count(), 4); //XXX need to try this with read/write prop (for read-only it correctly doesn't write) /*QList<MyQmlObject*> typedObjList; typedObjList << new MyQmlObject(); - prop.write(qVariantFromValue(&typedObjList)); + prop.write(QVariant::fromValue(&typedObjList)); QCOMPARE(container->children()->size(), 1);*/ } @@ -2017,9 +2023,13 @@ void tst_qqmlproperty::warnOnInvalidBinding() expectedWarning = testUrl.toString() + QString::fromLatin1(":7:5: Unable to assign QQuickText to QQuickRectangle"); QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); +#if QT_CONFIG(regularexpression) // V8 error message for invalid binding to anchor - expectedWarning = testUrl.toString() + QString::fromLatin1(":14:9: Unable to assign QQuickItem_QML_8 to QQuickAnchorLine"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + const QRegularExpression warning( + "^" + testUrl.toString() + + ":14:9: Unable to assign QQuickItem_QML_\\d+ to QQuickAnchorLine$"); + QTest::ignoreMessage(QtWarningMsg, warning); +#endif QQmlComponent component(&engine, testUrl); QObject *obj = component.create(); @@ -2062,6 +2072,99 @@ void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSyst QVERIFY(property.isValid()); } +// QTBUG-77027 +void tst_qqmlproperty::nullPropertyBinding() +{ + const QUrl url = testFileUrl("nullPropertyBinding.qml"); + QQmlEngine engine; + QQmlComponent component(&engine, url); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined"); + QMetaObject::invokeMethod(root.get(), "tog"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "defined"); + QMetaObject::invokeMethod(root.get(), "tog"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined"); + QMetaObject::invokeMethod(root.get(), "tog"); +} + +struct Interface { +}; + +QT_BEGIN_NAMESPACE +#define MyInterface_iid "io.qt.bugreports.Interface" +Q_DECLARE_INTERFACE(Interface, MyInterface_iid); +QT_END_NAMESPACE + +class A : public QObject, Interface { + Q_OBJECT + Q_INTERFACES(Interface) +}; + +class B : public QObject, Interface { + Q_OBJECT + Q_INTERFACES(Interface) +}; + +class C : public QObject { + Q_OBJECT +}; + +class InterfaceConsumer : public QObject { + Q_OBJECT + Q_PROPERTY(Interface* i READ interface WRITE setInterface NOTIFY interfaceChanged) + Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged) + + +public: + + Interface* interface() const + { + return m_interface; + } + void setInterface(Interface* interface) + { + QObject* object = reinterpret_cast<QObject*>(interface); + m_testValue = object->property("i").toInt(); + emit testValueChanged(); + if (m_interface == interface) + return; + + m_interface = interface; + emit interfaceChanged(); + } + + int testValue() { + return m_testValue; + } + +signals: + void interfaceChanged(); + void testValueChanged(); + +private: + Interface* m_interface = nullptr; + int m_testValue = 0; +}; +void tst_qqmlproperty::interfaceBinding() +{ + + qmlRegisterInterface<Interface>("Interface"); + qmlRegisterType<A>("io.qt.bugreports", 1, 0, "A"); + qmlRegisterType<B>("io.qt.bugreports", 1, 0, "B"); + qmlRegisterType<C>("io.qt.bugreports", 1, 0, "C"); + qmlRegisterType<InterfaceConsumer>("io.qt.bugreports", 1, 0, "InterfaceConsumer"); + + const QUrl url = testFileUrl("interfaceBinding.qml"); + QQmlEngine engine; + QQmlComponent component(&engine, url); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QCOMPARE(root->findChild<QObject*>("a1")->property("testValue").toInt(), 42); + QCOMPARE(root->findChild<QObject*>("a2")->property("testValue").toInt(), 43); + QCOMPARE(root->findChild<QObject*>("a3")->property("testValue").toInt(), 44); +} + void tst_qqmlproperty::floatToStringPrecision_data() { QTest::addColumn<QString>("propertyName"); diff --git a/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml b/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml new file mode 100644 index 0000000000..9559bc0b5f --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + readonly property bool fakeProperty: false +} diff --git a/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml b/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml new file mode 100644 index 0000000000..ed4ad04fef --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + objectName: "special" +} diff --git a/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml b/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml new file mode 100644 index 0000000000..5e1ea233b9 --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml @@ -0,0 +1,7 @@ +import QtQml 2.9 + +QtObject { + property SpecialObject1 obj1: SpecialObject1 {} + property SpecialObject2 obj2: SpecialObject2 {} + property string result: (obj1 instanceof SpecialObject2) ? "bad" : "good" +} diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 02b5302a45..c9e92cd3c9 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -31,7 +31,6 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlcomponent.h> -#include <private/qv8engine_p.h> #include <private/qmetaobjectbuilder_p.h> #include <QCryptographicHash> #include "../../shared/util.h" @@ -55,6 +54,7 @@ private slots: void metaObjectSize_data(); void metaObjectSize(); void metaObjectChecksum(); + void metaObjectsForRootElements(); private: QQmlEngine engine; @@ -544,4 +544,14 @@ void tst_qqmlpropertycache::metaObjectChecksum() } } +void tst_qqmlpropertycache::metaObjectsForRootElements() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("noDuckType.qml")); + QVERIFY(c.isReady()); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("result").toString(), QString::fromLatin1("good")); +} + QTEST_MAIN(tst_qqmlpropertycache) diff --git a/tests/auto/qml/qqmlpropertymap/dummy_imports.qml b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml new file mode 100644 index 0000000000..4ae9d3f2cf --- /dev/null +++ b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in the +// C++ code belonging to the test. + +import QtQuick 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro b/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro index 8da300171d..b83e1e0da2 100644 --- a/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro +++ b/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro @@ -7,3 +7,5 @@ SOURCES += tst_qqmlpropertymap.cpp include (../../shared/util.pri) QT += core-private gui-private qml-private quick-private testlib + +TESTDATA = data/* diff --git a/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml new file mode 100644 index 0000000000..4ae9d3f2cf --- /dev/null +++ b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml @@ -0,0 +1,8 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in the +// C++ code belonging to the test. + +import QtQuick 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp index 68ed22c01c..a6f0d65453 100644 --- a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp +++ b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp @@ -78,7 +78,7 @@ void tst_qqmlstatemachine::tst_cppObjectSignal() CppObject cppObject; QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("cppsignal.qml")); - QVERIFY(!component.isError()); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("_cppObject", &cppObject); diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml new file mode 100644 index 0000000000..00e1fa65a7 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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 Qt.labs.qmlmodels 1.0 + +import "TestUtils.js" as TestUtils + +TableModel { + id: testModel + objectName: "testModel" + + TableModelColumn { display: "name" } + TableModelColumn { display: "age" } + + rows: [ + { + name: "John", + age: 22 + }, + { + name: "Oliver", + age: 33 + } + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js new file mode 100644 index 0000000000..0b92a377bb --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +function testModelRoleDataProvider(index, role, cellData) { + switch (role) { + case "display": + switch (index.column) { + case 0: + return cellData.name + case 1: + return cellData.age + } + break + case "name": + return cellData.name + case "age": + return cellData.age + } + return cellData +} diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml new file mode 100644 index 0000000000..2f8b0c072b --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/common.qml @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.13 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function appendRow(personName, personAge) { + testModel.appendRow({ + name: personName, + age: personAge + }) + } + + function appendRowExtraData() { + testModel.appendRow({ + name: "Foo", + age: 99, + nonExistentRole: 123 + }) + } + + function appendRowInvalid1() { + testModel.appendRow(123) + } + + function appendRowInvalid2() { + testModel.appendRow({ + name: "Foo", + age: [] + }) + } + + function appendRowInvalid3() { + testModel.appendRow([ + { name: "Bar" }, + { age: "111" } + ]) + } + + function insertRow(personName, personAge, rowIndex) { + testModel.insertRow(rowIndex, { + name: personName, + age: personAge + }) + } + + function insertRowExtraData() { + testModel.insertRow(0, { + name: "Foo", + age: 99, + nonExistentRole: 123 + }) + } + + function insertRowInvalid1() { + testModel.insertRow(0, 123) + } + + function insertRowInvalid2() { + testModel.insertRow(0, { + name: "Foo", + age: [] + }) + } + + function insertRowInvalid3() { + testModel.insertRow(0, [ + { name: "Bar" }, + { age: "111" } + ]) + } + + function setRow(rowIndex, personName, personAge) { + testModel.setRow(rowIndex, { + name: personName, + age: personAge + }) + } + + function setRowExtraData() { + testModel.setRow(0, { + name: "Foo", + age: 99, + nonExistentRole: 123 + }) + } + + function setRowInvalid1() { + testModel.setRow(0, 123) + } + + function setRowInvalid2() { + testModel.setRow(0, { + name: "Foo", + age: [] + }) + } + + function setRowInvalid3() { + testModel.setRow(0, [ + { name: "Bar" }, + { age: "111" } + ]) + } + + TableView { + id: tableView + anchors.fill: parent + model: TestModel { + id: testModel + } + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/complex.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml new file mode 100644 index 0000000000..dbf53bac7e --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/complex.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.13 +import Qt.labs.qmlmodels 1.0 + +TableView { + width: 100 + height: 100 + delegate: Item { + implicitWidth: 50 + implicitHeight: 50 + + Text { + text: model.display + anchors.centerIn: parent + } + } + model: TableModel { + id: testModel + objectName: "testModel" + + TableModelColumn { + display: function(modelIndex) { return testModel.rows[modelIndex.row][0].name } + setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][0].name = cellData } + } + TableModelColumn { + display: function(modelIndex) { return testModel.rows[modelIndex.row][1].age } + setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][1].age = cellData } + } + + rows: [ + [ + { name: "John" }, + { age: 22 } + ], + [ + { name: "Oliver" }, + { age: 33 } + ] + ] + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml new file mode 100644 index 0000000000..d3f726bfa1 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.12 +import Qt.labs.qmlmodels 1.0 + +TableView { + width: 200; height: 200 + model: TestModel { + id: testModel + + // This is silly: in real life, store the birthdate instead of the age, + // and let the delegate calculate the age, so it won't need updating + function happyBirthday(dude) { + var row = -1; + for (var r = 0; row < 0 && r < testModel.rowCount; ++r) + if (testModel.data(testModel.index(r, 0), "display") === dude) + row = r; + var index = testModel.index(row, 1) + testModel.setData(index, "display", testModel.data(index, "display") + 1) + } + } + delegate: Text { + id: textItem + text: model.display + TapHandler { + onTapped: testModel.happyBirthday(testModel.data(testModel.index(row, 0), "display")) + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml new file mode 100644 index 0000000000..f5afbd1d27 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/empty.qml @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function setRows() { + testModel.rows = [ + { + name: "John", + age: 22 + }, + { + name: "Oliver", + age: 33 + } + ] + } + + function appendJohn() { + testModel.appendRow({ + name: "John", + age: 22 + }) + } + + function appendOliver() { + testModel.appendRow({ + name: "Oliver", + age: 33 + }) + } + + TableModel { + id: testModel + objectName: "testModel" + + TableModelColumn { display: "name" } + TableModelColumn { display: "age" } + } + TableView { + id: tableView + anchors.fill: parent + model: testModel + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml new file mode 100644 index 0000000000..86bcb08fa2 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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 Qt.labs.qmlmodels 1.0 + +TableModel { + objectName: "testModel" + + TableModelColumn { display: "name" } + TableModelColumn { display: "age" } + + rows: [ + { + name: "John", + age: 22 + }, + { + name: "Oliver", + age: 33 + } + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml new file mode 100644 index 0000000000..ebfe4ed930 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + + signal shouldModify() + signal shouldModifyInvalidRole() + signal shouldModifyInvalidType() + + function modify() { + shouldModify() + } + + function modifyInvalidRole() { + shouldModifyInvalidRole() + } + + function modifyInvalidType() { + shouldModifyInvalidType() + } + + TableView { + anchors.fill: parent + model: TestModel { + id: testModel + } + + delegate: Text { + id: textItem + text: model.display + + Connections { + target: root + enabled: column === 1 + onShouldModify: model.display = 18 + } + + Connections { + target: root + enabled: column === 0 + // Invalid: should be "display". + onShouldModifyInvalidRole: model.age = 100 + } + + Connections { + target: root + enabled: column === 1 + // Invalid: should be string. + onShouldModifyInvalidType: model.display = "Whoops" + } + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml new file mode 100644 index 0000000000..01ec40270c --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function setRowsValid() { + testModel.rows = [ + { + name: "Max", + age: 20 + }, + { + name: "Imum", + age: 41 + }, + { + name: "Power", + age: 89 + } + ] + } + + function setRowsInvalid() { + testModel.rows = [ + { + nope: "Nope", + age: 20 + }, + { + nope: "Nah", + age: 41 + }, + { + nope: "No", + age: 89 + } + ] + } + + TableView { + id: tableView + anchors.fill: parent + model: TestModel { + id: testModel + } + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro new file mode 100644 index 0000000000..9d298dfdf2 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmltablemodel + +SOURCES += tst_qqmltablemodel.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core gui qml-private qml quick-private quick testlib qmlmodels-private diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp new file mode 100644 index 0000000000..d913bcdf9a --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp @@ -0,0 +1,980 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite 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$ +** +****************************************************************************/ + +#include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> +#include <QtCore/qregularexpression.h> +#include <QtQml/private/qqmlengine_p.h> +#include <QtQmlModels/private/qqmltablemodel_p.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/private/qquicktableview_p.h> + +#include "../../shared/util.h" + +class tst_QQmlTableModel : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QQmlTableModel() {} + +private slots: + void appendRemoveRow(); + void appendRowToEmptyModel(); + void clear(); + void getRow(); + void insertRow(); + void moveRow(); + void setRow(); + void setDataThroughDelegate(); + void setRowsImperatively(); + void setRowsMultipleTimes(); + void dataAndEditing(); + void omitTableModelColumnIndex(); + void complexRow(); +}; + +void tst_QQmlTableModel::appendRemoveRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 1); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + + // Call remove() with a negative rowIndex. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, -1))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() with an rowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*removeRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 2))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() with a valid rowIndex but negative rows. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rows\" is less than or equal to zero")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, -1))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() with a valid rowIndex but excessive rows. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*removeRow\\(\\): \"rows\" 3 exceeds available rowCount\\(\\) of 2 when removing from \"rowIndex\" 0")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call remove() without specifying the number of rows to remove; it should remove one row. + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0))); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + + // Call append() with a row that has an unexpected role; the row should be added and the extra data ignored. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowExtraData")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + + // Call append() with a row that is an int. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid1")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call append() with a row with a role of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid2")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call append() with a row that is an array instead of a simple object. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): row manipulation functions do not support complex rows \\(row index: -1\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid3")); + // Nothing should change. + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Call append() to insert one row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + + // Call remove() and specify rowIndex and rows, removing all remaining rows. + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3))); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); +} + +void tst_QQmlTableModel::appendRowToEmptyModel() +{ + QQuickView view(testFileUrl("empty.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendJohn")); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 1); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::clear() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(roleNames.size(), 1); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(model, "clear")); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + // Wait until updatePolish() gets called, which is where the size is recalculated. + QTRY_COMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::getRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + // Call get() with a negative row index. + QVariant returnValue; + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*getRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, -1))); + QVERIFY(!returnValue.isValid()); + + // Call get() with a row index that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*getRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 2))); + QVERIFY(!returnValue.isValid()); + + // Call get() with a valid row index. + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 0))); + const QVariantMap rowAsVariantMap = returnValue.toMap(); + QCOMPARE(rowAsVariantMap.value(QLatin1String("name")).toString(), QLatin1String("John")); + QCOMPARE(rowAsVariantMap.value(QLatin1String("age")).toInt(), 22); +} + +void tst_QQmlTableModel::insertRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert with a negative index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, -1))); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert past the last allowed index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Call insert() with a row that is an int. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid1")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row with a role of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid2")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is an array instead of a simple object. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid3")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row has an unexpected role; the row should be added and the extra data ignored. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowExtraData")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); + + // Insert a row at the bottom of the table. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3))); + QCOMPARE(model->rowCount(), 4); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 4); + QCOMPARE(tableView->columns(), 2); + + // Insert a row in the middle of the table. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30), Q_ARG(QVariant, 2))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 5); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::moveRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + const QHash<int, QByteArray> roleNames = model->roleNames(); + + // Append some rows. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30))); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Trev")), Q_ARG(QVariant, 48))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + rowCountSignalEmissions = 3; + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a fromRowIndex that is negative. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, -1), Q_ARG(int, 1))); + // Shouldn't have changed. + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a fromRowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" 5 is greater than or equal to rowCount\\(\\)")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 5), Q_ARG(int, 1))); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a toRowIndex that is negative. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, -1))); + // Shouldn't have changed. + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Try to move with a toRowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" 5 is greater than or equal to rowCount\\(\\)")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 5))); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Move the first row to the end. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 4))); + // The counts shouldn't have changed. + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Move it back again. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 4), Q_ARG(int, 0))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + + // Move the first row down one by one row. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 1))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); +} + +void tst_QQmlTableModel::setRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int rowCountSignalEmissions = 0; + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to set with a negative index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, -1), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to set at an index past the last allowed index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 3), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to set a row that has an unexpected role; the row should be set and the extra data ignored. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowExtraData")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is not an array. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid1")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row with a role that is of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid2")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is an array instead of a simple object. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid3")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set the first row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 0), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set the last row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 1), Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Append a row by passing an index that is equal to rowCount(). + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 2), Q_ARG(QVariant, QLatin1String("Wot")), Q_ARG(QVariant, 99))); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Wot")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::setDataThroughDelegate() +{ + QQuickView view(testFileUrl("setDataThroughDelegate.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 1); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modify")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + // Test setting a role that doesn't exist for a certain column. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidRole")); + // Should be unchanged. + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + // Test setting a role with a value of the wrong type. + // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \ + "set at row 0 column 1 with role \"display\" to \"int\"")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \ + "set at row 1 column 1 with role \"display\" to \"int\"")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidType")); + // Should be unchanged. + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); +} + +// Start off with empty rows and then set them to test rowCountChanged(). +void tst_QQmlTableModel::setRowsImperatively() +{ + QQuickView view(testFileUrl("empty.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRows")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::setRowsMultipleTimes() +{ + QQuickView view(testFileUrl("setRowsMultipleTimes.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set valid rows after they've already been declared. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsValid")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); + + // Set invalid rows; we should get a warning and nothing should change. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRows\\(\\): expected a property named \"name\" in row at index 0, but couldn't find one")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsInvalid")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QCOMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::dataAndEditing() +{ + QQuickView view(testFileUrl("dataAndSetData.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>(); + QVERIFY(model); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QVERIFY(QMetaObject::invokeMethod(model, "happyBirthday", Q_ARG(QVariant, QLatin1String("Oliver")))); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 34); +} + +void tst_QQmlTableModel::omitTableModelColumnIndex() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("omitTableModelColumnIndex.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + + QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create())); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); +} + +void tst_QQmlTableModel::complexRow() +{ + QQuickView view(testFileUrl("complex.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQuickTableView *tableView = qobject_cast<QQuickTableView*>(view.rootObject()); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + QQmlTableModel *model = tableView->model().value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); +} + +QTEST_MAIN(tst_QQmlTableModel) + +#include "tst_qqmltablemodel.moc" diff --git a/tests/auto/qml/qqmltimer/dummy_imports.qml b/tests/auto/qml/qqmltimer/dummy_imports.qml new file mode 100644 index 0000000000..f78e04d489 --- /dev/null +++ b/tests/auto/qml/qqmltimer/dummy_imports.qml @@ -0,0 +1,9 @@ +// This file exists for the sole purpose for qmlimportscanner to find +// which modules it needs to extract for deployment. +// Otherwise, it fails to find the imports that are expressed in the +// C++ code belonging to the test. + +import QtQml 2.0 +import QtQuick 2.0 + +QtObject { } // This is needed in order to keep importscanner happy diff --git a/tests/auto/qml/qqmltranslation/data/mylibrary.js b/tests/auto/qml/qqmltranslation/data/mylibrary.js new file mode 100644 index 0000000000..5903db3b4b --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/mylibrary.js @@ -0,0 +1,5 @@ +Qt.include("nested_js_translation.js") + +function translation_success() { + return qsTr("English in mylibrary"); +} diff --git a/tests/auto/qml/qqmltranslation/data/nested_js_translation.js b/tests/auto/qml/qqmltranslation/data/nested_js_translation.js new file mode 100644 index 0000000000..336cdedfea --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/nested_js_translation.js @@ -0,0 +1,3 @@ +function translation_fail() { + return qsTr("English in translation") +} diff --git a/tests/auto/qml/qqmltranslation/data/preferjs.qml b/tests/auto/qml/qqmltranslation/data/preferjs.qml new file mode 100644 index 0000000000..040fa12e4e --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/preferjs.qml @@ -0,0 +1,8 @@ +import QtQml 2.12 + +import "mylibrary.js" as Lib + +QtObject { + property string german1: Lib.translation_fail() + property string german2: Lib.translation_success() +} diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 809a9bd9db..a75a00bd01 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -32,6 +32,7 @@ #include <QTranslator> #include <QQmlContext> #include <private/qqmlengine_p.h> +#include <private/qqmltypedata_p.h> #include "../../shared/util.h" class tst_qqmltranslation : public QQmlDataTest @@ -45,6 +46,7 @@ private slots: void translation(); void idTranslation(); void translationChange(); + void preferJSContext(); }; void tst_qqmltranslation::translation_data() @@ -85,7 +87,8 @@ void tst_qqmltranslation::translation() << QStringLiteral("disambiguation") << QStringLiteral("singular") << QStringLiteral("plural"); - const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); + const QV4::CompiledData::Object *rootObject + = compilationUnit->qmlData->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); @@ -139,7 +142,8 @@ void tst_qqmltranslation::idTranslation() QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); QVERIFY(compilationUnit); - const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); + const QV4::CompiledData::Object *rootObject + = compilationUnit->qmlData->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); @@ -172,6 +176,10 @@ class DummyTranslator : public QTranslator Q_UNUSED(n); if (!qstrcmp(sourceText, "translate me")) return QString::fromUtf8("xxx"); + if (!qstrcmp(sourceText, "English in mylibrary") && !qstrcmp(context, "mylibrary")) + return QString::fromUtf8("Deutsch in mylibrary"); + if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation")) + return QString::fromUtf8("Deutsch in Setzung"); return QString(); } @@ -210,6 +218,24 @@ void tst_qqmltranslation::translationChange() QCoreApplication::removeTranslator(&translator); } +void tst_qqmltranslation::preferJSContext() +{ + DummyTranslator translator; + QCoreApplication::installTranslator(&translator); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("preferjs.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("german1").toString(), + QStringLiteral("Deutsch in Setzung")); + QCOMPARE(object->property("german2").toString(), + QStringLiteral("Deutsch in mylibrary")); + + QCoreApplication::removeTranslator(&translator); +} + QTEST_MAIN(tst_qqmltranslation) #include "tst_qqmltranslation.moc" diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml b/tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml new file mode 100644 index 0000000000..28521e3af2 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml @@ -0,0 +1,5 @@ +import QtQuick 2.6 + +Item { + +} diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml new file mode 100644 index 0000000000..b20a2def11 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml @@ -0,0 +1,7 @@ +import QtQuick 2.6 +import Com.Orga 1.0 + +Rectangle { + color: Style.name + Text {text: "Hello world!"} +} diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir new file mode 100644 index 0000000000..368cb65b35 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir @@ -0,0 +1,2 @@ +module Com.Orga.Handlers +Handler 1.0 Handler.qml diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml new file mode 100644 index 0000000000..7951f5e768 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick 2.6 + +BaseStyle { + property color name: "black" +} diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir b/tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir new file mode 100644 index 0000000000..9c5560b323 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir @@ -0,0 +1,2 @@ +singleton Style 1.0 Style.qml +BaseStyle 1.0 BaseStyle.qml diff --git a/tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml b/tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml new file mode 100644 index 0000000000..7a054e199b --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml @@ -0,0 +1,5 @@ +import modulewithimplicitimport 2.0 as MyNS +MyNS.Test { + MyNS.Item {} // Implicitly imported from QtQuick + MyNS.ListModel {} +} diff --git a/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml new file mode 100644 index 0000000000..2f78302506 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml @@ -0,0 +1,3 @@ +import QtQuick 2.0 +Item { +} diff --git a/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir new file mode 100644 index 0000000000..10e8f90f62 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir @@ -0,0 +1,2 @@ +import QtQuick +Test 2.0 Test.qml diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 52c722aac8..9ad53aaa8b 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlfile.h> #include <QtQml/qqmlnetworkaccessmanagerfactory.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> @@ -35,6 +36,7 @@ #include <QtCore/qprocess.h> #endif #include <QtQml/private/qqmlengine_p.h> +#include <QtQml/private/qqmltypedata_p.h> #include <QtQml/private/qqmltypeloader_p.h> #include "../../shared/testhttpserver.h" #include "../../shared/util.h" @@ -57,6 +59,8 @@ private slots: void multiSingletonModule(); void implicitComponentModule(); void qrcRootPathUrl(); + void implicitImport(); + void compositeSingletonCycle(); }; void tst_QQMLTypeLoader::testLoadComplete() @@ -431,7 +435,7 @@ void tst_QQMLTypeLoader::redirect() component.loadUrl(server.urlString("/Load.qml"), QQmlComponent::Asynchronous); QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString())); - QObject *object = component.create(); + QScopedPointer<QObject> object {component.create()}; QTRY_COMPARE(object->property("xy").toInt(), 323232); } @@ -511,6 +515,33 @@ void tst_QQMLTypeLoader::qrcRootPathUrl() QCOMPARE(component.status(), QQmlComponent::Ready); } +void tst_QQMLTypeLoader::implicitImport() +{ + QQmlEngine engine; + engine.addImportPath(testFile("imports")); + QQmlComponent component(&engine, testFileUrl("implicitimporttest.qml")); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); +} + +void tst_QQMLTypeLoader::compositeSingletonCycle() +{ + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.serveDirectory(dataDirectory())); + + QQmlEngine engine; + QQmlComponent component(&engine); + engine.addImportPath(server.baseUrl().toString()); + component.loadUrl(server.urlString("Com/Orga/Handlers/Handler.qml"), QQmlComponent::Asynchronous); + QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString())); + + QScopedPointer<QObject> object {component.create()}; + QVERIFY(object); + QCOMPARE(qvariant_cast<QColor>(object->property("color")), QColorConstants::Black); +} + QTEST_MAIN(tst_QQMLTypeLoader) #include "tst_qqmltypeloader.moc" diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml index 73d2b921a7..a2d303b507 100644 --- a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml +++ b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml @@ -12,4 +12,7 @@ MyTypeObject { property real hsl_s: color.hslSaturation property real hsl_l: color.hslLightness property variant copy: color + + property bool valid: color.valid + property bool invalid: invalidColor.valid } diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h index bcfe4028c6..798c96e188 100644 --- a/tests/auto/qml/qqmlvaluetypes/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h @@ -69,6 +69,7 @@ class MyTypeObject : public QObject Q_PROPERTY(QMatrix4x4 matrix READ matrix WRITE setMatrix NOTIFY changed) Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed) + Q_PROPERTY(QColor invalidColor READ invalidColor CONSTANT) Q_PROPERTY(QVariant variant READ variant NOTIFY changed) public: @@ -168,6 +169,8 @@ public: QColor color() const { return m_color; } void setColor(const QColor &v) { m_color = v; emit changed(); } + QColor invalidColor() const { return QColor(); } + QVariant variant() const { return sizef(); } void emitRunScript() { emit runScript(); } diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index 15bd031ce3..3e9047cc5a 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -97,6 +97,7 @@ private slots: void enumerableProperties(); void enumProperties(); void scarceTypes(); + void nonValueTypes(); private: QQmlEngine engine; @@ -784,6 +785,7 @@ void tst_qqmlvaluetypes::font() { QQmlComponent component(&engine, testFileUrl("font_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); QVERIFY(object != nullptr); QCOMPARE(object->property("f_family").toString(), object->font().family()); @@ -793,8 +795,19 @@ void tst_qqmlvaluetypes::font() QCOMPARE(object->property("f_underline").toBool(), object->font().underline()); QCOMPARE(object->property("f_overline").toBool(), object->font().overline()); QCOMPARE(object->property("f_strikeout").toBool(), object->font().strikeOut()); - QCOMPARE(object->property("f_pointSize").toDouble(), object->font().pointSizeF()); - QCOMPARE(object->property("f_pixelSize").toInt(), int((object->font().pointSizeF() * qt_defaultDpi()) / qreal(72.))); + + // If QFont::pixelSize() was set, QFont::pointSizeF() would return -1. + // If QFont::pointSizeF() was set, QFont::pixelSize() would return -1. + // QQuickFontValueType doesn't follow this semantic (if its -1 it calculates the value of + // the property from the other one) + double expectedPointSizeF = object->font().pointSizeF(); + if (expectedPointSizeF == -1) expectedPointSizeF = object->font().pixelSize() * qreal(72.) / qreal(qt_defaultDpi()); + int expectedPixelSize = object->font().pixelSize(); + if (expectedPixelSize == -1) expectedPixelSize = int((object->font().pointSizeF() * qt_defaultDpi()) / qreal(72.)); + + QCOMPARE(object->property("f_pointSize").toDouble(), expectedPointSizeF); + QCOMPARE(object->property("f_pixelSize").toInt(), expectedPixelSize); + QCOMPARE(object->property("f_capitalization").toInt(), (int)object->font().capitalization()); QCOMPARE(object->property("f_letterSpacing").toDouble(), object->font().letterSpacing()); QCOMPARE(object->property("f_wordSpacing").toDouble(), object->font().wordSpacing()); @@ -922,6 +935,11 @@ void tst_qqmlvaluetypes::color() QCOMPARE(qRound(object->property("hsl_s").toDouble() * 100), 74); QCOMPARE(qRound(object->property("hsl_l").toDouble() * 100), 54); + QCOMPARE(object->property("valid").userType(), QMetaType::Bool); + QVERIFY(object->property("valid").toBool()); + QCOMPARE(object->property("invalid").userType(), QMetaType::Bool); + QVERIFY(!object->property("invalid").toBool()); + QColor comparison; comparison.setRedF(0.2); comparison.setGreenF(0.88); @@ -1713,7 +1731,7 @@ void tst_qqmlvaluetypes::sequences() QJSValue value = engine.toScriptValue(qcharVector); QCOMPARE(value.property("length").toInt(), qcharVector.length()); for (int i = 0; i < qcharVector.length(); ++i) - QCOMPARE(value.property(i).toInt(), qcharVector.at(i)); + QCOMPARE(value.property(i).toString(), qcharVector.at(i)); } { MyTypeObject a, b, c; @@ -1832,6 +1850,16 @@ void tst_qqmlvaluetypes::scarceTypes() QCOMPARE(QByteArray(pixmapValue->vtable()->className), QByteArray("VariantObject")); } +#define CHECK_TYPE_IS_NOT_VALUETYPE(Type, typeId, cppType) \ + QVERIFY(!QQmlValueTypeFactory::isValueType(QMetaType::Type)); + +void tst_qqmlvaluetypes::nonValueTypes() +{ + CHECK_TYPE_IS_NOT_VALUETYPE(UnknownType, 0, void) + QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(CHECK_TYPE_IS_NOT_VALUETYPE); +} + +#undef CHECK_TYPE_IS_NOT_VALUETYPE QTEST_MAIN(tst_qqmlvaluetypes) diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp index 4b2ae45bae..ae99e35467 100644 --- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp +++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp @@ -75,6 +75,7 @@ private slots: void introspectQrc(); void sortCaseSensitive_data(); void sortCaseSensitive(); + void updateProperties(); private: void checkNoErrors(const QQmlComponent& component); QQmlEngine engine; @@ -112,6 +113,10 @@ void tst_qquickfolderlistmodel::initTestCase() void tst_qquickfolderlistmodel::basicProperties() { +#ifdef Q_OS_ANDROID + QSKIP("[QTBUG-77335] Initial folder of FolderListModel on Android does not work properly," + " and from there on it is unreliable to change the folder"); +#endif QQmlComponent component(&engine, testFileUrl("basic.qml")); checkNoErrors(component); @@ -356,6 +361,9 @@ void tst_qquickfolderlistmodel::showDotAndDotDot() void tst_qquickfolderlistmodel::showDotAndDotDot_data() { +#ifdef Q_OS_ANDROID + QSKIP("Resource file system does not list '.' and '..' due to QDir::entryList() behavior"); +#endif QTest::addColumn<QUrl>("folder"); QTest::addColumn<QUrl>("rootFolder"); QTest::addColumn<bool>("showDotAndDotDot"); @@ -411,13 +419,54 @@ void tst_qquickfolderlistmodel::sortCaseSensitive() QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); QVERIFY(flm != 0); - flm->setProperty("folder", QUrl::fromLocalFile(dataDirectoryUrl().path() + QLatin1String("/sortdir"))); + flm->setProperty("folder", testFileUrl("sortdir")); flm->setProperty("sortCaseSensitive", sortCaseSensitive); QTRY_COMPARE(flm->property("count").toInt(), 2); // wait for refresh for (int i = 0; i < 2; ++i) QTRY_COMPARE(flm->data(flm->index(i),FileNameRole).toString(), expectedOrder.at(i)); } +void tst_qquickfolderlistmodel::updateProperties() +{ + QQmlComponent component(&engine, testFileUrl("basic.qml")); + checkNoErrors(component); + + QObject *folderListModel = component.create(); + QVERIFY(folderListModel); + + QVariant caseSensitive = folderListModel->property("caseSensitive"); + QVERIFY(caseSensitive.isValid()); + QCOMPARE(caseSensitive.toBool(), true); + folderListModel->setProperty("caseSensitive", false); + caseSensitive = folderListModel->property("caseSensitive"); + QVERIFY(caseSensitive.isValid()); + QCOMPARE(caseSensitive.toBool(), false); + + QVariant showOnlyReadable = folderListModel->property("showOnlyReadable"); + QVERIFY(showOnlyReadable.isValid()); + QCOMPARE(showOnlyReadable.toBool(), false); + folderListModel->setProperty("showOnlyReadable", true); + showOnlyReadable = folderListModel->property("showOnlyReadable"); + QVERIFY(showOnlyReadable.isValid()); + QCOMPARE(showOnlyReadable.toBool(), true); + + QVariant showDotAndDotDot = folderListModel->property("showDotAndDotDot"); + QVERIFY(showDotAndDotDot.isValid()); + QCOMPARE(showDotAndDotDot.toBool(), false); + folderListModel->setProperty("showDotAndDotDot", true); + showDotAndDotDot = folderListModel->property("showDotAndDotDot"); + QVERIFY(showDotAndDotDot.isValid()); + QCOMPARE(showDotAndDotDot.toBool(), true); + + QVariant showHidden = folderListModel->property("showHidden"); + QVERIFY(showHidden.isValid()); + QCOMPARE(showHidden.toBool(), false); + folderListModel->setProperty("showHidden", true); + showHidden = folderListModel->property("showHidden"); + QVERIFY(showHidden.isValid()); + QCOMPARE(showHidden.toBool(), true); +} + QTEST_MAIN(tst_qquickfolderlistmodel) #include "tst_qquickfolderlistmodel.moc" diff --git a/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro b/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro index be8b9089a2..f58fd4543f 100644 --- a/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro +++ b/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro @@ -8,4 +8,4 @@ include (../../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private testlib +QT += core-private gui-private qml-private testlib qmlworkerscript-private diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp index 4ad58ba56c..bea9978f0b 100644 --- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp +++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp @@ -30,6 +30,7 @@ #include <QtCore/qtimer.h> #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> +#include <QtCore/qregularexpression.h> #include <QtQml/qjsengine.h> #include <QtQml/qqmlcomponent.h> @@ -91,14 +92,14 @@ void tst_QQuickWorkerScript::source() QCOMPARE(worker->source(), source); QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value))); waitForEchoMessage(worker.data()); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello_World"))); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), QVariant::fromValue(QString("Hello_World"))); source = testFileUrl("script_module.mjs"); worker->setSource(source); QCOMPARE(worker->source(), source); QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value))); waitForEchoMessage(worker.data()); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello from the module"))); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), QVariant::fromValue(QString("Hello from the module"))); qApp->processEvents(); } @@ -118,7 +119,18 @@ void tst_QQuickWorkerScript::messaging() QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(); if (response.userType() == qMetaTypeId<QJSValue>()) response = response.value<QJSValue>().toVariant(); - QCOMPARE(response, value); + + if (value.type() == QMetaType::QRegExp && response.type() == QMetaType::QRegularExpression) { + // toVariant() doesn't know if we want QRegExp or QRegularExpression. It always creates + // a QRegularExpression from a JavaScript regular expression. + const QRegularExpression responseRegExp = response.toRegularExpression(); + const QRegExp valueRegExp = value.toRegExp(); + QCOMPARE(responseRegExp.pattern(), valueRegExp.pattern()); + QCOMPARE(bool(responseRegExp.patternOptions() & QRegularExpression::CaseInsensitiveOption), + bool(valueRegExp.caseSensitivity() == Qt::CaseInsensitive)); + } else { + QCOMPARE(response, value); + } qApp->processEvents(); delete worker; @@ -129,16 +141,16 @@ void tst_QQuickWorkerScript::messaging_data() QTest::addColumn<QVariant>("value"); QTest::newRow("invalid") << QVariant(); - QTest::newRow("bool") << qVariantFromValue(true); - QTest::newRow("int") << qVariantFromValue(1001); - QTest::newRow("real") << qVariantFromValue(10334.375); - QTest::newRow("string") << qVariantFromValue(QString("More cheeeese, Gromit!")); - QTest::newRow("variant list") << qVariantFromValue((QVariantList() << "a" << "b" << "c")); - QTest::newRow("date time") << qVariantFromValue(QDateTime::currentDateTime()); -#ifndef QT_NO_REGEXP - // Qt Script's QScriptValue -> QRegExp uses RegExp2 pattern syntax - QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, QRegExp::RegExp2)); -#endif + QTest::newRow("bool") << QVariant::fromValue(true); + QTest::newRow("int") << QVariant::fromValue(1001); + QTest::newRow("real") << QVariant::fromValue(10334.375); + QTest::newRow("string") << QVariant::fromValue(QString("More cheeeese, Gromit!")); + QTest::newRow("variant list") << QVariant::fromValue((QVariantList() << "a" << "b" << "c")); + QTest::newRow("date time") << QVariant::fromValue(QDateTime::currentDateTime()); + QTest::newRow("regexp") << QVariant::fromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, + QRegExp::RegExp2)); + QTest::newRow("regularexpression") << QVariant::fromValue(QRegularExpression( + "^\\d\\d?$", QRegularExpression::CaseInsensitiveOption)); } void tst_QQuickWorkerScript::messaging_sendQObjectList() @@ -153,9 +165,9 @@ void tst_QQuickWorkerScript::messaging_sendQObjectList() QVariantList objects; for (int i=0; i<3; i++) - objects << qVariantFromValue(new QObject(this)); + objects << QVariant::fromValue(new QObject(this)); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(objects)))); + QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(objects)))); waitForEchoMessage(worker); const QMetaObject *mo = worker->metaObject(); @@ -181,10 +193,10 @@ void tst_QQuickWorkerScript::messaging_sendJsObject() map.insert("name", "zyz"); map.insert("spell power", 3101); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(map)))); + QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(map)))); waitForEchoMessage(worker); - QVariant result = qVariantFromValue(false); + QVariant result = QVariant::fromValue(false); QVERIFY(QMetaObject::invokeMethod(worker, "compareLiteralResponse", Qt::DirectConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, jsObject))); QVERIFY(result.toBool()); diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp index fd50ff5020..5dd8e9dcc0 100644 --- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp +++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp @@ -59,7 +59,7 @@ void tst_QV4Assembler::initTestCase() void tst_QV4Assembler::perfMapFile() { -#if !defined(Q_OS_LINUX) +#if !defined(Q_OS_LINUX) || defined(Q_OS_ANDROID) QSKIP("perf map files are only generated on linux"); #else const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs"; @@ -140,23 +140,17 @@ void tst_QV4Assembler::functionTable() #endif } -#ifdef V4_ENABLE_JIT -#define JIT_ENABLED 1 -#else -#define JIT_ENABLED 0 -#endif - void tst_QV4Assembler::jitEnabled() { #if defined(Q_OS_IOS) || defined(Q_OS_TVOS) /* JIT should be disabled on iOS and tvOS. */ - QCOMPARE(JIT_ENABLED, 0); + QVERIFY(!QT_CONFIG(qml_jit)); #elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM) /* JIT should be disabled Windows on ARM/ARM64 for now. */ - QCOMPARE(JIT_ENABLED, 0); + QVERIFY(!QT_CONFIG(qml_jit)); #else /* JIT should be enabled on all other architectures/OSes tested in CI. */ - QCOMPARE(JIT_ENABLED, 1); + QVERIFY(QT_CONFIG(qml_jit)); #endif } diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp index 308fba9049..157d0f2a62 100644 --- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp +++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2016 basysKom GmbH. ** Contact: https://www.qt.io/licensing/ ** @@ -110,8 +111,8 @@ void tst_qv4identifiertable::sweepCenterEntryInBucket() table.asPropertyKey(entry2); table.asPropertyKey(entry3); - QCOMPARE(table.size, 3); - QCOMPARE(table.alloc, 5); + QCOMPARE(table.size, 3u); + QCOMPARE(table.alloc, 5u); QCOMPARE(table.entriesByHash[0], entry1); QCOMPARE(table.entriesByHash[1], entry2); @@ -153,8 +154,8 @@ void tst_qv4identifiertable::sweepLastEntryInBucket() table.asPropertyKey(entry2); table.asPropertyKey(entry3); - QCOMPARE(table.size, 3); - QCOMPARE(table.alloc, 5); + QCOMPARE(table.size, 3u); + QCOMPARE(table.alloc, 5u); QCOMPARE(table.entriesByHash[0], entry1); QCOMPARE(table.entriesByHash[1], entry2); @@ -193,8 +194,8 @@ void tst_qv4identifiertable::sweepFirstEntryInSameBucketWithDifferingHash() table.asPropertyKey(entry1); table.asPropertyKey(entry2); - QCOMPARE(table.size, 2); - QCOMPARE(table.alloc, 5); + QCOMPARE(table.size, 2u); + QCOMPARE(table.alloc, 5u); QCOMPARE(table.entriesByHash[0], entry1); QCOMPARE(table.entriesByHash[1], entry2); @@ -231,8 +232,8 @@ void tst_qv4identifiertable::dontSweepAcrossBucketBoundaries() table.asPropertyKey(entry1); table.asPropertyKey(entry2); - QCOMPARE(table.size, 2); - QCOMPARE(table.alloc, 5); + QCOMPARE(table.size, 2u); + QCOMPARE(table.alloc, 5u); QCOMPARE(table.entriesByHash[0], entry1); QCOMPARE(table.entriesByHash[1], entry2); @@ -279,8 +280,8 @@ void tst_qv4identifiertable::sweepAcrossBucketBoundariesIfFirstBucketFull() table.asPropertyKey(entry3); table.asPropertyKey(entry4); - QCOMPARE(table.size, 4); - QCOMPARE(table.alloc, 11); + QCOMPARE(table.size, 4u); + QCOMPARE(table.alloc, 11u); QCOMPARE(table.entriesByHash[0], entry1); QCOMPARE(table.entriesByHash[1], entry2); @@ -336,8 +337,8 @@ void tst_qv4identifiertable::sweepBucketGap() table.asPropertyKey(entry3); table.asPropertyKey(entry4); - QCOMPARE(table.size, 4); - QCOMPARE(table.alloc, 11); + QCOMPARE(table.size, 4u); + QCOMPARE(table.alloc, 11u); QCOMPARE(table.entriesByHash[0], entry1); QCOMPARE(table.entriesByHash[1], entry2); diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index 1e34b79954..5d635aa63b 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -136,7 +136,7 @@ void tst_qv4mm::clearICParent() // to change this test. for (uint i = 0; i < 16 * 1024; ++i) { QV4::Scope scope(&engine); - QV4::ScopedString s(scope, identifiers->getIndexed(i)); + QV4::ScopedString s(scope, identifiers->get(i)); QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass()); QVERIFY(ic->d()->parent != nullptr); object->deleteProperty(s->toPropertyKey()); diff --git a/tests/auto/qml/v4traced/tst_v4traced.cpp b/tests/auto/qml/v4traced/tst_v4traced.cpp deleted file mode 100644 index f82cc0ed5e..0000000000 --- a/tests/auto/qml/v4traced/tst_v4traced.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite 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$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include <QJSEngine> -#include <private/qjsvalue_p.h> -#include <private/qv4scopedvalue_p.h> -#include <private/qv4script_p.h> - -class EnvVarSaver -{ -public: - EnvVarSaver(const char *name, const QByteArray &newValue) - : _name(name) - { - _wasSet = qEnvironmentVariableIsSet(name); - if (_wasSet) - _oldValue = qgetenv(name); - qputenv(name, newValue); - } - - ~EnvVarSaver() - { - if (_wasSet) - qputenv(_name, _oldValue); - else - qunsetenv(_name); - } - -private: - const char *_name; - bool _wasSet; - QByteArray _oldValue; -}; - -class tst_v4traced : public QObject -{ - Q_OBJECT - -private slots: - void collectTraces_data(); - void collectTraces(); - - void binopI32deopt_data(); - void binopI32deopt(); - - void calls_data(); - void calls(); - - void setLookup(); - void construct(); -}; - -void tst_v4traced::collectTraces_data() -{ - QTest::addColumn<QString>("code"); - QTest::addColumn<int>("tracePointCount"); - QTest::addColumn<int>("interestingTracePoint"); - QTest::addColumn<quint8>("expectedBits"); - - QTest::newRow("int+") << "var a = 4; a + 2" << 2 << 1 << quint8(QV4::ObservedTraceValues::Integer); - QTest::newRow("double+") << "var a = 4.1; a + 1.9" << 2 << 1 << quint8(QV4::ObservedTraceValues::Double); - QTest::newRow("object+") << "var a = '4'; a + '2'" << 2 << 1 << quint8(QV4::ObservedTraceValues::Other); -} - -void tst_v4traced::collectTraces() -{ - QFETCH(QString, code); - QFETCH(int, tracePointCount); - QFETCH(int, interestingTracePoint); - QFETCH(quint8, expectedBits); - - EnvVarSaver forceInterpreter("QV4_FORCE_INTERPRETER", "1"); - EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); - - QV4::ExecutionEngine vm; - QV4::Scope scope(&vm); - QV4::ScopedContext ctx(scope, vm.rootContext()); - QV4::ScopedValue result(scope); - QScopedPointer<QV4::Script> script; - script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "collectTraces")); - script->parseAsBinding = false; - - QVERIFY(!scope.engine->hasException); - script->parse(); - QVERIFY(!scope.engine->hasException); - - QVERIFY(script->function()->tracingEnabled()); - result = script->run(); - QVERIFY(!scope.engine->hasException); - - QCOMPARE(int(script->function()->compiledFunction->nTraceInfos), tracePointCount); - QCOMPARE(*script->function()->traceInfo(interestingTracePoint), expectedBits); -} - -void tst_v4traced::binopI32deopt_data() -{ - QTest::addColumn<QString>("operand"); - QTest::addColumn<int>("int_arg1"); - QTest::addColumn<int>("int_arg2"); - QTest::addColumn<int>("int_result"); - QTest::addColumn<QString>("other_arg1"); - QTest::addColumn<QString>("other_arg2"); - QTest::addColumn<double>("other_result"); - - QTest::newRow("+") << "+" << 1 << 2 << 3 << "1.1" << "1.9" << 3.0; - QTest::newRow("-") << "-" << 3 << 2 << 1 << "3.1" << "2.1" << 1.0; - QTest::newRow("*") << "*" << 2 << 3 << 6 << "2.1" << "1.9" << 3.99; - QTest::newRow("/") << "/" << 6 << 3 << 2 << "6.6" << "3.3" << 2.0; - - QTest::newRow("&") << "&" << 6 << 3 << 2 << "'6'" << "'3'" << 2.0; - QTest::newRow("|") << "|" << 6 << 3 << 7 << "'6'" << "'3'" << 7.0; - QTest::newRow("^") << "^" << 6 << 3 << 5 << "'6'" << "'3'" << 5.0; - - QTest::newRow("<<") << "<<" << 5 << 1 << 10 << "'5'" << "'1'" << 10.0; - QTest::newRow(">>") << ">>" << -1 << 1 << -1 << "'-1'" << "'1'" << -1.0; - QTest::newRow(">>>") << ">>>" << -1 << 1 << 0x7FFFFFFF << "'-1'" << "'1'" << 2147483647.0; - - QTest::newRow("==") << "==" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0; - QTest::newRow("!=") << "!=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0; - QTest::newRow("<" ) << "<" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0; - QTest::newRow("<=") << "<=" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0; - QTest::newRow(">" ) << ">" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0; - QTest::newRow(">=") << ">=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0; -} - -void tst_v4traced::binopI32deopt() -{ - QFETCH(QString, operand); - QFETCH(int, int_arg1); - QFETCH(int, int_arg2); - QFETCH(int, int_result); - QFETCH(QString, other_arg1); - QFETCH(QString, other_arg2); - QFETCH(double, other_result); - - QString func = QStringLiteral("function binopI32(a, b) { return a %1 b }").arg(operand); - QString intCall = QStringLiteral("binopI32(%1, %2)").arg(int_arg1).arg(int_arg2); - QString otherCall = QStringLiteral("binopI32(%1, %2)").arg(other_arg1).arg(other_arg2); - - QJSEngine engine; - engine.evaluate(func); - - QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // interpret + trace - QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // jit - QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // deopt - QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // retrace - QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // rejit -} - -void tst_v4traced::calls_data() -{ - QTest::addColumn<QString>("call"); - - QTest::newRow("callGlobalLookup") << "globalLookup"; - QTest::newRow("callPropertyLookup") << "obj.propertyLookup"; -} - -class Calls -{ -public: - static int callCount; - - static QV4::ReturnedValue doSomething(const QV4::FunctionObject */*o*/, - const QV4::Value */*thiz*/, - const QV4::Value *argv, int argc) - { - ++callCount; - - if (argc == 0) - return QV4::Encode(42); - - int prod = 1; - for (int i = 0; i < argc; ++i) { - Q_ASSERT(argv[i].isInteger()); - prod *= argv[i].int_32(); - } - return QV4::Encode(prod); - } -}; - -int Calls::callCount = 0; - -void tst_v4traced::calls() -{ - QFETCH(QString, call); - - EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); - EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1"); - - QV4::ExecutionEngine vm; - QV4::Scope scope(&vm); - QV4::ScopedContext ctx(scope, vm.rootContext()); - vm.globalObject->defineDefaultProperty(QLatin1String("globalLookup"), - Calls::doSomething); - QV4::ScopedObject obj(scope, vm.newObject()); - vm.globalObject->defineDefaultProperty(QLatin1String("obj"), obj); - obj->defineDefaultProperty("propertyLookup", Calls::doSomething); - - QString code = QStringLiteral( - "function doCalls() {\n" - " if (%1() != 42) return false\n" - " if (%1(21, 2) != 42) return false\n" - " if (%1(2, 3, 7) != 42) return false\n" - " return true\n" - "}\n" - "var result = true\n" - "for (var i = 0; i < 10; ++i) {\n" - " if (!doCalls()) { result = false; break }" - "}\n" - "result\n").arg(call); - QScopedPointer<QV4::Script> script; - script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "call")); - script->parseAsBinding = false; - - QVERIFY(!scope.engine->hasException); - script->parse(); - QVERIFY(!scope.engine->hasException); - - Calls::callCount = 0; - QV4::ScopedValue result(scope, script->run()); - QVERIFY(!scope.engine->hasException); - - QVERIFY(result->isBoolean()); - QVERIFY(result->booleanValue()); - QCOMPARE(Calls::callCount, 30); -} - -void tst_v4traced::setLookup() -{ - EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); - EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1"); - - QV4::ExecutionEngine vm; - QV4::Scope scope(&vm); - QV4::ScopedContext ctx(scope, vm.rootContext()); - QV4::ScopedObject obj(scope, vm.newObject()); - vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj); - obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32)); - - QString code = QStringLiteral( - "function doit() {\n" - " ++oracle.answer\n" - "}\n" - "for (var i = 0; i < 10; ++i) doit()\n" - "oracle.answer\n"); - QScopedPointer<QV4::Script> script; - script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup")); - script->parseAsBinding = false; - - QVERIFY(!scope.engine->hasException); - script->parse(); - QVERIFY(!scope.engine->hasException); - - QV4::ScopedValue result(scope, script->run()); - QVERIFY(!scope.engine->hasException); - - QVERIFY(result->isInteger()); - QCOMPARE(result->int_32(), 42); -} - -void tst_v4traced::construct() -{ - EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); - EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1"); - - QV4::ExecutionEngine vm; - QV4::Scope scope(&vm); - QV4::ScopedContext ctx(scope, vm.rootContext()); - QV4::ScopedObject obj(scope, vm.newObject()); - vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj); - obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32)); - - QString code = QStringLiteral( - "function doit() {\n" - " this.arr = new Array()\n" - " this.arr[0] = 0\n" - " this.arr[1] = 1\n" - " this.arr[2] = 2\n" - "}\n" - "var o\n" - "for (var i = 0; i < 10; ++i) o = new doit()\n" - "o.arr\n"); - QScopedPointer<QV4::Script> script; - script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup")); - script->parseAsBinding = false; - - QVERIFY(!scope.engine->hasException); - script->parse(); - QVERIFY(!scope.engine->hasException); - - QV4::ScopedValue result(scope, script->run()); - QVERIFY(!scope.engine->hasException); - - QVERIFY(result->as<QV4::ArrayObject>()); -} - -QTEST_MAIN(tst_v4traced) - -#include "tst_v4traced.moc" diff --git a/tests/auto/qml/v4traced/v4traced.pro b/tests/auto/qml/v4traced/v4traced.pro deleted file mode 100644 index cbc8f38046..0000000000 --- a/tests/auto/qml/v4traced/v4traced.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_v4traced -macos:CONFIG -= app_bundle -QT += core-private qml-private testlib -SOURCES += tst_v4traced.cpp |