diff options
Diffstat (limited to 'tests/auto/qml')
279 files changed, 5584 insertions, 555 deletions
diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index 5c328fbfcc..890e722aa3 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -4,6 +4,7 @@ SUBDIRS += qqmldebugjsserver PUBLICTESTS += \ qdebugmessageservice \ + qqmldebugtranslationservice \ qqmlenginedebugservice \ qqmldebugjs \ qqmlinspector \ diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp index d2cfd3897a..ec7ee15d34 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp +++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp @@ -88,8 +88,7 @@ public: QList<LogEntry> logBuffer; protected: - //inherited from QQmlDebugClient - void messageReceived(const QByteArray &data); + void messageReceived(const QByteArray &data) override; signals: void debugOutput(); @@ -136,7 +135,7 @@ QList<QQmlDebugClient *> tst_QDebugMessageService::createClients() void tst_QDebugMessageService::retrieveDebugOutput() { - QCOMPARE(QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + QCOMPARE(QQmlDebugTest::connectTo(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", QString(), testFile(QMLFILE), true), ConnectSuccess); QTRY_VERIFY(m_client->logBuffer.size() >= 2); diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml index deba24cf91..a7231df48b 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml @@ -36,5 +36,9 @@ Item { } id: root property int a: 10 + + Item { + property int b: 11 + } } diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 5b6c43bc0c..91470e0651 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -180,7 +180,7 @@ QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString const QString executable = qmlscene ? QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene" : debugJsServerPath("qqmldebugjs"); - return QQmlDebugTest::connect( + return QQmlDebugTest::connectTo( executable, restrictServices ? QStringLiteral("V8Debugger") : QString(), testFile(qmlFile), blockMode); } @@ -896,6 +896,16 @@ void tst_QQmlDebugJS::evaluateInContext() QVERIFY(waitForClientSignal(SIGNAL(result()))); QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 20); + + auto childObjects = object.children; + QVERIFY(childObjects.count() > 0); // QQmlComponentAttached is also in there + QCOMPARE(childObjects[0].className, QString::fromLatin1("Item")); + + // "b" accessible in context of surrounding (child) object + m_client->evaluate(QLatin1String("b"), -1, childObjects[0].debugId); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 11); } void tst_QQmlDebugJS::getScripts() diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml b/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml new file mode 100644 index 0000000000..234496577a --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/data/test.qml @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 QtQuick.Controls 2.12 + +Item { + width: 360 + height: 360 + + Text { + text: qsTr("hello") + width: parent.width / 10 + elide: Text.ElideRight + } +} diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/qqmldebugtranslationservice.pro b/tests/auto/qml/debugger/qqmldebugtranslationservice/qqmldebugtranslationservice.pro new file mode 100644 index 0000000000..32e60e306d --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/qqmldebugtranslationservice.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qdebugtranslationservice +QT += network testlib gui-private core-private qmldebug-private +macos:CONFIG -= app_bundle + +SOURCES += tst_qqmldebugtranslationservice.cpp + +include(../shared/debugutil.pri) + +TESTDATA = data/* + +OTHER_FILES += data/test.qml diff --git a/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp new file mode 100644 index 0000000000..01ee805dee --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugtranslationservice/tst_qqmldebugtranslationservice.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +//QQmlDebugTest +#include <debugutil_p.h> +#include <qqmldebugprocess_p.h> + +#include <private/qqmldebugclient_p.h> +#include <private/qqmldebugtranslationclient_p.h> +#include <private/qqmldebugconnection_p.h> +#include <private/qpacket_p.h> + +#include <QtCore/qstring.h> +#include <QtCore/qlibraryinfo.h> +#include <QtTest/qtest.h> + +const char *QMLFILE = "test.qml"; + +class tst_QQmlDebugTranslationService : public QQmlDebugTest +{ + Q_OBJECT + +private slots: + void pluginConnection(); + +private: + QList<QQmlDebugClient *> createClients() override; + QPointer<QQmlDebugTranslationClient> m_client; +}; + +QList<QQmlDebugClient *> tst_QQmlDebugTranslationService::createClients() +{ + m_client = new QQmlDebugTranslationClient(m_connection); + + QObject::connect(m_client, &QQmlDebugClient::stateChanged, m_client, [this](QQmlDebugClient::State newState) { + QCOMPARE(newState, m_client->state()); + }); + + return {m_client}; +} + +void tst_QQmlDebugTranslationService::pluginConnection() +{ + auto executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml"; + auto services = "DebugTranslation"; + auto extraArgs = testFile(QMLFILE); + auto block = true; + + auto result = QQmlDebugTest::connectTo(executable, services, extraArgs, block); + QCOMPARE(result, ConnectSuccess); +} + +QTEST_MAIN(tst_QQmlDebugTranslationService) + +#include "tst_qqmldebugtranslationservice.moc" diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp index a8c43b1c75..c8915fb840 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp +++ b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp @@ -66,7 +66,7 @@ class tst_QQmlEngineControl : public QQmlDebugTest Q_OBJECT private: - ConnectResult connect(const QString &testFile, bool restrictServices); + ConnectResult connectTo(const QString &testFile, bool restrictServices); QList<QQmlDebugClient *> createClients() override; void engine_data(); @@ -79,10 +79,10 @@ private slots: void stopEngine(); }; -QQmlDebugTest::ConnectResult tst_QQmlEngineControl::connect(const QString &file, +QQmlDebugTest::ConnectResult tst_QQmlEngineControl::connectTo(const QString &file, bool restrictServices) { - return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", + return QQmlDebugTest::connectTo(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", restrictServices ? QStringLiteral("EngineControl") : QString(), testFile(file), true); } @@ -109,7 +109,7 @@ void tst_QQmlEngineControl::startEngine_data() void tst_QQmlEngineControl::startEngine() { QFETCH(bool, restrictMode); - QCOMPARE(connect("test.qml", restrictMode), ConnectSuccess); + QCOMPARE(connectTo("test.qml", restrictMode), ConnectSuccess); QTRY_VERIFY(!m_client->blockedEngines().empty()); m_client->releaseEngine(m_client->blockedEngines().last()); @@ -130,7 +130,7 @@ void tst_QQmlEngineControl::stopEngine() { QFETCH(bool, restrictMode); - QCOMPARE(connect("exit.qml", restrictMode), ConnectSuccess); + QCOMPARE(connectTo("exit.qml", restrictMode), ConnectSuccess); QTRY_VERIFY(!m_client->blockedEngines().empty()); m_client->releaseEngine(m_client->blockedEngines().last()); diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp index 980e2be1f1..9830f1a9bd 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp @@ -87,7 +87,7 @@ QQmlEngineDebugObjectReference tst_QQmlEngineDebugInspectorIntegration::findRoot QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices) { - return QQmlDebugTest::connect( + return QQmlDebugTest::connectTo( QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", restrictServices ? QStringLiteral("QmlDebugger,QmlInspector") : QString(), testFile("qtquick2.qml"), true); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 0ebf43eb6f..12befeb1ec 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -280,11 +280,11 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( } else { QCOMPARE(ref.name, QString("<unknown value>")); } - } else if (pmeta.type() < QVariant::UserType && pmeta.userType() != QMetaType::QVariant) { + } else if (pmeta.userType() < QMetaType::User && pmeta.userType() != QMetaType::QVariant) { const QVariant expected = pmeta.read(o); QVERIFY2(p.value == expected, QString::fromLatin1("%1 != %2. Details: %3/%4/%5/%6") .arg(QTest::toString(p.value)).arg(QTest::toString(expected)).arg(p.name) - .arg(p.valueTypeName).arg(pmeta.type()).arg(pmeta.userType()).toUtf8()); + .arg(p.valueTypeName).arg(pmeta.userType()).arg(pmeta.userType()).toUtf8()); } if (p.name == "parent") diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp index 6685558bb5..b5f45f1eeb 100644 --- a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp +++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp @@ -64,7 +64,7 @@ private slots: QQmlDebugTest::ConnectResult tst_QQmlInspector::startQmlProcess(const QString &qmlFile, bool restrictServices) { - return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + return QQmlDebugTest::connectTo(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", restrictServices ? QStringLiteral("QmlInspector") : QString(), testFile(qmlFile), true); } diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp index f08f3c1da7..bfec776614 100644 --- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp +++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp @@ -74,7 +74,7 @@ private slots: QQmlDebugTest::ConnectResult tst_QQmlPreview::startQmlProcess(const QString &qmlFile) { - return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + return QQmlDebugTest::connectTo(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", QStringLiteral("QmlPreview"), testFile(qmlFile), true); } @@ -122,7 +122,6 @@ void checkFiles(const QStringList &files) { QVERIFY(!files.contains("/etc/localtime")); QVERIFY(!files.contains("/etc/timezome")); - QVERIFY(!files.contains(":/qgradient/webgradients.binaryjson")); } void tst_QQmlPreview::cleanup() diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 085eb7b87a..c2a774b42d 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -201,7 +201,7 @@ private: CheckType = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckFileEndsWith }; - ConnectResult connect(bool block, const QString &file, bool recordFromStart = true, + ConnectResult connectTo(bool block, const QString &file, bool recordFromStart = true, uint flushInterval = 0, bool restrictServices = true, const QString &executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"); @@ -246,7 +246,7 @@ private: #define VERIFY(type, position, expected, checks, numbers) \ QVERIFY(verify(type, position, expected, checks, numbers)) -QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect( +QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connectTo( bool block, const QString &file, bool recordFromStart, uint flushInterval, bool restrictServices, const QString &executable) { @@ -255,7 +255,7 @@ QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect( m_isComplete = false; // ### Still using qmlscene due to QTBUG-33377 - return QQmlDebugTest::connect( + return QQmlDebugTest::connectTo( executable, restrictServices ? "CanvasFrameRate,EngineControl,DebugMessages" : QString(), testFile(file), block); @@ -542,7 +542,7 @@ void tst_QQmlProfilerService::connect() QFETCH(bool, restrictMode); QFETCH(bool, traceEnabled); - QCOMPARE(connect(blockMode, "test.qml", traceEnabled, 0, restrictMode), ConnectSuccess); + QCOMPARE(connectTo(blockMode, "test.qml", traceEnabled, 0, restrictMode), ConnectSuccess); if (!traceEnabled) m_client->client->setRecording(true); @@ -556,7 +556,7 @@ void tst_QQmlProfilerService::connect() void tst_QQmlProfilerService::pixmapCacheData() { - QCOMPARE(connect(true, "pixmapCacheTest.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "pixmapCacheTest.qml"), ConnectSuccess); // Don't wait for readyReadStandardOutput before the loop. It may have already arrived. while (m_process->output().indexOf(QLatin1String("image loaded")) == -1 && @@ -594,7 +594,7 @@ void tst_QQmlProfilerService::pixmapCacheData() void tst_QQmlProfilerService::scenegraphData() { - QCOMPARE(connect(true, "scenegraphTest.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "scenegraphTest.qml"), ConnectSuccess); while (!m_process->output().contains(QLatin1String("tick"))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); @@ -654,7 +654,7 @@ void tst_QQmlProfilerService::scenegraphData() void tst_QQmlProfilerService::profileOnExit() { - QCOMPARE(connect(true, "exit.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "exit.qml"), ConnectSuccess); checkProcessTerminated(); checkTraceReceived(); @@ -663,7 +663,7 @@ void tst_QQmlProfilerService::profileOnExit() void tst_QQmlProfilerService::controlFromJS() { - QCOMPARE(connect(true, "controlFromJS.qml", false), ConnectSuccess); + QCOMPARE(connectTo(true, "controlFromJS.qml", false), ConnectSuccess); QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); m_client->client->setRecording(false); @@ -673,7 +673,7 @@ void tst_QQmlProfilerService::controlFromJS() void tst_QQmlProfilerService::signalSourceLocation() { - QCOMPARE(connect(true, "signalSourceLocation.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "signalSourceLocation.qml"), ConnectSuccess); while (!(m_process->output().contains(QLatin1String("500")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); @@ -694,7 +694,7 @@ void tst_QQmlProfilerService::signalSourceLocation() void tst_QQmlProfilerService::javascript() { - QCOMPARE(connect(true, "javascript.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "javascript.qml"), ConnectSuccess); while (!(m_process->output().contains(QLatin1String("done")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); @@ -722,7 +722,7 @@ void tst_QQmlProfilerService::javascript() void tst_QQmlProfilerService::flushInterval() { - QCOMPARE(connect(true, "timer.qml", true, 1), ConnectSuccess); + QCOMPARE(connectTo(true, "timer.qml", true, 1), ConnectSuccess); // Make sure we get multiple messages QTRY_VERIFY(m_client->qmlMessages.length() > 0); @@ -736,7 +736,7 @@ void tst_QQmlProfilerService::flushInterval() void tst_QQmlProfilerService::translationBinding() { - QCOMPARE(connect(true, "qstr.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "qstr.qml"), ConnectSuccess); checkProcessTerminated(); checkTraceReceived(); @@ -752,7 +752,7 @@ void tst_QQmlProfilerService::translationBinding() void tst_QQmlProfilerService::memory() { - QCOMPARE(connect(true, "memory.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "memory.qml"), ConnectSuccess); checkProcessTerminated(); checkTraceReceived(); @@ -781,7 +781,7 @@ static bool hasCompileEvents(const QVector<QQmlProfilerEventType> &types) void tst_QQmlProfilerService::compile() { // Flush interval so that we actually get the events before we stop recording. - connect(true, "test.qml", true, 100); + connectTo(true, "test.qml", true, 100); QVERIFY(m_client); @@ -820,7 +820,7 @@ void tst_QQmlProfilerService::compile() void tst_QQmlProfilerService::multiEngine() { - QCOMPARE(connect(true, "quit.qml", true, 0, false, debugJsServerPath("qqmlprofilerservice")), + QCOMPARE(connectTo(true, "quit.qml", true, 0, false, debugJsServerPath("qqmlprofilerservice")), ConnectSuccess); QSignalSpy spy(m_client->client, SIGNAL(complete(qint64))); @@ -837,7 +837,7 @@ void tst_QQmlProfilerService::multiEngine() void tst_QQmlProfilerService::batchOverflow() { // The trace client checks that the events are received in order. - QCOMPARE(connect(true, "batchOverflow.qml"), ConnectSuccess); + QCOMPARE(connectTo(true, "batchOverflow.qml"), ConnectSuccess); checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp index 68446b53a4..3787f34bc2 100644 --- a/tests/auto/qml/debugger/shared/debugutil.cpp +++ b/tests/auto/qml/debugger/shared/debugutil.cpp @@ -120,7 +120,7 @@ void QQmlDebugTestClient::messageReceived(const QByteArray &ba) emit serverMessage(ba); } -QQmlDebugTest::ConnectResult QQmlDebugTest::connect( +QQmlDebugTest::ConnectResult QQmlDebugTest::connectTo( const QString &executable, const QString &services, const QString &extraArgs, bool block) { diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h index 1c32590305..190909dc44 100644 --- a/tests/auto/qml/debugger/shared/debugutil_p.h +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -53,7 +53,6 @@ public: static QString clientStateString(const QQmlDebugClient *client); static QString connectionStateString(const QQmlDebugConnection *connection); -protected: enum ConnectResult { ConnectSuccess, ProcessFailed, @@ -64,7 +63,9 @@ protected: RestrictFailed }; - ConnectResult connect(const QString &executable, const QString &services, + Q_ENUM(ConnectResult) +protected: + ConnectResult connectTo(const QString &executable, const QString &services, const QString &extraArgs, bool block); virtual QQmlDebugProcess *createProcess(const QString &executable); diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 4c04afe886..6b743ba433 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -249,15 +249,6 @@ built-ins/String/prototype/toLocaleLowerCase/Final_Sigma_U180E.js fails built-ins/String/prototype/toLocaleLowerCase/special_casing_conditional.js fails built-ins/String/prototype/toLowerCase/Final_Sigma_U180E.js fails built-ins/String/prototype/toLowerCase/special_casing_conditional.js fails -built-ins/TypedArray/from/arylk-get-length-error.js fails -built-ins/TypedArray/from/arylk-to-length-error.js fails -built-ins/TypedArray/from/iter-access-error.js fails -built-ins/TypedArray/from/iter-invoke-error.js fails -built-ins/TypedArray/from/iter-next-error.js fails -built-ins/TypedArray/from/iter-next-value-error.js fails -built-ins/TypedArray/from/length.js fails -built-ins/TypedArray/from/name.js fails -built-ins/TypedArray/from/prop-desc.js fails built-ins/TypedArray/prototype/constructor.js fails built-ins/TypedArray/prototype/fill/fill-values-conversion-operations-consistent-nan.js fails built-ins/TypedArray/prototype/slice/bit-precision.js fails @@ -293,29 +284,6 @@ built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-species-not-cto built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-species-prototype-throws.js fails built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-species-throws.js fails built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-value-not-obj-throws.js fails -built-ins/TypedArrays/from/arylk-get-length-error.js fails -built-ins/TypedArrays/from/arylk-to-length-error.js fails -built-ins/TypedArrays/from/custom-ctor-returns-other-instance.js fails -built-ins/TypedArrays/from/custom-ctor.js fails -built-ins/TypedArrays/from/iter-access-error.js fails -built-ins/TypedArrays/from/iter-invoke-error.js fails -built-ins/TypedArrays/from/iter-next-error.js fails -built-ins/TypedArrays/from/iter-next-value-error.js fails -built-ins/TypedArrays/from/mapfn-abrupt-completion.js fails -built-ins/TypedArrays/from/mapfn-arguments.js fails -built-ins/TypedArrays/from/mapfn-this-with-thisarg.js fails -built-ins/TypedArrays/from/mapfn-this-without-thisarg-non-strict.js sloppyFails -built-ins/TypedArrays/from/mapfn-this-without-thisarg-strict.js strictFails -built-ins/TypedArrays/from/nan-conversion.js fails -built-ins/TypedArrays/from/new-instance-empty.js fails -built-ins/TypedArrays/from/new-instance-from-ordinary-object.js fails -built-ins/TypedArrays/from/new-instance-from-sparse-array.js fails -built-ins/TypedArrays/from/new-instance-from-zero.js fails -built-ins/TypedArrays/from/new-instance-using-custom-ctor.js fails -built-ins/TypedArrays/from/new-instance-with-mapfn.js fails -built-ins/TypedArrays/from/new-instance-without-mapfn.js fails -built-ins/TypedArrays/from/property-abrupt-completion.js fails -built-ins/TypedArrays/from/set-value-abrupt-completion.js fails built-ins/TypedArrays/internals/Get/key-is-not-integer.js fails built-ins/TypedArrays/internals/Get/key-is-not-minus-zero.js fails built-ins/TypedArrays/internals/Get/key-is-out-of-bounds.js fails diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 66a526fda8..26737e79c4 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -263,6 +263,9 @@ private slots: void arrayIncludesWithLargeArray(); void printCircularArray(); void typedArraySet(); + void dataViewCtor(); + + void uiLanguage(); public: Q_INVOKABLE QJSValue throwingCppMethod1(); @@ -1543,7 +1546,7 @@ void tst_QJSEngine::valueConversion_QVariant() { QVariant tmp1; QVariant tmp2(QMetaType::QVariant, &tmp1); - QCOMPARE(QMetaType::Type(tmp2.type()), QMetaType::QVariant); + QCOMPARE(QMetaType::Type(tmp2.userType()), QMetaType::QVariant); QJSValue val1 = eng.toScriptValue(tmp1); QJSValue val2 = eng.toScriptValue(tmp2); @@ -1558,9 +1561,9 @@ void tst_QJSEngine::valueConversion_QVariant() QVariant tmp1(123); QVariant tmp2(QMetaType::QVariant, &tmp1); QVariant tmp3(QMetaType::QVariant, &tmp2); - QCOMPARE(QMetaType::Type(tmp1.type()), QMetaType::Int); - QCOMPARE(QMetaType::Type(tmp2.type()), QMetaType::QVariant); - QCOMPARE(QMetaType::Type(tmp3.type()), QMetaType::QVariant); + QCOMPARE(QMetaType::Type(tmp1.userType()), QMetaType::Int); + QCOMPARE(QMetaType::Type(tmp2.userType()), QMetaType::QVariant); + QCOMPARE(QMetaType::Type(tmp3.userType()), QMetaType::QVariant); QJSValue val1 = eng.toScriptValue(tmp2); QJSValue val2 = eng.toScriptValue(tmp3); @@ -3340,7 +3343,7 @@ void tst_QJSEngine::dateRoundtripJSQtJS() #ifdef Q_OS_WIN QSKIP("This test fails on Windows due to a bug in QDateTime."); #endif - qint64 secs = QDateTime(QDate(2009, 1, 1)).toUTC().toSecsSinceEpoch(); + qint64 secs = QDate(2009, 1, 1).startOfDay(Qt::UTC).toSecsSinceEpoch(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0)); @@ -3357,7 +3360,7 @@ void tst_QJSEngine::dateRoundtripQtJSQt() #ifdef Q_OS_WIN QSKIP("This test fails on Windows due to a bug in QDateTime."); #endif - QDateTime qtDate = QDateTime(QDate(2009, 1, 1)); + QDateTime qtDate = QDate(2009, 1, 1).startOfDay(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { QJSValue jsDate = eng.toScriptValue(qtDate); @@ -3373,7 +3376,7 @@ void tst_QJSEngine::dateConversionJSQt() #ifdef Q_OS_WIN QSKIP("This test fails on Windows due to a bug in QDateTime."); #endif - qint64 secs = QDateTime(QDate(2009, 1, 1)).toUTC().toSecsSinceEpoch(); + qint64 secs = QDate(2009, 1, 1).startOfDay(Qt::UTC).toSecsSinceEpoch(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0)); @@ -3389,7 +3392,7 @@ void tst_QJSEngine::dateConversionJSQt() void tst_QJSEngine::dateConversionQtJS() { - QDateTime qtDate = QDateTime(QDate(2009, 1, 1)); + QDateTime qtDate = QDate(2009, 1, 1).startOfDay(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { QJSValue jsDate = eng.toScriptValue(qtDate); @@ -5143,6 +5146,48 @@ void tst_QJSEngine::typedArraySet() } } +void tst_QJSEngine::dataViewCtor() +{ + QJSEngine engine; + const auto error = engine.evaluate(R"( + (function() { try { + var buf = new ArrayBuffer(0x200); + var vuln = new DataView(buf, 8, 0xfffffff8); + } catch (e) { + return e; + }})() + )"); + QVERIFY(error.isError()); + QCOMPARE(error.toString(), "RangeError: DataView: constructor arguments out of range"); +} + +void tst_QJSEngine::uiLanguage() +{ + { + QJSEngine engine; + + QVERIFY(!engine.globalObject().hasProperty("Qt")); + + engine.installExtensions(QJSEngine::TranslationExtension); + QVERIFY(engine.globalObject().hasProperty("Qt")); + QVERIFY(engine.globalObject().property("Qt").hasProperty("uiLanguage")); + + engine.setUiLanguage("Blah"); + QCOMPARE(engine.globalObject().property("Qt").property("uiLanguage").toString(), "Blah"); + + engine.evaluate("Qt.uiLanguage = \"another\""); + QCOMPARE(engine.globalObject().property("Qt").property("uiLanguage").toString(), "another"); + } + + { + QQmlEngine qmlEngine; + QVERIFY(qmlEngine.globalObject().hasProperty("Qt")); + QVERIFY(qmlEngine.globalObject().property("Qt").hasProperty("uiLanguage")); + qmlEngine.setUiLanguage("Blah"); + QCOMPARE(qmlEngine.globalObject().property("Qt").property("uiLanguage").toString(), "Blah"); + } +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index cab4686c37..0d0bd2ae7e 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -1067,7 +1067,7 @@ void tst_QJSValue::toVariant() } { - QDateTime dateTime = QDateTime(QDate(1980, 10, 4)); + QDateTime dateTime = QDate(1980, 10, 4).startOfDay(); QJSValue dateObject = eng.toScriptValue(dateTime); QVariant var = dateObject.toVariant(); QCOMPARE(var, QVariant(dateTime)); @@ -1081,7 +1081,7 @@ void tst_QJSValue::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); + QCOMPARE(var.userType(), QMetaType::QRegularExpression); QRegularExpression result = var.toRegularExpression(); QCOMPARE(result.pattern(), rx.pattern()); QCOMPARE(result.patternOptions() & QRegularExpression::CaseInsensitiveOption, 0); @@ -1138,7 +1138,7 @@ void tst_QJSValue::toVariant() QVERIFY(array.isArray()); QCOMPARE(array.property("length").toInt(), 2); QVariant ret = array.toVariant(); - QCOMPARE(ret.type(), QVariant::List); + QCOMPARE(ret.userType(), QVariant::List); QVariantList listOut = ret.toList(); QCOMPARE(listOut.size(), listIn.size()); for (int i = 0; i < listIn.size(); ++i) @@ -1222,7 +1222,7 @@ void tst_QJSValue::toDateTime() QDateTime dt = eng.evaluate("new Date(0)").toDateTime(); QVERIFY(dt.isValid()); QCOMPARE(dt.timeSpec(), Qt::LocalTime); - QCOMPARE(dt.toUTC(), QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::UTC)); + QCOMPARE(dt.toUTC(), QDate(1970, 1, 1).startOfDay(Qt::UTC)); QVERIFY(!eng.evaluate("[]").toDateTime().isValid()); QVERIFY(!eng.evaluate("{}").toDateTime().isValid()); @@ -2145,8 +2145,8 @@ void tst_QJSValue::equals() QCOMPARE(str2.equals(QJSValue(321)), false); QCOMPARE(str2.equals(QJSValue()), false); - QJSValue date1 = eng.toScriptValue(QDateTime(QDate(2000, 1, 1))); - QJSValue date2 = eng.toScriptValue(QDateTime(QDate(1999, 1, 1))); + QJSValue date1 = eng.toScriptValue(QDate(2000, 1, 1).startOfDay()); + QJSValue date2 = eng.toScriptValue(QDate(1999, 1, 1).startOfDay()); QCOMPARE(date1.equals(date2), false); QCOMPARE(date1.equals(date1), true); QCOMPARE(date2.equals(date2), true); @@ -2278,8 +2278,8 @@ void tst_QJSValue::strictlyEquals() QCOMPARE(str2.strictlyEquals(QJSValue(321)), false); QVERIFY(!str2.strictlyEquals(QJSValue())); - QJSValue date1 = eng.toScriptValue(QDateTime(QDate(2000, 1, 1))); - QJSValue date2 = eng.toScriptValue(QDateTime(QDate(1999, 1, 1))); + QJSValue date1 = eng.toScriptValue(QDate(2000, 1, 1).startOfDay()); + QJSValue date2 = eng.toScriptValue(QDate(1999, 1, 1).startOfDay()); QCOMPARE(date1.strictlyEquals(date2), false); QCOMPARE(date1.strictlyEquals(date1), true); QCOMPARE(date2.strictlyEquals(date2), true); diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index db9bb52010..621e8bb437 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -91,7 +91,7 @@ SUBDIRS += $$METATYPETESTS qtConfig(process) { qtConfig(qml-debug): SUBDIRS += debugger !boot2qt { - SUBDIRS += qmllint qmlplugindump + SUBDIRS += qmlformat qmllint qmlplugindump } } @@ -105,3 +105,6 @@ qtConfig(private_tests): \ qtNomakeTools( \ qmlplugindump \ ) + +!cross_compile: \ + SUBDIRS += qmltyperegistrar diff --git a/tests/auto/qml/qmlcachegen/retain.qrc b/tests/auto/qml/qmlcachegen/data/retain.qrc index e5eed9b12f..e1b9045fbe 100644 --- a/tests/auto/qml/qmlcachegen/retain.qrc +++ b/tests/auto/qml/qmlcachegen/data/retain.qrc @@ -1,5 +1,5 @@ <RCC> <qresource prefix="/"> - <file alias="Retain.qml">data/Retain.qml</file> + <file alias="Retain.qml">Retain.qml</file> </qresource> </RCC> diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index 4daf1d35c3..452bd7d04a 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -28,7 +28,7 @@ workerscripts_test.prefix = /workerscripts RESOURCES += \ workerscripts_test \ trickypaths.qrc \ - retain.qrc + data/retain.qrc # QTBUG-46375 !win32: RESOURCES += trickypaths_umlaut.qrc diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 4a1f5378a6..f940f9c476 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -363,7 +363,7 @@ static QQmlPrivate::CachedQmlUnit *temporaryModifiedCachedUnit = nullptr; void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() { QVERIFY(QFile::exists(":/data/versionchecks.qml")); - QCOMPARE(QFileInfo(":/data/versionchecks.qml").size(), 0); + QVERIFY(QFileInfo(":/data/versionchecks.qml").size() > 0); Q_ASSERT(!temporaryModifiedCachedUnit); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; @@ -390,12 +390,8 @@ void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() { QQmlEngine engine; - QQmlComponent component(&engine, QUrl("qrc:/data/versionchecks.qml")); - QCOMPARE(component.status(), QQmlComponent::Error); - QCOMPARE(component.errorString(), - QString("qrc:/data/versionchecks.qml:-1 File was compiled ahead of time with an " - "incompatible version of Qt and the original file cannot be found. Please " - "recompile\n")); + CleanlyLoadingComponent component(&engine, QUrl("qrc:/data/versionchecks.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); } Q_ASSERT(temporaryModifiedCachedUnit); @@ -417,7 +413,7 @@ void tst_qmlcachegen::workerScripts() { QVERIFY(QFile::exists(":/workerscripts/data/worker.js")); QVERIFY(QFile::exists(":/workerscripts/data/worker.qml")); - QCOMPARE(QFileInfo(":/workerscripts/data/worker.js").size(), 0); + QVERIFY(QFileInfo(":/workerscripts/data/worker.js").size() > 0); QQmlEngine engine; CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/data/worker.qml")); @@ -506,7 +502,7 @@ void tst_qmlcachegen::trickyPaths() { QFETCH(QString, filePath); QVERIFY2(QFile::exists(filePath), qPrintable(filePath)); - QCOMPARE(QFileInfo(filePath).size(), 0); + QVERIFY(QFileInfo(filePath).size() > 0); QQmlEngine engine; QQmlComponent component(&engine, QUrl("qrc" + filePath)); QScopedPointer<QObject> obj(component.create()); @@ -587,7 +583,7 @@ void tst_qmlcachegen::moduleScriptImport() QTRY_VERIFY(obj->property("ok").toBool()); QVERIFY(QFile::exists(":/data/script.mjs")); - QCOMPARE(QFileInfo(":/data/script.mjs").size(), 0); + QVERIFY(QFileInfo(":/data/script.mjs").size() > 0); { auto componentPrivate = QQmlComponentPrivate::get(&component); @@ -610,7 +606,6 @@ void tst_qmlcachegen::moduleScriptImport() void tst_qmlcachegen::esModulesViaQJSEngine() { - QCOMPARE(QFileInfo(":/data/module.mjs").size(), 0); QJSEngine engine; QJSValue module = engine.importModule(":/data/module.mjs"); QJSValue result = module.property("entry").call(); @@ -629,7 +624,7 @@ void tst_qmlcachegen::enums() void tst_qmlcachegen::sourceFileIndices() { QVERIFY(QFile::exists(":/data/versionchecks.qml")); - QCOMPARE(QFileInfo(":/data/versionchecks.qml").size(), 0); + QVERIFY(QFileInfo(":/data/versionchecks.qml").size() > 0); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit( @@ -644,8 +639,7 @@ void tst_qmlcachegen::reproducibleCache_data() QTest::addColumn<QString>("filePath"); QDir dir(dataDirectory()); - for (const QString &entry : dir.entryList(QDir::Files)) { - QVERIFY(entry.endsWith(".qml") || entry.endsWith(".js") || entry.endsWith(".mjs")); + for (const QString &entry : dir.entryList((QStringList() << "*.qml" << "*.js" << "*.mjs"), QDir::Files)) { QTest::newRow(entry.toUtf8().constData()) << dir.filePath(entry); } } diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 1f0115b926..d0c8390a74 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -87,7 +87,9 @@ public: void waitForLoad() { - QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + QTRY_VERIFY_WITH_TIMEOUT( + status() == QQmlComponent::Ready || status() == QQmlComponent::Error, + 32768); } }; diff --git a/tests/auto/qml/qmlformat/data/Annotations.formatted.nosort.qml b/tests/auto/qml/qmlformat/data/Annotations.formatted.nosort.qml new file mode 100644 index 0000000000..a05c2125dc --- /dev/null +++ b/tests/auto/qml/qmlformat/data/Annotations.formatted.nosort.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +//![2] +import QtQuick 2.0 +//![2] +import QtCharts 2.0 + +@Pippo { + atg1: 3 +} +@Annotation2 { +} +Item { + //![1] + + @AnnotateMore { + property int x: 5 + } + @AnnotateALot { + } + + property variant othersSlice: 0 + @Annotate { + } + + anchors.fill: parent + @SuperComplete { + binding: late + } + Component.onCompleted: { + // You can also manipulate slices dynamically, like append a slice or set a slice exploded + othersSlice = pieSeries.append("Others", 52); + pieSeries.find("Volkswagen").exploded = true; + } + //![1] + ChartView { + id: chart + + title: "Top-5 car brand shares in Finland" + anchors.fill: parent + legend.alignment: Qt.AlignBottom + antialiasing: true + + @ExtraAnnotation { + signal pippo() + } + PieSeries { + id: pieSeries + + PieSlice { + label: "Volkswagen" + value: 13.5 + } + + PieSlice { + label: "Toyota" + value: 10.9 + } + + PieSlice { + label: "Ford" + value: 8.6 + } + + PieSlice { + label: "Skoda" + value: 8.2 + } + + PieSlice { + label: "Volvo" + value: 6.8 + } + + } + + } + +} diff --git a/tests/auto/qml/qmlformat/data/Annotations.formatted.qml b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml new file mode 100644 index 0000000000..a142d4cb74 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/Annotations.formatted.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +//![2] +import QtCharts 2.0 +//![2] +import QtQuick 2.0 + +@Pippo { + atg1: 3 +} +@Annotation2 { +} +Item { + //![1] + + @AnnotateMore { + property int x: 5 + } + @AnnotateALot { + } + + property variant othersSlice: 0 + @Annotate { + } + + anchors.fill: parent + @SuperComplete { + binding: late + } + Component.onCompleted: { + // You can also manipulate slices dynamically, like append a slice or set a slice exploded + othersSlice = pieSeries.append("Others", 52); + pieSeries.find("Volkswagen").exploded = true; + } + //![1] + ChartView { + id: chart + + title: "Top-5 car brand shares in Finland" + anchors.fill: parent + legend.alignment: Qt.AlignBottom + antialiasing: true + + @ExtraAnnotation { + signal pippo() + } + PieSeries { + id: pieSeries + + PieSlice { + label: "Volkswagen" + value: 13.5 + } + + PieSlice { + label: "Toyota" + value: 10.9 + } + + PieSlice { + label: "Ford" + value: 8.6 + } + + PieSlice { + label: "Skoda" + value: 8.2 + } + + PieSlice { + label: "Volvo" + value: 6.8 + } + + } + + } + +} diff --git a/tests/auto/qml/qmlformat/data/Annotations.qml b/tests/auto/qml/qmlformat/data/Annotations.qml new file mode 100644 index 0000000000..2d3d7d2cfd --- /dev/null +++ b/tests/auto/qml/qmlformat/data/Annotations.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +//![2] +import QtQuick 2.0 +//![2] +import QtCharts 2.0 + +@Pippo{ atg1:3 } +@Annotation2{} +Item { + @Annotate{} + anchors.fill: parent + @AnnotateMore{ + property int x: 5 + } + @AnnotateALot{} + property variant othersSlice: 0 + + //![1] + ChartView { + id: chart + title: "Top-5 car brand shares in Finland" + anchors.fill: parent + legend.alignment: Qt.AlignBottom + antialiasing: true + +@ExtraAnnotation{ + signal pippo +} + PieSeries { + id: pieSeries + PieSlice { label: "Volkswagen"; value: 13.5 } + PieSlice { label: "Toyota"; value: 10.9 } + PieSlice { label: "Ford"; value: 8.6 } + PieSlice { label: "Skoda"; value: 8.2 } + PieSlice { label: "Volvo"; value: 6.8 } + } + } + +@SuperComplete{ +binding: late +} + Component.onCompleted: { + // You can also manipulate slices dynamically, like append a slice or set a slice exploded + othersSlice = pieSeries.append("Others", 52.0); + pieSeries.find("Volkswagen").exploded = true; + } + //![1] +} diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.nosort.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.nosort.qml new file mode 100644 index 0000000000..34d58cf571 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.nosort.qml @@ -0,0 +1,152 @@ +/* This file is licensed under the not a license license + 1. You may not comply + 2. Goodbye +*/ + +// Importing this is very important +import QtQuick 5.15 +// Muddling the waters! +import QtQuick.Models 3.14 as muddle +// Importing that is important too +import Z +import That +import This // THIS IS VERY IMPORTANT! +import Y +import X.Z +import X.Y +import A.LLOHA +import A.B.B.A + +// This comment is related to Item +Item { + // Orphan comment + // Another orphan + // More orphans + + // This to id + // Also id. (line 2) + // This is the third id + // fourth id comment + id: foo + + // This to enum + enum Foo { + A = 3, // This is A + B, // This is B + C = 4, // This is C + D // This is D + } + + property bool some_bool: false + property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] + property bool something_computed: function(x) { + // This is an orphan inside something_computed + // Are these getting duplicated? + // Another orphan inside something_computed + + const PI = 3, DAYS_PER_YEAR = 365.25; + var x = 3 + 2; + x["bla"] = 50; + // This one to var few! + var few = new WhatEver(); + x += Math.sin(3); + x--; + --x; + x++; + ++x; + for (var x = 0; x < 100; x++) { + x++; + console.log("Foo"); + } + for (var x in [3, 2, 1]) { + y++; + console.log("Bar"); + } + while (true) + console.log("Wee"); + + with (foo) { + bar; + x += 5; + } // This is related to with! + x3: + do { + console.log("Hello"); + } while (3 == 0); + try { + dangerous(); + } catch (e) { + console.log(e); + } finally { + dangerous(); + } + switch (x) { + case 0: + x = 1; + break; + case 1: + x = 5; + break; + case 4: + x = 100; + break; + } + if (x == 50) + console.log("true"); + else if (x == 50) + console.log("other thing"); + else + console.log("false"); + + if (x == 50) { + console.log("true"); + } else if (x == 50) { + console.log("other thing"); + x--; + } else { + console.log("false"); + } + return "foobar"; + }() + default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool + // some_read_only_bool + readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0 + + signal say(string name, bool caps) + + // This one to aFunc() + function aFunc() { + var x = 3; + return x; + } + + x: 3 // Very cool + Component.onCompleted: console.log("Foo!") + myFavouriteThings: [ + // This is an orphan + + // This is a cool text + Text { + }, + // This is a cool rectangle + Rectangle { + } + ] + + Text { + required property string batman + + signal boo(int count, int times, real duration) + + text: "Bla" + } + + // This comment is related to the property animation + PropertyAnimation on x { + id: foo + + x: 3 + y: x + 3 + } + +} diff --git a/tests/auto/qml/qmlformat/data/Example1.formatted.qml b/tests/auto/qml/qmlformat/data/Example1.formatted.qml new file mode 100644 index 0000000000..b06734eb0b --- /dev/null +++ b/tests/auto/qml/qmlformat/data/Example1.formatted.qml @@ -0,0 +1,152 @@ +/* This file is licensed under the not a license license + 1. You may not comply + 2. Goodbye +*/ + +import A.B.B.A +import A.LLOHA +// Importing this is very important +import QtQuick 5.15 +// Muddling the waters! +import QtQuick.Models 3.14 as muddle +import That +import This // THIS IS VERY IMPORTANT! +import X.Y +import X.Z +import Y +// Importing that is important too +import Z + +// This comment is related to Item +Item { + // Orphan comment + // Another orphan + // More orphans + + // This to id + // Also id. (line 2) + // This is the third id + // fourth id comment + id: foo + + // This to enum + enum Foo { + A = 3, // This is A + B, // This is B + C = 4, // This is C + D // This is D + } + + property bool some_bool: false + property variant some_array_literal: [30, 20, Math["PI"], [4, 3, 2], "foo", 0.3] + property bool something_computed: function(x) { + // This is an orphan inside something_computed + // Are these getting duplicated? + // Another orphan inside something_computed + + const PI = 3, DAYS_PER_YEAR = 365.25; + var x = 3 + 2; + x["bla"] = 50; + // This one to var few! + var few = new WhatEver(); + x += Math.sin(3); + x--; + --x; + x++; + ++x; + for (var x = 0; x < 100; x++) { + x++; + console.log("Foo"); + } + for (var x in [3, 2, 1]) { + y++; + console.log("Bar"); + } + while (true) + console.log("Wee"); + + with (foo) { + bar; + x += 5; + } // This is related to with! + x3: + do { + console.log("Hello"); + } while (3 == 0); + try { + dangerous(); + } catch (e) { + console.log(e); + } finally { + dangerous(); + } + switch (x) { + case 0: + x = 1; + break; + case 1: + x = 5; + break; + case 4: + x = 100; + break; + } + if (x == 50) + console.log("true"); + else if (x == 50) + console.log("other thing"); + else + console.log("false"); + + if (x == 50) { + console.log("true"); + } else if (x == 50) { + console.log("other thing"); + x--; + } else { + console.log("false"); + } + return "foobar"; + }() + default property bool some_default_bool: 500 % 5 !== 0 // some_default_bool + // some_read_only_bool + readonly property bool some_read_only_bool: Math.sin(3) && (aFunc()[30] + 5) | 2 != 0 + + signal say(string name, bool caps) + + // This one to aFunc() + function aFunc() { + var x = 3; + return x; + } + + x: 3 // Very cool + Component.onCompleted: console.log("Foo!") + myFavouriteThings: [ + // This is an orphan + + // This is a cool text + Text { + }, + // This is a cool rectangle + Rectangle { + } + ] + + Text { + required property string batman + + signal boo(int count, int times, real duration) + + text: "Bla" + } + + // This comment is related to the property animation + PropertyAnimation on x { + id: foo + + x: 3 + y: x + 3 + } + +} diff --git a/tests/auto/qml/qmlformat/data/Example1.qml b/tests/auto/qml/qmlformat/data/Example1.qml new file mode 100644 index 0000000000..0fb9053e3a --- /dev/null +++ b/tests/auto/qml/qmlformat/data/Example1.qml @@ -0,0 +1,105 @@ + + +/* This file is licensed under the not a license license + 1. You may not comply + 2. Goodbye +*/ + +// Importing this is very important +import QtQuick 5.15 +// Muddling the waters! +import QtQuick.Models 3.14 as muddle +// Importing that is important too +import Z +import That +import This // THIS IS VERY IMPORTANT! +import Y +import X.Z +import X.Y +import A.LLOHA +import A.B.B.A + +// This comment is related to Item +Item { + x: 3 // Very cool + + // This to enum + enum Foo { + A = 3, // This is A + B, // This is B + C = 4, // This is C + D // This is D + } + + // This one to aFunc() + function aFunc() { + var x = 3; + return x; + } + + property bool some_bool : false + // This comment is related to the property animation + PropertyAnimation on x { + id: foo; x: 3; y: x + 3 + } + + // Orphan comment + + // Another orphan + + // More orphans + + + property variant some_array_literal: [30,20,Math["PI"],[4,3,2],"foo",0.3] + property bool something_computed: function(x) { + const PI = 3, DAYS_PER_YEAR=365.25; var x = 3 + 2; x["bla"] = 50; + + // This is an orphan inside something_computed + + // Are these getting duplicated? + + + // This one to var few! + var few = new WhatEver(); + x += Math.sin(3); x--; --x; x++; ++x; + for (var x = 0; x < 100; x++) { x++; console.log("Foo"); } + for (var x in [3,2,1]) { y++; console.log("Bar"); } + while (true) { console.log("Wee"); } + with (foo) { bar; x+=5; } // This is related to with! + x3: + do { console.log("Hello"); } while (3 == 0) + try { dangerous(); } catch(e) { console.log(e); } finally { console.log("What else?"); } + switch (x) { case 0: x = 1; break; case 1: x = 5; break; case 4: x = 100; break; } + if (x == 50) { console.log("true"); } else if (x == 50) { console.log("other thing"); } else { console.log("false"); } + if (x == 50) { console.log("true"); } else if (x == 50) { console.log("other thing"); x--; } else { console.log("false"); } + + // Another orphan inside something_computed + + return "foobar"; }(); + + default property bool some_default_bool : 500 % 5 !== 0 // some_default_bool + + myFavouriteThings: [ + // This is an orphan + + // This is a cool text + Text {}, + // This is a cool rectangle + Rectangle {}] + + // some_read_only_bool + readonly property bool some_read_only_bool : Math.sin(3) && (aFunc()[30] + 5) | 2 != 0 + + signal say(string name, bool caps); + + Text { text: "Bla"; signal boo(int count, int times, real duration); required property string batman; } + + Component.onCompleted: console.log("Foo!"); + + // This to id + // Also id. (line 2) + // This is the third id + // fourth id comment + id: foo + +} diff --git a/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml new file mode 100644 index 0000000000..620fbf4120 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/FrontInline.formatted.qml @@ -0,0 +1,3 @@ +// This comment should be directly above Item after formatting +Item { +} diff --git a/tests/auto/qml/qmlformat/data/FrontInline.qml b/tests/auto/qml/qmlformat/data/FrontInline.qml new file mode 100644 index 0000000000..c63265481c --- /dev/null +++ b/tests/auto/qml/qmlformat/data/FrontInline.qml @@ -0,0 +1,2 @@ +Item { // This comment should be directly above Item after formatting +} diff --git a/tests/auto/qml/qmlformat/data/IfBlocks.formatted.qml b/tests/auto/qml/qmlformat/data/IfBlocks.formatted.qml new file mode 100644 index 0000000000..b8e77ec23a --- /dev/null +++ b/tests/auto/qml/qmlformat/data/IfBlocks.formatted.qml @@ -0,0 +1,63 @@ +Item { + + function test() { + //// The following if blocks should NOT HAVE braces + // Single branch, no braces + if (true) + console.log("foo"); + + // Single branch, no braces + if (true) + console.log("foo"); + + // Multiple branches, No braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else + console.log("baz"); + // Multiple branches, all braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else + console.log("baz"); + + //// The following if blocks should HAVE braces + // Single branch, braces + if (true) { + console.log("foo"); + console.log("bar"); + } + // Multiple branches, some braces + if (true) { + console.log("foo"); + console.log("foo2"); + } else if (false) { + console.log("bar"); + } else { + console.log("baz"); + } + // Multiple branches, some braces + if (true) { + console.log("foo"); + } else if (false) { + console.log("bar"); + console.log("bar2"); + } else { + console.log("baz"); + } + // Multiple branches, some braces + if (true) { + console.log("foo"); + } else if (false) { + console.log("bar"); + } else { + console.log("baz"); + console.log("baz2"); + } + } + +} diff --git a/tests/auto/qml/qmlformat/data/IfBlocks.qml b/tests/auto/qml/qmlformat/data/IfBlocks.qml new file mode 100644 index 0000000000..505988b238 --- /dev/null +++ b/tests/auto/qml/qmlformat/data/IfBlocks.qml @@ -0,0 +1,66 @@ +Item { + function test() { + //// The following if blocks should NOT HAVE braces + // Single branch, no braces + if (true) + console.log("foo"); + + // Single branch, no braces + if (true) { + console.log("foo"); + } + + + // Multiple branches, No braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else + console.log("baz"); + + // Multiple branches, all braces + if (true) { + console.log("foo"); + } else if (false) { + console.log("bar"); + } else { + console.log("baz"); + } + + //// The following if blocks should HAVE braces + // Single branch, braces + if (true) { + console.log("foo"); + console.log("bar"); + } + + // Multiple branches, some braces + if (true) { + console.log("foo"); + console.log("foo2"); + } else if (false) + console.log("bar"); + else + console.log("baz"); + + // Multiple branches, some braces + if (true) + console.log("foo"); + else if (false) { + console.log("bar"); + console.log("bar2"); + } else + console.log("baz"); + + // Multiple branches, some braces + if (true) + console.log("foo"); + else if (false) + console.log("bar"); + else { + console.log("baz"); + console.log("baz2"); + } + } +} diff --git a/tests/auto/qml/qmlformat/data/readOnlyProps.formatted.qml b/tests/auto/qml/qmlformat/data/readOnlyProps.formatted.qml new file mode 100644 index 0000000000..6e7cc31dcf --- /dev/null +++ b/tests/auto/qml/qmlformat/data/readOnlyProps.formatted.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 + +QtObject { + // Testing UiObjectBinding + readonly property Item + item: Item { + id: test + + signal foo() + } + // End comment + + // Testing UiArrayBinding + readonly property list<Item> array: [ + Item { + id: test1 + + signal foo() + }, + Item { + id: test2 + + signal bar() + } + ] + // Testing UiScriptBinding + readonly property int script: Math.sin(Math.PI) + property bool normalProperty: true +} diff --git a/tests/auto/qml/qmlformat/data/readOnlyProps.qml b/tests/auto/qml/qmlformat/data/readOnlyProps.qml new file mode 100644 index 0000000000..8a32dd131e --- /dev/null +++ b/tests/auto/qml/qmlformat/data/readOnlyProps.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 + +QtObject { + // Testing UiObjectBinding + readonly property Item item: Item { id: test; signal foo() } + // End comment + + // Testing UiArrayBinding + readonly property list<Item> array: [ Item { id: test1; signal foo() }, Item { id: test2; signal bar() } ] + + // Testing UiScriptBinding + readonly property int script: Math.sin(Math.PI) + + property bool normalProperty: true +} diff --git a/tests/auto/qml/qmlformat/qmlformat.pro b/tests/auto/qml/qmlformat/qmlformat.pro new file mode 100644 index 0000000000..a6ae391711 --- /dev/null +++ b/tests/auto/qml/qmlformat/qmlformat.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qmlformat +macos:CONFIG -= app_bundle + +SOURCES += tst_qmlformat.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += testlib diff --git a/tests/auto/qml/qmlformat/tst_qmlformat.cpp b/tests/auto/qml/qmlformat/tst_qmlformat.cpp new file mode 100644 index 0000000000..5a8974b907 --- /dev/null +++ b/tests/auto/qml/qmlformat/tst_qmlformat.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** 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/QtTest> +#include <QDir> +#include <QFile> +#include <QProcess> +#include <QString> +#include <QTemporaryDir> + +#include <util.h> + +class TestQmlformat: public QQmlDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase() override; + + void testFormat(); + void testFormatNoSort(); + void testAnnotations(); + void testAnnotationsNoSort(); + void testLineEndings(); + void testFrontInline(); + void testIfBlocks(); + + void testReadOnlyProps(); + +#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled + void testExample(); + void testExample_data(); +#endif + +private: + QString readTestFile(const QString &path); + QString runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed, const QString &newlineFormat = "native"); + + QString m_qmlformatPath; + QStringList m_excludedDirs; + QStringList m_invalidFiles; + + QStringList findFiles(const QDir &); + bool isInvalidFile(const QFileInfo &fileName) const; +}; + +void TestQmlformat::initTestCase() +{ + QQmlDataTest::initTestCase(); + m_qmlformatPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmlformat"); +#ifdef Q_OS_WIN + m_qmlformatPath += QLatin1String(".exe"); +#endif + if (!QFileInfo(m_qmlformatPath).exists()) { + QString message = QStringLiteral("qmlformat executable not found (looked for %0)").arg(m_qmlformatPath); + QFAIL(qPrintable(message)); + } + + // Add directories you want excluded here + + // These snippets are not expected to run on their own. + m_excludedDirs << "doc/src/snippets/qml/visualdatamodel_rootindex"; + m_excludedDirs << "doc/src/snippets/qml/qtbinding"; + m_excludedDirs << "doc/src/snippets/qml/imports"; + m_excludedDirs << "doc/src/snippets/qtquick1/visualdatamodel_rootindex"; + m_excludedDirs << "doc/src/snippets/qtquick1/qtbinding"; + m_excludedDirs << "doc/src/snippets/qtquick1/imports"; + m_excludedDirs << "tests/manual/v4"; + m_excludedDirs << "tests/auto/qml/ecmascripttests"; + m_excludedDirs << "tests/auto/qml/qmllint"; + + // Add invalid files (i.e. files with syntax errors) + m_invalidFiles << "tests/auto/quick/qquickloader/data/InvalidSourceComponent.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.2.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.3.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/signal.5.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/property.4.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/empty.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/missingObject.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/insertedSemicolon.1.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nonexistantProperty.5.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidRoot.1.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml"; + m_invalidFiles << "tests/auto/qml/qquickfolderlistmodel/data/dummy.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; + m_invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml"; + m_invalidFiles << "tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml"; + m_invalidFiles << "tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml"; + + // These files rely on exact formatting + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; + m_invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml"; +} + +QStringList TestQmlformat::findFiles(const QDir &d) +{ + for (int ii = 0; ii < m_excludedDirs.count(); ++ii) { + QString s = m_excludedDirs.at(ii); + if (d.absolutePath().endsWith(s)) + return QStringList(); + } + + QStringList rv; + + QStringList files = d.entryList(QStringList() << QLatin1String("*.qml"), + QDir::Files); + foreach (const QString &file, files) { + rv << d.absoluteFilePath(file); + } + + QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::NoSymLinks); + foreach (const QString &dir, dirs) { + QDir sub = d; + sub.cd(dir); + rv << findFiles(sub); + } + + return rv; +} + +bool TestQmlformat::isInvalidFile(const QFileInfo &fileName) const +{ + for (const QString &invalidFile : m_invalidFiles) { + if (fileName.absoluteFilePath().endsWith(invalidFile)) + return true; + } + return false; +} + +QString TestQmlformat::readTestFile(const QString &path) +{ + QFile file(testFile(path)); + + if (!file.open(QIODevice::ReadOnly)) + return ""; + + return QString::fromUtf8(file.readAll()); +} + +void TestQmlformat::testFormat() +{ + QCOMPARE(runQmlformat(testFile("Example1.qml"), true, true), readTestFile("Example1.formatted.qml")); +} + +void TestQmlformat::testFormatNoSort() +{ + QCOMPARE(runQmlformat(testFile("Example1.qml"), false, true), readTestFile("Example1.formatted.nosort.qml")); +} + +void TestQmlformat::testAnnotations() +{ + QCOMPARE(runQmlformat(testFile("Annotations.qml"), true, true), readTestFile("Annotations.formatted.qml")); +} + +void TestQmlformat::testAnnotationsNoSort() +{ + QCOMPARE(runQmlformat(testFile("Annotations.qml"), false, true), readTestFile("Annotations.formatted.nosort.qml")); +} + +void TestQmlformat::testFrontInline() +{ + QCOMPARE(runQmlformat(testFile("FrontInline.qml"), false, true), readTestFile("FrontInline.formatted.qml")); +} + +void TestQmlformat::testIfBlocks() +{ + QCOMPARE(runQmlformat(testFile("IfBlocks.qml"), false, true), readTestFile("IfBlocks.formatted.qml")); +} + +void TestQmlformat::testReadOnlyProps() +{ + QCOMPARE(runQmlformat(testFile("readOnlyProps.qml"), false, true), readTestFile("readOnlyProps.formatted.qml")); +} + +void TestQmlformat::testLineEndings() +{ + // macos + const QString macosContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "macos"); + QVERIFY(!macosContents.contains("\n")); + QVERIFY(macosContents.contains("\r")); + + // windows + const QString windowsContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "windows"); + QVERIFY(windowsContents.contains("\r\n")); + + // unix + const QString unixContents = runQmlformat(testFile("Example1.formatted.qml"), false, true, "unix"); + QVERIFY(unixContents.contains("\n")); + QVERIFY(!unixContents.contains("\r")); +} + +#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +void TestQmlformat::testExample_data() +{ + QTest::addColumn<QString>("file"); + + QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; + QString tests = QLatin1String(SRCDIR) + "/../../../../tests/"; + + QStringList files; + files << findFiles(QDir(examples)); + files << findFiles(QDir(tests)); + + for (const QString &file : files) + QTest::newRow(qPrintable(file)) << file; +} +#endif + +#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +void TestQmlformat::testExample() +{ + QFETCH(QString, file); + QString output = runQmlformat(file, true, !isInvalidFile(file)); + + if (!isInvalidFile(file)) + QVERIFY(!output.isEmpty()); +} +#endif + +QString TestQmlformat::runQmlformat(const QString &fileToFormat, bool sortImports, bool shouldSucceed, const QString &newlineFormat) +{ + // Copy test file to temporary location + QTemporaryDir tempDir; + const QString tempFile = tempDir.path() + QDir::separator() + "to_format.qml"; + QFile::copy(fileToFormat, tempFile); + + QStringList args; + args << "-i"; + args << tempFile; + + if (!sortImports) + args << "-n"; + + args << "-l" << newlineFormat; + + auto verify = [&]() { + QProcess process; + process.start(m_qmlformatPath, args); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + if (shouldSucceed) + QCOMPARE(process.exitCode(), 0); + }; + verify(); + + QFile temp(tempFile); + + temp.open(QIODevice::ReadOnly); + QString formatted = QString::fromUtf8(temp.readAll()); + + return formatted; +} + +QTEST_MAIN(TestQmlformat) +#include "tst_qmlformat.moc" diff --git a/tests/auto/qml/qmllint/data/AttachedProps.qml b/tests/auto/qml/qmllint/data/AttachedProps.qml new file mode 100644 index 0000000000..01be66f866 --- /dev/null +++ b/tests/auto/qml/qmllint/data/AttachedProps.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 + +QtObject { + id: self + property var foo: self.Component.completed +} diff --git a/tests/auto/qml/qmllint/data/AutomatchedSignalHandler.qml b/tests/auto/qml/qmllint/data/AutomatchedSignalHandler.qml new file mode 100644 index 0000000000..064444e182 --- /dev/null +++ b/tests/auto/qml/qmllint/data/AutomatchedSignalHandler.qml @@ -0,0 +1,15 @@ +import QtQuick 2.12 + +Item { + width: 640 + height: 480 + + MouseArea { + anchors.fill: parent + onClicked: console.log("okok") + + Connections { + onClicked: console.log(mouse.x) + } + } +} diff --git a/tests/auto/qml/qmllint/data/ButtonLoader.qml b/tests/auto/qml/qmllint/data/ButtonLoader.qml new file mode 100644 index 0000000000..2721614735 --- /dev/null +++ b/tests/auto/qml/qmllint/data/ButtonLoader.qml @@ -0,0 +1,10 @@ +import QtQuick 2.12 + +Item { + Text { + id: roundButton + Text { + font.pixelSize: roundButton.font.pixelSize * 0.5 + } + } +} diff --git a/tests/auto/qml/qmllint/data/Cycle1.qml b/tests/auto/qml/qmllint/data/Cycle1.qml new file mode 100644 index 0000000000..8095e9f732 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Cycle1.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +Cycle2 {} diff --git a/tests/auto/qml/qmllint/data/Cycle2.qml b/tests/auto/qml/qmllint/data/Cycle2.qml new file mode 100644 index 0000000000..90c376fcda --- /dev/null +++ b/tests/auto/qml/qmllint/data/Cycle2.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +Cycle3 {} diff --git a/tests/auto/qml/qmllint/data/Cycle3.qml b/tests/auto/qml/qmllint/data/Cycle3.qml new file mode 100644 index 0000000000..f4cba68653 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Cycle3.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +Cycle1 {} diff --git a/tests/auto/qml/qmllint/data/Dialog.qml b/tests/auto/qml/qmllint/data/Dialog.qml new file mode 100644 index 0000000000..bde8eae18b --- /dev/null +++ b/tests/auto/qml/qmllint/data/Dialog.qml @@ -0,0 +1,10 @@ +import QtQuick 2.12 + +Item { + id: control + property Text header + header: Text { + font.bold: true + padding: 12 + } +} diff --git a/tests/auto/qml/qmllint/data/Drawer.qml b/tests/auto/qml/qmllint/data/Drawer.qml new file mode 100644 index 0000000000..db1d785c6c --- /dev/null +++ b/tests/auto/qml/qmllint/data/Drawer.qml @@ -0,0 +1,5 @@ +import QtQml 2.12 as T + +T.QtObject { + objectName: T.Component.objectName +} diff --git a/tests/auto/qml/qmllint/data/Form.ui.qml b/tests/auto/qml/qmllint/data/Form.ui.qml new file mode 100644 index 0000000000..459c82afbb --- /dev/null +++ b/tests/auto/qml/qmllint/data/Form.ui.qml @@ -0,0 +1,4 @@ +import QtQuick 2.0 + +Item { +} diff --git a/tests/auto/qml/qmllint/data/FormUser.qml b/tests/auto/qml/qmllint/data/FormUser.qml new file mode 100644 index 0000000000..ea3621586f --- /dev/null +++ b/tests/auto/qml/qmllint/data/FormUser.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +Form { + x: 12 + y: 13 + objectName: "horst" +} diff --git a/tests/auto/qml/qmllint/data/ImportWithPrefix.qml b/tests/auto/qml/qmllint/data/ImportWithPrefix.qml new file mode 100644 index 0000000000..6d070da21a --- /dev/null +++ b/tests/auto/qml/qmllint/data/ImportWithPrefix.qml @@ -0,0 +1,5 @@ +import "." as MyStuff + +MyStuff.Simple { + property bool something: contains(Qt.point(12, 34)) +} diff --git a/tests/auto/qml/qmllint/data/MethodInItem.qml b/tests/auto/qml/qmllint/data/MethodInItem.qml new file mode 100644 index 0000000000..dbdaf8bcc1 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MethodInItem.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + function doThings() { console.log("things") } +} diff --git a/tests/auto/qml/qmllint/data/MethodInScope.qml b/tests/auto/qml/qmllint/data/MethodInScope.qml new file mode 100644 index 0000000000..7ba0829f61 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MethodInScope.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +MethodInItem { + Component.onCompleted: doThings() +} diff --git a/tests/auto/qml/qmllint/data/Methods.js b/tests/auto/qml/qmllint/data/Methods.js new file mode 100644 index 0000000000..52ab857e38 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Methods.js @@ -0,0 +1,3 @@ +function foo() { + return "ttt" +} diff --git a/tests/auto/qml/qmllint/data/Text.qml b/tests/auto/qml/qmllint/data/Text.qml new file mode 100644 index 0000000000..130578d1e3 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Text.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 as T + +T.Text { + id: control + text: "'ello" +} diff --git a/tests/auto/qml/qmllint/data/Things/SomethingElse.qml b/tests/auto/qml/qmllint/data/Things/SomethingElse.qml new file mode 100644 index 0000000000..0e69012662 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Things/SomethingElse.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +QtObject {} diff --git a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes new file mode 100644 index 0000000000..9d81c21070 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes @@ -0,0 +1,17 @@ +import QtQuick.tooling 1.2 +Module { + dependencies: [] + Component { + name: "SomethingEntirelyStrange" + prototype: "QObject" + Enum { + name: "AnEnum" + values: { + "AAA": 0, + "BBB": 1, + "CCC": 2 + } + } + Property { name: "palette"; type: "QPalette" } + } +} diff --git a/tests/auto/qml/qmllint/data/Things/qmldir b/tests/auto/qml/qmllint/data/Things/qmldir new file mode 100644 index 0000000000..c53af3a340 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Things/qmldir @@ -0,0 +1,3 @@ +module Things +Something 1.0 SomethingElse.qml +plugin doesNotExistPlugin diff --git a/tests/auto/qml/qmllint/data/badAlias.qml b/tests/auto/qml/qmllint/data/badAlias.qml new file mode 100644 index 0000000000..2dd7d1a7e0 --- /dev/null +++ b/tests/auto/qml/qmllint/data/badAlias.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property alias wrong: nowhere +} diff --git a/tests/auto/qml/qmllint/data/badAliasProperty.qml b/tests/auto/qml/qmllint/data/badAliasProperty.qml new file mode 100644 index 0000000000..9483c52cd0 --- /dev/null +++ b/tests/auto/qml/qmllint/data/badAliasProperty.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 + +QtObject { + id: self + property alias wrong: self.nowhere +} diff --git a/tests/auto/qml/qmllint/data/badParent.qml b/tests/auto/qml/qmllint/data/badParent.qml new file mode 100644 index 0000000000..f381f059cc --- /dev/null +++ b/tests/auto/qml/qmllint/data/badParent.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +Item { + Item { + property int yyy: parent.rrr + } +} diff --git a/tests/auto/qml/qmllint/data/badTypeAssertion.qml b/tests/auto/qml/qmllint/data/badTypeAssertion.qml new file mode 100644 index 0000000000..717fc1b1bb --- /dev/null +++ b/tests/auto/qml/qmllint/data/badTypeAssertion.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Item { + property QtObject foo: Item { x: 4 } + property real foox: (foo as Item).rrr +} diff --git a/tests/auto/qml/qmllint/data/esmodule.mjs b/tests/auto/qml/qmllint/data/esmodule.mjs new file mode 100644 index 0000000000..50a53be2b1 --- /dev/null +++ b/tests/auto/qml/qmllint/data/esmodule.mjs @@ -0,0 +1,2 @@ + +export function test() { console.log("hello world"); } diff --git a/tests/auto/qml/qmllint/data/forLoop.qml b/tests/auto/qml/qmllint/data/forLoop.qml new file mode 100644 index 0000000000..be8b12409b --- /dev/null +++ b/tests/auto/qml/qmllint/data/forLoop.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +QtObject { + Component.onCompleted: { + var stuff = [1, 2, 3, 4] + for (var a in stuff) + console.log(a); + } +} diff --git a/tests/auto/qml/qmllint/data/goodAlias.qml b/tests/auto/qml/qmllint/data/goodAlias.qml new file mode 100644 index 0000000000..d2fa4485ec --- /dev/null +++ b/tests/auto/qml/qmllint/data/goodAlias.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + id: self + + QtObject { + id: inner + } + + property alias innerObj: inner + property alias name: self.objectName +} diff --git a/tests/auto/qml/qmllint/data/goodParent.qml b/tests/auto/qml/qmllint/data/goodParent.qml new file mode 100644 index 0000000000..413337713a --- /dev/null +++ b/tests/auto/qml/qmllint/data/goodParent.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Item { + property int rrr: 5 + Item { + property int yyy: parent.rrr + } +} diff --git a/tests/auto/qml/qmllint/data/goodTypeAssertion.qml b/tests/auto/qml/qmllint/data/goodTypeAssertion.qml new file mode 100644 index 0000000000..6f5f52eb6b --- /dev/null +++ b/tests/auto/qml/qmllint/data/goodTypeAssertion.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +Item { + property QtObject foo: Item { x: 4 } + property real foox: (foo as Item).x +} diff --git a/tests/auto/qml/qmllint/data/incompleteQmltypes.qml b/tests/auto/qml/qmllint/data/incompleteQmltypes.qml new file mode 100644 index 0000000000..ab06bbd8b0 --- /dev/null +++ b/tests/auto/qml/qmllint/data/incompleteQmltypes.qml @@ -0,0 +1,6 @@ +import Things 1.0 + +SomethingEntirelyStrange { + id: self + property var a: self.palette.weDontKnowIt +} diff --git a/tests/auto/qml/qmllint/data/javascriptMethods.qml b/tests/auto/qml/qmllint/data/javascriptMethods.qml new file mode 100644 index 0000000000..62ddcfda60 --- /dev/null +++ b/tests/auto/qml/qmllint/data/javascriptMethods.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "Methods.js" as Methods + +QtObject { + objectName: Methods.foo() +} diff --git a/tests/auto/qml/qmllint/data/memberNotFound.qml b/tests/auto/qml/qmllint/data/memberNotFound.qml new file mode 100644 index 0000000000..da2e353227 --- /dev/null +++ b/tests/auto/qml/qmllint/data/memberNotFound.qml @@ -0,0 +1,7 @@ +import QtQml 2.0 + +QtObject { + id: self + property string n: self.objectName + property string not: self.foo +} diff --git a/tests/auto/qml/qmllint/data/parentIsComponent.qml b/tests/auto/qml/qmllint/data/parentIsComponent.qml new file mode 100644 index 0000000000..a74bf9e4e8 --- /dev/null +++ b/tests/auto/qml/qmllint/data/parentIsComponent.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Item { + Component { + id: foo + Item { + property real yyy: parent.progress + Component.onCompleted: console.log(yyy) + } + } + + property var stuff: foo.createObject() +} diff --git a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml new file mode 100644 index 0000000000..4847fc9196 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml @@ -0,0 +1,6 @@ +import Things 1.0 + +Something { + property var a: SomethingEntirelyStrange {} + property var b: SomethingEntirelyStrange.AAA +} diff --git a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml b/tests/auto/qml/qmllint/data/unknownElement.qml index 1323593031..1323593031 100644 --- a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml +++ b/tests/auto/qml/qmllint/data/unknownElement.qml diff --git a/tests/auto/qml/qmllint/data/unknownJavascriptMethod.qml b/tests/auto/qml/qmllint/data/unknownJavascriptMethod.qml new file mode 100644 index 0000000000..2718e07c60 --- /dev/null +++ b/tests/auto/qml/qmllint/data/unknownJavascriptMethod.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "Methods.js" as Methods + +QtObject { + objectName: Methods.foo2() +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 582f146dca..8697495a6f 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -38,13 +38,21 @@ class TestQmllint: public QQmlDataTest private Q_SLOTS: void initTestCase() override; - void test(); - void test_data(); + void testUnqualified(); void testUnqualified_data(); - void testUnqualifiedNoSpuriousParentWarning(); - void catchIdentifierNoFalsePositive(); + + void cleanQmlCode_data(); + void cleanQmlCode(); + + void dirtyQmlCode_data(); + void dirtyQmlCode(); + + void testUnknownCausesFail(); + private: + QString runQmllint(const QString &fileToLint, bool shouldSucceed); + QString m_qmllintPath; }; @@ -61,37 +69,14 @@ void TestQmllint::initTestCase() } } -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(); + + const QString output = runQmllint(filename, false); QVERIFY(output.contains(QString::asprintf("Warning: unqualified access at %d:%d", warningLine, warningColumn))); QVERIFY(output.contains(warningMessage)); } @@ -118,56 +103,142 @@ void TestQmllint::testUnqualified_data() 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; + + QTest::newRow("NonSpuriousParent") << QStringLiteral("nonSpuriousParentWarning.qml") << QStringLiteral("property int x: <id>.parent.x") << 6 << 25; } -void TestQmllint::testUnqualifiedNoSpuriousParentWarning() +void TestQmllint::testUnknownCausesFail() { - 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()); - } + const QString unknownNotFound = runQmllint("unknownElement.qml", false); + QVERIFY(unknownNotFound.contains( + QStringLiteral("warning: Unknown was not found. Did you add all import paths?"))); } -void TestQmllint::catchIdentifierNoFalsePositive() +void TestQmllint::dirtyQmlCode_data() { - 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); + QTest::addColumn<QString>("filename"); + QTest::addColumn<QString>("warningMessage"); + QTest::addColumn<QString>("notContained"); + + QTest::newRow("Invalid_syntax_QML") + << QStringLiteral("failure1.qml") + << QStringLiteral("failure1.qml:4 : Expected token `:'") + << QString(); + QTest::newRow("Invalid_syntax_JS") + << QStringLiteral("failure1.js") + << QStringLiteral("failure1.js:4 : Expected token `;'") + << QString(); + QTest::newRow("AutomatchedSignalHandler") + << QStringLiteral("AutomatchedSignalHandler.qml") + << QString("Warning: unqualified access at 12:36") + << QStringLiteral("no matching signal found"); + QTest::newRow("MemberNotFound") + << QStringLiteral("memberNotFound.qml") + << QString("Warning: Property \"foo\" not found on type \"QtObject\" at 6:31") + << QString(); + QTest::newRow("UnknownJavascriptMethd") + << QStringLiteral("unknownJavascriptMethod.qml") + << QString("Warning: Property \"foo2\" not found on type \"Methods\" at 5:25") + << QString(); + QTest::newRow("badAlias") + << QStringLiteral("badAlias.qml") + << QString("Warning: unqualified access at 4:27") + << QString(); + QTest::newRow("badAliasProperty") + << QStringLiteral("badAliasProperty.qml") + << QString("Warning: Property \"nowhere\" not found on type \"QtObject\" at 5:32") + << QString(); + QTest::newRow("badParent") + << QStringLiteral("badParent.qml") + << QString("Warning: Property \"rrr\" not found on type \"Item\" at 5:34") + << QString(); + QTest::newRow("parentIsComponent") + << QStringLiteral("parentIsComponent.qml") + << QString("Warning: Property \"progress\" not found on type \"QQuickItem\" at 7:39") + << QString(); + QTest::newRow("badTypeAssertion") + << QStringLiteral("badTypeAssertion.qml") + << QString("Warning: Property \"rrr\" not found on type \"Item\" at 5:39") + << QString(); + QTest::newRow("incompleteQmltypes") + << QStringLiteral("incompleteQmltypes.qml") + << QString("Warning: Type \"QPalette\" of member \"palette\" not found at 5:26") + << QString(); + QTest::newRow("inheritanceCylce") + << QStringLiteral("Cycle1.qml") + << QString("Warning: Cycle2 is part of an inheritance cycle: Cycle2 -> Cycle3 -> Cycle1 -> Cycle2") + << QString(); } -void TestQmllint::test() +void TestQmllint::dirtyQmlCode() { QFETCH(QString, filename); - QFETCH(bool, isValid); - QStringList args; - args << QStringLiteral("--silent") << testFile(filename); + QFETCH(QString, warningMessage); + QFETCH(QString, notContained); + + const QString output = runQmllint(filename, false); + QVERIFY(output.contains(warningMessage)); + if (!notContained.isEmpty()) + QVERIFY(!output.contains(notContained)); +} + +void TestQmllint::cleanQmlCode_data() +{ + QTest::addColumn<QString>("filename"); + QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml"); + QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml"); + QTest::newRow("JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js"); + QTest::newRow("uiQml") << QStringLiteral("FormUser.qml"); + QTest::newRow("methodInScope") << QStringLiteral("MethodInScope.qml"); + QTest::newRow("importWithPrefix") << QStringLiteral("ImportWithPrefix.qml"); + QTest::newRow("catchIdentifier") << QStringLiteral("catchIdentifierNoWarning.qml"); + QTest::newRow("qmldirAndQmltypes") << QStringLiteral("qmldirAndQmltypes.qml"); + QTest::newRow("forLoop") << QStringLiteral("forLoop.qml"); + QTest::newRow("esmodule") << QStringLiteral("esmodule.mjs"); + QTest::newRow("methodsInJavascript") << QStringLiteral("javascriptMethods.qml"); + QTest::newRow("goodAlias") << QStringLiteral("goodAlias.qml"); + QTest::newRow("goodParent") << QStringLiteral("goodParent.qml"); + QTest::newRow("goodTypeAssertion") << QStringLiteral("goodTypeAssertion.qml"); + QTest::newRow("AttachedProps") << QStringLiteral("AttachedProps.qml"); + QTest::newRow("unknownBuiltinFont") << QStringLiteral("ButtonLoader.qml"); + QTest::newRow("confusingImport") << QStringLiteral("Dialog.qml"); + QTest::newRow("qualifiedAttached") << QStringLiteral("Drawer.qml"); +} + +void TestQmllint::cleanQmlCode() +{ + QFETCH(QString, filename); + const QString warnings = runQmllint(filename, true); + QVERIFY(warnings.isEmpty()); +} - bool success = QProcess::execute(m_qmllintPath, args) == 0; - QCOMPARE(success, isValid); +QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed) +{ + auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath); + QStringList args; + args << QStringLiteral("-U") << testFile(fileToLint) + << QStringLiteral("-I") << qmlImportDir + << QStringLiteral("-I") << dataDirectory() + << QStringLiteral("--silent"); + QString errors; + auto verify = [&](bool isSilent) { + QProcess process; + process.start(m_qmllintPath, args); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + if (shouldSucceed) + QCOMPARE(process.exitCode(), 0); + else + QVERIFY(process.exitCode() != 0); + errors = process.readAllStandardError(); + + if (isSilent) + QVERIFY(errors.isEmpty()); + }; + verify(true); + args.removeLast(); + verify(false); + return errors; } QTEST_MAIN(TestQmllint) diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index e244369581..0501a8112a 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -131,6 +131,7 @@ void tst_qmlmin::initTestCase() 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"; + invalidFiles << "tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml"; // generatorFunction.qml is not invalid per se, but the minifier cannot handle yield statements invalidFiles << "tests/auto/qml/qqmlecmascript/data/generatorFunction.qml"; #endif diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp new file mode 100644 index 0000000000..db080a5cad --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "foreign.h" + +int Foreign::things() const +{ + return m_things; +} + +void Foreign::setThings(int things) +{ + if (m_things == things) + return; + + m_things = things; + emit thingsChanged(m_things); +} diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.h b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h new file mode 100644 index 0000000000..dc9fbc84a8 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef FOREIGN_H +#define FOREIGN_H + +#include <QtCore/qobject.h> +#include <QtCore/qsize.h> + +class Foreign : public QObject +{ + Q_OBJECT + Q_PROPERTY(int things READ things WRITE setThings NOTIFY thingsChanged) + +public: + int things() const; + +public slots: + void setThings(int things); + +signals: + void thingsChanged(int things); + +private: + int m_things = 0; +}; + +class SizeGadget : public QSize +{ + Q_GADGET + Q_PROPERTY(int height READ height WRITE setHeight FINAL) +}; + +#endif // FOREIGN_H diff --git a/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro new file mode 100644 index 0000000000..87521eac43 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/foreign/foreign.pro @@ -0,0 +1,10 @@ +TEMPLATE = lib +QT = core + +macos:CONFIG -= app_bundle +CONFIG -= debug_and_release_target + +SOURCES = foreign.cpp +HEADERS = foreign.h + +CONFIG += metatypes static diff --git a/tests/auto/qml/qmltyperegistrar/hppheader.hpp b/tests/auto/qml/qmltyperegistrar/hppheader.hpp new file mode 100644 index 0000000000..f5fc881b77 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/hppheader.hpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef HPPHEADER_HPP +#define HPPHEADER_HPP + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class HppClass : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int eieiei READ eieiei WRITE setEieiei NOTIFY eieieiChanged) + +public: + int eieiei() const + { + return m_eieiei; + } + +public slots: + void setEieiei(int eieiei) + { + if (m_eieiei == eieiei) + return; + + m_eieiei = eieiei; + emit eieieiChanged(m_eieiei); + } + +signals: + void eieieiChanged(int eieiei); + +private: + int m_eieiei; +}; + +#endif // HPPHEADER_HPP diff --git a/tests/auto/qml/qmltyperegistrar/noextheader b/tests/auto/qml/qmltyperegistrar/noextheader new file mode 100644 index 0000000000..3b6cb6d72c --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/noextheader @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef NOEXTHEADER +#define NOEXTHEADER + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> + +class Noext : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int gagaga READ gagaga WRITE setGagaga NOTIFY gagagaChanged) + +public: + int gagaga() const + { + return m_gagaga; + } + +public slots: + void setGagaga(int gagaga) + { + if (m_gagaga == gagaga) + return; + + m_gagaga = gagaga; + emit gagagaChanged(m_gagaga); + } + +signals: + void gagagaChanged(int gagaga); + +private: + int m_gagaga; +}; + +#endif // NOEXTHEADER diff --git a/tests/auto/qml/qmltyperegistrar/qmltyperegistrar.pro b/tests/auto/qml/qmltyperegistrar/qmltyperegistrar.pro new file mode 100644 index 0000000000..738d099123 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/qmltyperegistrar.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = foreign tst_qmltyperegistrar.pro + +tst_qmltyperegistrar_pro.depends = foreign diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp new file mode 100644 index 0000000000..1cfcd689c6 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "tst_qmltyperegistrar.h" +#include <QtTest/qtest.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qfile.h> + +void tst_qmltyperegistrar::initTestCase() +{ + QFile file(QCoreApplication::applicationDirPath() + "/tst_qmltyperegistrar.qmltypes"); + QVERIFY(file.open(QIODevice::ReadOnly)); + qmltypesData = file.readAll(); + QVERIFY(file.atEnd()); + QCOMPARE(file.error(), QFile::NoError); +} + +void tst_qmltyperegistrar::qmltypesHasForeign() +{ + QVERIFY(qmltypesData.contains("things")); +} + +void tst_qmltyperegistrar::qmltypesHasHppClassAndNoext() +{ + QVERIFY(qmltypesData.contains("HppClass")); + QVERIFY(qmltypesData.contains("Noext")); +} + +void tst_qmltyperegistrar::qmltypesHasFileNames() +{ + QVERIFY(qmltypesData.contains("file: \"hppheader.hpp\"")); + QVERIFY(qmltypesData.contains("file: \"noextheader\"")); + QVERIFY(qmltypesData.contains("file: \"tst_qmltyperegistrar.h\"")); +} + +void tst_qmltyperegistrar::qmltypesHasFlags() +{ + QVERIFY(qmltypesData.contains("name: \"Flags\"")); + QVERIFY(qmltypesData.contains("alias: \"Flag\"")); + QVERIFY(qmltypesData.contains("isFlag: true")); +} + +void tst_qmltyperegistrar::superAndForeignTypes() +{ + QVERIFY(qmltypesData.contains("values: [\"Pixel\", \"Centimeter\", \"Inch\", \"Point\"]")); + QVERIFY(qmltypesData.contains("name: \"SizeGadget\"")); + QVERIFY(qmltypesData.contains("prototype: \"SizeEnums\"")); + QVERIFY(qmltypesData.contains("Property { name: \"height\"; type: \"int\" }")); + QVERIFY(qmltypesData.contains("Property { name: \"width\"; type: \"int\" }")); + QVERIFY(qmltypesData.contains("Method { name: \"sizeToString\"; type: \"string\" }")); +} + +QTEST_MAIN(tst_qmltyperegistrar) diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h new file mode 100644 index 0000000000..09485ab0b6 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef TST_QMLTYPEREGISTRAR_H +#define TST_QMLTYPEREGISTRAR_H + +#include "foreign.h" + +#include <QtQml/qqml.h> + +class SizeEnums +{ + Q_GADGET + QML_NAMED_ELEMENT(SizeEnums) + QML_UNCREATABLE("Element is not creatable.") + +public: + enum Unit { Pixel, Centimeter, Inch, Point }; + Q_ENUM(Unit) +}; + +class SizeValueType : public SizeEnums +{ + QSize v; + Q_GADGET + Q_PROPERTY(int width READ width WRITE setWidth FINAL) + QML_NAMED_ELEMENT(MySize) + QML_FOREIGN(SizeGadget) + +public: + Q_INVOKABLE QString sizeToString() const + { + return QString::fromLatin1("%1x%2").arg(v.width()).arg(v.height()); + } + + int width() const { return v.width(); } + void setWidth(int width) { v.setWidth(width); } +}; + +class Local : public Foreign +{ + Q_OBJECT + QML_ELEMENT +public: + enum Flag { + Flag1 = 0x1, + Flag2 = 0x2, + Flag3 = 0x4, + Flag4 = 0x8 + }; + Q_DECLARE_FLAGS(Flags, Flag) + Q_FLAG(Flags) +}; + +class tst_qmltyperegistrar : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void qmltypesHasForeign(); + void qmltypesHasHppClassAndNoext(); + void qmltypesHasFileNames(); + void qmltypesHasFlags(); + void superAndForeignTypes(); + +private: + QByteArray qmltypesData; +}; + +#endif // TST_QMLTYPEREGISTRAR_H diff --git a/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro new file mode 100644 index 0000000000..fe21b122c2 --- /dev/null +++ b/tests/auto/qml/qmltyperegistrar/tst_qmltyperegistrar.pro @@ -0,0 +1,20 @@ +TEMPLATE = app + +QT = core qml testlib +CONFIG += testcase qmltypes +CONFIG -= debug_and_release_target +macos:CONFIG -= app_bundle + +SOURCES += tst_qmltyperegistrar.cpp +HEADERS += \ + hppheader.hpp \ + noextheader \ + tst_qmltyperegistrar.h + +QMLTYPES_FILENAME = tst_qmltyperegistrar.qmltypes +QML_FOREIGN_METATYPES += foreign/foreign_metatypes.json +QML_IMPORT_NAME = QmlTypeRegistrarTest +QML_IMPORT_VERSION = 1.0 + +INCLUDEPATH += foreign +LIBS += -Lforeign -lforeign diff --git a/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm Binary files differnew file mode 100644 index 0000000000..926d74f905 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.qm diff --git a/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts new file mode 100644 index 0000000000..2105cfb2cf --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml_de_CH.ts @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_CH" sourcelanguage="en"> +<context> + <name>loadTranslation</name> + <message> + <source>translate it</source> + <translation>Grüezi</translation> + </message> +</context> +</TS> diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index 2bee283826..f636e527c3 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -53,6 +53,7 @@ private slots: void removeObjectsWhenDestroyed(); void loadTranslation_data(); void loadTranslation(); + void translationChange(); void setInitialProperties(); void failureToLoadTriggersWarningSignal(); @@ -278,6 +279,28 @@ void tst_qqmlapplicationengine::loadTranslation() QCOMPARE(rootObject->property("translation").toString(), translation); } +void tst_qqmlapplicationengine::translationChange() +{ + if (QLocale().language() == QLocale::SwissGerman) { + QSKIP("Skipping this when running under the Swiss locale as we would always load translation."); + } + + QQmlApplicationEngine engine(testFileUrl("loadTranslation.qml")); + + QCOMPARE(engine.uiLanguage(), QLocale().bcp47Name()); + + QObject *rootObject = engine.rootObjects().first(); + QVERIFY(rootObject); + + QCOMPARE(rootObject->property("translation").toString(), "translated"); + + engine.setUiLanguage("de_CH"); + QCOMPARE(rootObject->property("translation").toString(), QString::fromUtf8("Gr\u00FCezi")); + + engine.setUiLanguage(QString()); + QCOMPARE(rootObject->property("translation").toString(), "translate it"); +} + void tst_qqmlapplicationengine::setInitialProperties() { QQmlApplicationEngine test {}; diff --git a/tests/auto/qml/qqmlcomponent/data/AliasToSubcomponentRequiredBase.qml b/tests/auto/qml/qqmlcomponent/data/AliasToSubcomponentRequiredBase.qml new file mode 100644 index 0000000000..f693cb4c2e --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/AliasToSubcomponentRequiredBase.qml @@ -0,0 +1,10 @@ +import QtQuick 2.13 + +Item { + property alias i_alias: sub.i + + Item { + id: sub + required property int i + } +} diff --git a/tests/auto/qml/qqmlcomponent/data/BaseWithRequired.qml b/tests/auto/qml/qqmlcomponent/data/BaseWithRequired.qml new file mode 100644 index 0000000000..0ce61c8d9d --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/BaseWithRequired.qml @@ -0,0 +1,6 @@ +import QtQuick 2.14 + +Item { + id: base + required property int i +} diff --git a/tests/auto/qml/qqmlcomponent/data/RequiredDefault.qml b/tests/auto/qml/qqmlcomponent/data/RequiredDefault.qml new file mode 100644 index 0000000000..7e8f225a52 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/RequiredDefault.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + required default property Text requiredDefault +} diff --git a/tests/auto/qml/qqmlcomponent/data/aliasToSubcomponentNotSet.qml b/tests/auto/qml/qqmlcomponent/data/aliasToSubcomponentNotSet.qml new file mode 100644 index 0000000000..527eb417e5 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/aliasToSubcomponentNotSet.qml @@ -0,0 +1,4 @@ +import QtQuick 2.13 + +AliasToSubcomponentRequiredBase { +} diff --git a/tests/auto/qml/qqmlcomponent/data/createdFromQml.qml b/tests/auto/qml/qqmlcomponent/data/createdFromQml.qml new file mode 100644 index 0000000000..60a2077606 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/createdFromQml.qml @@ -0,0 +1,11 @@ +import QtQuick 2.14 + +Item { + id: root + property Item it + Component.onCompleted: function() { + let component = Qt.createComponent("requiredNotSet.qml", Component.PreferSynchronous, root) + console.assert(component.status == Component.Ready) + it = component.createObject(component, {i: 42}) + } +} diff --git a/tests/auto/qml/qqmlcomponent/data/createdFromQmlFail.qml b/tests/auto/qml/qqmlcomponent/data/createdFromQmlFail.qml new file mode 100644 index 0000000000..e09ddcccc1 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/createdFromQmlFail.qml @@ -0,0 +1,11 @@ +import QtQuick 2.14 + +Item { + id: root + property Item it + Component.onCompleted: function() { + let component = Qt.createComponent("requiredNotSet.qml", Component.PreferSynchronous, root) + console.assert(component.status == Component.Ready) + root.it = component.createObject(component) + } +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredDefault.1.qml b/tests/auto/qml/qqmlcomponent/data/requiredDefault.1.qml new file mode 100644 index 0000000000..68dff22f33 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredDefault.1.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +RequiredDefault { + Text {text: "Hello, world!"} +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredDefault.2.qml b/tests/auto/qml/qqmlcomponent/data/requiredDefault.2.qml new file mode 100644 index 0000000000..a6c4e8ea3f --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredDefault.2.qml @@ -0,0 +1,3 @@ +import QtQuick 2.15 + +RequiredDefault { } diff --git a/tests/auto/qml/qqmlcomponent/data/requiredDefault.3.qml b/tests/auto/qml/qqmlcomponent/data/requiredDefault.3.qml new file mode 100644 index 0000000000..19b3271858 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredDefault.3.qml @@ -0,0 +1,6 @@ +import QtQuick 2.15 +import qt.test 1.0 + +RequiredDefaultCpp { + Text {text: "Hello, world!"} +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredDefault.4.qml b/tests/auto/qml/qqmlcomponent/data/requiredDefault.4.qml new file mode 100644 index 0000000000..acd56db328 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredDefault.4.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import qt.test 1.0 + +RequiredDefaultCpp { } diff --git a/tests/auto/qml/qqmlcomponent/data/requiredNotSet.qml b/tests/auto/qml/qqmlcomponent/data/requiredNotSet.qml new file mode 100644 index 0000000000..c0b5d695f1 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredNotSet.qml @@ -0,0 +1,5 @@ +import QtQuick 2.14 + +Item { + required property int i +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredSetInSameFile.qml b/tests/auto/qml/qqmlcomponent/data/requiredSetInSameFile.qml new file mode 100644 index 0000000000..76dfcd87e5 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredSetInSameFile.qml @@ -0,0 +1,6 @@ +import QtQuick 2.14 + +Item { + required property int i + i: 42 +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredSetLater.qml b/tests/auto/qml/qqmlcomponent/data/requiredSetLater.qml new file mode 100644 index 0000000000..3b7811e453 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredSetLater.qml @@ -0,0 +1,5 @@ +import QtQuick 2.14 + +BaseWithRequired { + i: 13 +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasAfterSameFile.qml b/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasAfterSameFile.qml new file mode 100644 index 0000000000..163616bc8a --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasAfterSameFile.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +Item { + id: withAlias + j: 42 + required property int i + property alias j: withAlias.i +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasBeforeSameFile.qml b/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasBeforeSameFile.qml new file mode 100644 index 0000000000..e59bccf379 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasBeforeSameFile.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +Item { + id: withAlias + required property int i + j: 42 + property alias j: withAlias.i +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasParentFile.qml b/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasParentFile.qml new file mode 100644 index 0000000000..0bc2f8a7df --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredSetViaAliasParentFile.qml @@ -0,0 +1,7 @@ +import QtQuick 2.14 + +BaseWithRequired { + id: withAlias + property alias j: withAlias.i + j: 42 +} diff --git a/tests/auto/qml/qqmlcomponent/data/requiredSetViaChainedAlias.qml b/tests/auto/qml/qqmlcomponent/data/requiredSetViaChainedAlias.qml new file mode 100644 index 0000000000..45cf02d3d8 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/requiredSetViaChainedAlias.qml @@ -0,0 +1,9 @@ +import QtQml 2.12 + +QtObject { + id: stuff + required property int x; + property alias y: stuff.x + property alias z: stuff.y + z: 5 +} diff --git a/tests/auto/qml/qqmlcomponent/data/setViaAliasToSubcomponent.qml b/tests/auto/qml/qqmlcomponent/data/setViaAliasToSubcomponent.qml new file mode 100644 index 0000000000..2dc182ea82 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/setViaAliasToSubcomponent.qml @@ -0,0 +1,5 @@ +import QtQuick 2.13 + +AliasToSubcomponentRequiredBase { + i_alias: 42 +} diff --git a/tests/auto/qml/qqmlcomponent/data/shadowing.qml b/tests/auto/qml/qqmlcomponent/data/shadowing.qml new file mode 100644 index 0000000000..4d119d8884 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/shadowing.qml @@ -0,0 +1,5 @@ +import QtQuick 2.14 + +BaseWithRequired { + property int i: 13 +} diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 1d2fa42b75..43cbd93396 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -121,6 +121,9 @@ private slots: void relativeUrl_data(); void relativeUrl(); void setDataNoEngineNoSegfault(); + void testRequiredProperties_data(); + void testRequiredProperties(); + void testRequiredPropertiesFromQml(); void testSetInitialProperties(); private: @@ -668,35 +671,96 @@ void tst_qqmlcomponent::setDataNoEngineNoSegfault() QVERIFY(!c); } -void tst_qqmlcomponent::testSetInitialProperties() +class RequiredDefaultCpp : public QObject +{ + Q_OBJECT +public: + Q_PROPERTY(QQuickItem *defaultProperty MEMBER m_defaultProperty NOTIFY defaultPropertyChanged REQUIRED) + Q_SIGNAL void defaultPropertyChanged(); + Q_CLASSINFO("DefaultProperty", "defaultProperty") +private: + QQuickItem *m_defaultProperty = nullptr; +}; + +void tst_qqmlcomponent::testRequiredProperties_data() +{ + qmlRegisterType<RequiredDefaultCpp>("qt.test", 1, 0, "RequiredDefaultCpp"); + QTest::addColumn<QUrl>("testFile"); + QTest::addColumn<bool>("shouldSucceed"); + QTest::addColumn<QString>("errorMsg"); + + QTest::addRow("requiredSetViaChainedAlias") << testFileUrl("requiredSetViaChainedAlias.qml") << true << ""; + QTest::addRow("requiredNotSet") << testFileUrl("requiredNotSet.qml") << false << "Required property i was not initialized"; + QTest::addRow("requiredSetInSameFile") << testFileUrl("requiredSetInSameFile.qml") << true << ""; + QTest::addRow("requiredSetViaAlias1") << testFileUrl("requiredSetViaAliasBeforeSameFile.qml") << true << ""; + QTest::addRow("requiredSetViaAlias2") << testFileUrl("requiredSetViaAliasAfterSameFile.qml") << true << ""; + QTest::addRow("requiredSetViaAlias3") << testFileUrl("requiredSetViaAliasParentFile.qml") << true << ""; + QTest::addRow("shadowing") << testFileUrl("shadowing.qml") << false << "Required property i was not initialized"; + QTest::addRow("setLater") << testFileUrl("requiredSetLater.qml") << true << ""; + QTest::addRow("setViaAliasToSubcomponent") << testFileUrl("setViaAliasToSubcomponent.qml") << true << ""; + QTest::addRow("aliasToSubcomponentNotSet") << testFileUrl("aliasToSubcomponentNotSet.qml") << false << "It can be set via the alias property i_alias"; + QTest::addRow("required default set") << testFileUrl("requiredDefault.1.qml") << true << ""; + QTest::addRow("required default not set") << testFileUrl("requiredDefault.2.qml") << false << "Required property requiredDefault was not initialized"; + QTest::addRow("required default set (C++)") << testFileUrl("requiredDefault.3.qml") << true << ""; + QTest::addRow("required default not set (C++)") << testFileUrl("requiredDefault.4.qml") << false << "Required property defaultProperty was not initialized"; +} + + +void tst_qqmlcomponent::testRequiredProperties() +{ + QQmlEngine eng; + using QScopedObjPointer = QScopedPointer<QObject>; + QFETCH(QUrl, testFile); + QFETCH(bool, shouldSucceed); + QQmlComponent comp(&eng); + comp.loadUrl(testFile); + QScopedObjPointer obj {comp.create()}; + if (shouldSucceed) + QVERIFY(obj); + else { + QVERIFY(!obj); + QFETCH(QString, errorMsg); + QVERIFY(comp.errorString().contains(errorMsg)); + } +} + +void tst_qqmlcomponent::testRequiredPropertiesFromQml() { QQmlEngine eng; { - // JSON based initialization QQmlComponent comp(&eng); - comp.loadUrl(testFileUrl("allJSONTypes.qml")); - QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) }; + comp.loadUrl(testFileUrl("createdFromQml.qml")); + QScopedPointer<QObject> obj { comp.create() }; 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)); + auto root = qvariant_cast<QQuickItem*>(obj->property("it")); + QVERIFY(root); + QCOMPARE(root->property("i").toInt(), 42); } { - // QVariant + QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(".*requiredNotSet.qml:4:5: Required property i was not initialized")); QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("createdFromQmlFail.qml")); + QScopedPointer<QObject> obj { comp.create() }; + QVERIFY(obj); + QCOMPARE(qvariant_cast<QQuickItem *>(obj->property("it")), nullptr); + } +} + +struct ComponentWithPublicSetInitial : QQmlComponent +{ + using QQmlComponent::QQmlComponent; + void setInitialProperties(QObject *o, QVariantMap map) + { + QQmlComponent::setInitialProperties(o, map); + } +}; + +void tst_qqmlcomponent::testSetInitialProperties() +{ + QQmlEngine eng; + { + // QVariant + ComponentWithPublicSetInitial comp(&eng); comp.loadUrl(testFileUrl("variantBasedInitialization.qml")); QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) }; QVERIFY(obj); @@ -728,8 +792,6 @@ void tst_qqmlcomponent::testSetInitialProperties() }); #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); @@ -751,13 +813,20 @@ void tst_qqmlcomponent::testSetInitialProperties() } { + // createWithInitialProperties convenience function + QQmlComponent comp(&eng); + comp.loadUrl(testFileUrl("requiredNotSet.qml")); + QScopedPointer<QObject> obj {comp.createWithInitialProperties( QVariantMap { {QLatin1String("i"), QJsonValue{42}} })}; + QVERIFY(obj); + QCOMPARE(obj->property("i"), 42); + } + { // 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")); } diff --git a/tests/auto/qml/qqmlconsole/data/logging.qml b/tests/auto/qml/qqmlconsole/data/logging.qml index 1f929d311b..f5eaeb442a 100644 --- a/tests/auto/qml/qqmlconsole/data/logging.qml +++ b/tests/auto/qml/qqmlconsole/data/logging.qml @@ -31,6 +31,8 @@ import QtQuick 2.0 QtObject { id:root + required property var customObject + required property var stringListProperty function consoleCount() { console.count("console.count", "Ignore additional argument"); @@ -67,7 +69,7 @@ QtObject { console.log(1, "pong!", new Object); console.log(1, ["ping","pong"], new Object, 2); - console.log(contextStringListProperty); + console.log(stringListProperty); console.log(customObject); console.log([[1,2,3,[2,2,2,2],4],[5,6,7,8]]); diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp index b157314071..48613d04f1 100644 --- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp +++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp @@ -95,16 +95,16 @@ void tst_qqmlconsole::logging() QTest::ignoreMessage(QtDebugMsg, "QVariant(CustomObject, MY OBJECT)"); QTest::ignoreMessage(QtDebugMsg, "[[1,2,3,[2,2,2,2],4],[5,6,7,8]]"); - QScopedPointer<QQmlContext> loggingContext(new QQmlContext(engine.rootContext())); QStringList stringList; stringList << QStringLiteral("Hello") << QStringLiteral("World"); - loggingContext->setContextProperty("contextStringListProperty", stringList); CustomObject customObject; QVERIFY(QMetaType::registerDebugStreamOperator<CustomObject>()); - loggingContext->setContextProperty("customObject", QVariant::fromValue(customObject)); QQmlComponent component(&engine, testUrl); - QScopedPointer<QObject> object(component.create(loggingContext.data())); + QScopedPointer<QObject> object(component.createWithInitialProperties({ + {"customObject", QVariant::fromValue(customObject)}, + {"stringListProperty", stringList} + })); QVERIFY(object != nullptr); } diff --git a/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml b/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml new file mode 100644 index 0000000000..01ebfd8e1e --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/abstractItemModel.qml @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** 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 QtQml.Models 2.15 +import QtQuick 2.15 + +import Test 1.0 + +DelegateModel { + model: AbstractItemModel {} + delegate: Item {} +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml b/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml new file mode 100644 index 0000000000..f6cd886e09 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/integerModel.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** 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 QtQml.Models 2.15 +import QtQuick 2.15 + +DelegateModel { + model: 100 + delegate: Item {} +} diff --git a/tests/auto/qml/qqmldelegatemodel/data/listModel.qml b/tests/auto/qml/qqmldelegatemodel/data/listModel.qml new file mode 100644 index 0000000000..1ee8bcac72 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/listModel.qml @@ -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$ +** +****************************************************************************/ + +import QtQml.Models 2.15 +import QtQuick 2.15 + +DelegateModel { + model: ListModel { + ListElement { + name: "Item 0" + } + ListElement { + name: "Item 1" + } + ListElement { + name: "Item 2" + } + } + delegate: Item {} +} diff --git a/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro b/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro new file mode 100644 index 0000000000..7fdd3ab5f1 --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/qqmldelegatemodel.pro @@ -0,0 +1,13 @@ +CONFIG += testcase +TARGET = tst_qqmldelegatemodel +macos:CONFIG -= app_bundle + +QT += qml testlib core-private qml-private qmlmodels-private + +SOURCES += tst_qqmldelegatemodel.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +OTHER_FILES += data/* diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp new file mode 100644 index 0000000000..87f42c0c8a --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** 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 <QtQml/qqmlcomponent.h> +#include <QtQmlModels/private/qqmldelegatemodel_p.h> + +#include "../../shared/util.h" + +class tst_QQmlDelegateModel : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QQmlDelegateModel(); + +private slots: + void valueWithoutCallingObjectFirst_data(); + void valueWithoutCallingObjectFirst(); +}; + +class AbstractItemModel : public QAbstractItemModel +{ + Q_OBJECT +public: + AbstractItemModel() + { + for (int i = 0; i < 3; ++i) + mValues.append(QString::fromLatin1("Item %1").arg(i)); + } + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) + return QModelIndex(); + + return createIndex(row, column); + } + + QModelIndex parent(const QModelIndex &) const override + { + return QModelIndex(); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) + return 0; + + return mValues.count(); + } + + int columnCount(const QModelIndex &parent) const override + { + if (parent.isValid()) + return 0; + + return 1; + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + if (role != Qt::DisplayRole) + return QVariant(); + + return mValues.at(index.row()); + } + +private: + QVector<QString> mValues; +}; + +tst_QQmlDelegateModel::tst_QQmlDelegateModel() +{ + qmlRegisterType<AbstractItemModel>("Test", 1, 0, "AbstractItemModel"); +} + +void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst_data() +{ + QTest::addColumn<QUrl>("qmlFileUrl"); + QTest::addColumn<int>("index"); + QTest::addColumn<QString>("role"); + QTest::addColumn<QVariant>("expectedValue"); + + QTest::addRow("integer") << testFileUrl("integerModel.qml") + << 50 << QString::fromLatin1("modelData") << QVariant(50); + QTest::addRow("ListModel") << testFileUrl("listModel.qml") + << 1 << QString::fromLatin1("name") << QVariant(QLatin1String("Item 1")); + QTest::addRow("QAbstractItemModel") << testFileUrl("abstractItemModel.qml") + << 1 << QString::fromLatin1("display") << QVariant(QLatin1String("Item 1")); +} + +// Tests that it's possible to call variantValue() without creating +// costly delegate items first via object(). +void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst() +{ + QFETCH(const QUrl, qmlFileUrl); + QFETCH(const int, index); + QFETCH(const QString, role); + QFETCH(const QVariant, expectedValue); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(qmlFileUrl); + QScopedPointer<QObject> root(component.create()); + QVERIFY2(root, qPrintable(component.errorString())); + QQmlDelegateModel *model = qobject_cast<QQmlDelegateModel*>(root.data()); + QVERIFY(model); + QCOMPARE(model->variantValue(index, role), expectedValue); +} + +QTEST_MAIN(tst_QQmlDelegateModel) + +#include "tst_qqmldelegatemodel.moc" diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp index bc4ba9437c..7e96205743 100644 --- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp +++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp @@ -63,10 +63,10 @@ namespace { 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); + if (e.loc.startLine > 0) { + errorString += QLatin1Char(':') + QString::number(e.loc.startLine); + if (e.loc.startColumn > 0) + errorString += QLatin1Char(':') + QString::number(e.loc.startColumn); } errorString += QLatin1String(": ") + e.message; diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index 3233e7f105..03e3262e7b 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -36,7 +36,6 @@ #include <QtCore/qregularexpression.h> #include <QtQml/qqmllist.h> #include <QtCore/qrect.h> -#include <QtGui/qmatrix.h> #include <QtGui/qcolor.h> #include <QtGui/qvector3d.h> #include <QtGui/QFont> diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 26232ac1a3..4b0cbfa1aa 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -74,6 +74,7 @@ public: private slots: void initTestCase(); + void arrayIncludesValueType(); void assignBasicTypes(); void assignDate_data(); void assignDate(); @@ -414,6 +415,36 @@ void tst_qqmlecmascript::initTestCase() registerTypes(); } +void tst_qqmlecmascript::arrayIncludesValueType() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + // It is vital that QtQuick is imported below else we get a warning about + // QQml_colorProvider and tst_qqmlecmascript::signalParameterTypes fails due + // to some static variable being initialized with the wrong value + component.setData(R"( + import QtQuick 2.15 + import QtQml 2.15 + QtObject { + id: root + property color r: Qt.rgba(1, 0, 0) + property color g: Qt.rgba(0, 1, 0) + property color b: Qt.rgba(0, 0, 1) + property var colors: [r, g, b] + property bool success: false + + Component.onCompleted: { + root.success = root.colors.includes(root.g) + } + } + )", QUrl("testData")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + auto success = o->property("success"); + QVERIFY(success.isValid()); + QVERIFY(success.toBool()); +} + void tst_qqmlecmascript::assignBasicTypes() { QQmlEngine engine; @@ -6439,6 +6470,8 @@ void tst_qqmlecmascript::topLevelGeneratorFunction() QQmlComponent component(&engine, testFileUrl("generatorFunction.qml")); QScopedPointer<QObject> o {component.create()}; + if (!o) + qDebug() << component.errorString(); QVERIFY(o != nullptr); // check that generator works correctly in QML @@ -7271,7 +7304,7 @@ void tst_qqmlecmascript::forInLoop() QMetaObject::invokeMethod(object, "listProperty"); - QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts); + QStringList r = object->property("listResult").toString().split("|", Qt::SkipEmptyParts); QCOMPARE(r.size(), 3); QCOMPARE(r[0],QLatin1String("0=obj1")); QCOMPARE(r[1],QLatin1String("1=obj2")); diff --git a/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.1.qml index 812242f146..a37461a173 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.2.qml index a171ee6b28..1dc06ee6ae 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyAggregateEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.1.qml index de40284452..ea945eebb0 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.2.qml index 4939087b31..9a4fc53833 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyAggregateVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyComponent.1.qml index 5cee0341fe..155dbdcb91 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyComponent.2.qml index 2a13822fab..940d54be90 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.1.qml index 2f238175fa..46d20e38bb 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.2.qml index d36e95fec3..ffdb30f072 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyExtendEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.1.qml index 53dd5a17e9..ca669a9708 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.2.qml index e5cd7d60de..a93c3b302c 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyExtendVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.1.qml index d98aef2932..5930e12df6 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.2.qml index 7f438aa995..8f929c20b2 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyPropertyEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.1.qml index 83d6226e83..20bde6d0af 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.2.qml index 98dfb7241b..10ae369459 100644 --- a/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testEmptyPropertyVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testIncubatedComponent.qml b/tests/auto/qml/qqmlengine/data/testIncubatedComponent.qml index 50af9c4f16..422637fe31 100644 --- a/tests/auto/qml/qqmlengine/data/testIncubatedComponent.qml +++ b/tests/auto/qml/qqmlengine/data/testIncubatedComponent.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testLoaderComponent.qml b/tests/auto/qml/qqmlengine/data/testLoaderComponent.qml index a04ca41c26..67d7ddd1c9 100644 --- a/tests/auto/qml/qqmlengine/data/testLoaderComponent.qml +++ b/tests/auto/qml/qqmlengine/data/testLoaderComponent.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testReloadComponent.qml b/tests/auto/qml/qqmlengine/data/testReloadComponent.qml index 74442108cd..79d4f29db0 100644 --- a/tests/auto/qml/qqmlengine/data/testReloadComponent.qml +++ b/tests/auto/qml/qqmlengine/data/testReloadComponent.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testScriptComponent.qml b/tests/auto/qml/qqmlengine/data/testScriptComponent.qml index b33eb48461..4952a08a6c 100644 --- a/tests/auto/qml/qqmlengine/data/testScriptComponent.qml +++ b/tests/auto/qml/qqmlengine/data/testScriptComponent.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testTopLevelComponent.qml b/tests/auto/qml/qqmlengine/data/testTopLevelComponent.qml index 6cf8ec4203..a6a1f4d4d3 100644 --- a/tests/auto/qml/qqmlengine/data/testTopLevelComponent.qml +++ b/tests/auto/qml/qqmlengine/data/testTopLevelComponent.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testTransientComponent.1.qml b/tests/auto/qml/qqmlengine/data/testTransientComponent.1.qml index d3e6ffd7cf..c661cb6fe4 100644 --- a/tests/auto/qml/qqmlengine/data/testTransientComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testTransientComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testTransientComponent.2.qml b/tests/auto/qml/qqmlengine/data/testTransientComponent.2.qml index acb0113e61..8b6c7eda4d 100644 --- a/tests/auto/qml/qqmlengine/data/testTransientComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testTransientComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.1.qml index a5beede469..8c0596eae4 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.2.qml index 4c8e52f251..39b604d42f 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEAggregateEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.1.qml index 983d6e824c..807203b6c7 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.2.qml index fc8e5a0cd4..100b287cfd 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEAggregateVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEComponent.1.qml index fcfd05c51f..dddf51b926 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEComponent.2.qml index f434406eec..fa508f16ac 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.1.qml index 1dcaec90e6..b36d6ebf14 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.2.qml index fd7d7e454c..fce617046b 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEExtendEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.1.qml index d2dab32fc9..39629d47df 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.2.qml index 813e43896c..a4c947cc17 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEExtendVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.1.qml index c6f0b7928b..a87372cd27 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.2.qml index 255138520c..c4b16d8ec9 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEPropertyEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.1.qml index 0ad59b32d3..a18a88884c 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.2.qml index 60f72a92fe..eb042e82f8 100644 --- a/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMEPropertyVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.1.qml index 6c7f959f49..1f97e587e0 100644 --- a/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.2.qml index 86060c3998..794686494b 100644 --- a/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMETransientEmptyComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.1.qml b/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.1.qml index c50fd70dec..f442500c7f 100644 --- a/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.1.qml +++ b/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.1.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.2.qml b/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.2.qml index 120d249bc0..d57cd8a739 100644 --- a/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.2.qml +++ b/tests/auto/qml/qqmlengine/data/testVMETransientVMEComponent.2.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 Item { property bool success: false + required property QtObject componentCache function reportError(s) { console.warn(s) } diff --git a/tests/auto/qml/qqmlengine/data/uiLanguage.qml b/tests/auto/qml/qqmlengine/data/uiLanguage.qml new file mode 100644 index 0000000000..bc20351245 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/uiLanguage.qml @@ -0,0 +1,9 @@ +import QtQml 2.15 +QtObject { + property string chosenLanguage: Qt.uiLanguage + property string textToTranslate: { + numberOfTranslationBindingEvaluations++; + return qsTr("Translate me maybe"); + } + property int numberOfTranslationBindingEvaluations: 0 +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 613616ab71..cfbbd2a94c 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -84,6 +84,7 @@ private slots: void aggressiveGc(); void cachedGetterLookup_qtbug_75335(); void createComponentOnSingletonDestruction(); + void uiLanguage(); public slots: QObject *createAQObjectForOwnershipTest () @@ -425,12 +426,13 @@ void tst_qqmlengine::trimComponentCache() QQmlEngine engine; ComponentCacheFunctions componentCache(engine); - engine.rootContext()->setContextProperty("componentCache", &componentCache); engine.setIncubationController(&componentCache); QQmlComponent component(&engine, testFileUrl(file)); QVERIFY2(component.isReady(), qPrintable(component.errorString())); - QScopedPointer<QObject> object(component.create()); + QScopedPointer<QObject> object(component.createWithInitialProperties({ + {"componentCache", QVariant::fromValue(&componentCache)} + })); QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); } @@ -512,6 +514,7 @@ void tst_qqmlengine::failedCompilation() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(file)); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); QVERIFY(!component.isReady()); QScopedPointer<QObject> object(component.create()); QVERIFY(object.isNull()); @@ -602,11 +605,10 @@ void tst_qqmlengine::objectOwnership() { QQmlEngine engine; QQmlComponent c(&engine); - engine.rootContext()->setContextProperty("test", this); QQmlEngine::setObjectOwnership(ptr, QQmlEngine::JavaScriptOwnership); - c.setData("import QtQuick 2.0; Item { property int data: test.createAQObjectForOwnershipTest() ? 0 : 1 }", QUrl()); + c.setData("import QtQuick 2.0; Item { required property QtObject test; property int data: test.createAQObjectForOwnershipTest() ? 0 : 1 }", QUrl()); QVERIFY(c.isReady()); - QObject *o = c.create(); + QObject *o = c.createWithInitialProperties( {{"test", QVariant::fromValue(this)}} ); QVERIFY(o != nullptr); } QTRY_VERIFY(spy.count()); @@ -617,13 +619,13 @@ void tst_qqmlengine::objectOwnership() { QQmlEngine engine; QQmlComponent c(&engine); - engine.rootContext()->setContextProperty("test", ptr); QQmlEngine::setObjectOwnership(ptr, QQmlEngine::JavaScriptOwnership); - c.setData("import QtQuick 2.0; QtObject { property var object: { var i = test; test ? 0 : 1 } }", QUrl()); + c.setData("import QtQuick 2.0; QtObject { required property QtObject test; property var object: { var i = test; test ? 0 : 1 } }", QUrl()); QVERIFY(c.isReady()); - QObject *o = c.create(); + QObject *o = c.createWithInitialProperties({{"test", QVariant::fromValue(ptr)}}); QVERIFY(o != nullptr); - engine.rootContext()->setContextProperty("test", nullptr); + QQmlProperty testProp(o, "test"); + testProp.write(QVariant::fromValue<QObject*>(nullptr)); } QTRY_VERIFY(spy.count()); } @@ -773,6 +775,7 @@ public: }; Q_DECLARE_METATYPE(QList<QQmlAbstractUrlInterceptor::DataType>); + void tst_qqmlengine::urlInterceptor_data() { QTest::addColumn<QUrl>("testFile"); @@ -941,14 +944,15 @@ void tst_qqmlengine::cppSignalAndEval() { ObjectCaller objectCaller; QQmlEngine engine; - engine.rootContext()->setContextProperty(QLatin1String("CallerCpp"), &objectCaller); + qmlRegisterSingletonInstance("Test", 1, 0, "CallerCpp", &objectCaller); QQmlComponent c(&engine); c.setData("import QtQuick 2.9\n" + "import Test 1.0\n" "Item {\n" " property var r: 0\n" " Connections {\n" " target: CallerCpp;\n" - " onDoubleReply: {\n" + " function onDoubleReply() {\n" " eval('var z = 1');\n" " r = a;\n" " }\n" @@ -993,6 +997,11 @@ public: SomeQObjectClass() : QObject(nullptr){} }; +class Dayfly : public QObject +{ + Q_OBJECT +}; + void tst_qqmlengine::singletonInstance() { QQmlEngine engine; @@ -1111,7 +1120,7 @@ void tst_qqmlengine::singletonInstance() { // deleted object - auto dayfly = new QObject{}; + auto dayfly = new Dayfly{}; 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."); @@ -1172,6 +1181,38 @@ void tst_qqmlengine::createComponentOnSingletonDestruction() QVERIFY(obj); } +void tst_qqmlengine::uiLanguage() +{ + QQmlEngine engine; + + QObject::connect(&engine, &QJSEngine::uiLanguageChanged, [&engine]() { + engine.retranslate(); + }); + + QSignalSpy uiLanguageChangeSpy(&engine, SIGNAL(uiLanguageChanged())); + + QQmlComponent component(&engine, testFileUrl("uiLanguage.qml")); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QVERIFY(engine.uiLanguage().isEmpty()); + QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 1); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1()); + engine.setUiLanguage("TestLanguage"); + QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 2); + QCOMPARE(object->property("chosenLanguage").toString(), "TestLanguage"); + + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, (component.url().toString() + ":2:1: QML QtObject: Binding loop detected for property \"textToTranslate\"").toLatin1()); + engine.evaluate("Qt.uiLanguage = \"anotherLanguage\""); + QCOMPARE(engine.uiLanguage(), QString("anotherLanguage")); + QCOMPARE(object->property("numberOfTranslationBindingEvaluations").toInt(), 3); + QCOMPARE(object->property("chosenLanguage").toString(), "anotherLanguage"); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" diff --git a/tests/auto/qml/qqmlenginecleanup/data/MyItem.qml b/tests/auto/qml/qqmlenginecleanup/data/MyItem.qml new file mode 100644 index 0000000000..4bb8dfb486 --- /dev/null +++ b/tests/auto/qml/qqmlenginecleanup/data/MyItem.qml @@ -0,0 +1,2 @@ +import QtQuick 2.12 +Item {} diff --git a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp index 690db30838..846ac842db 100644 --- a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp +++ b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp @@ -45,6 +45,7 @@ private slots: void test_qmlClearTypeRegistrations(); void test_valueTypeProviderModule(); // QTBUG-43004 void test_customModuleCleanup(); + void test_qmlListCleared(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -186,6 +187,18 @@ void tst_qqmlenginecleanup::test_customModuleCleanup() } } +void tst_qqmlenginecleanup::test_qmlListCleared() +{ + { + QQmlEngine engine; + auto url = testFileUrl("MyItem.qml"); + QQmlComponent comp(&engine, url); + QScopedPointer<QObject> item {comp.create()}; + QCOMPARE(QQmlMetaType::qmlRegisteredListTypeCount(), 1); + } + QCOMPARE(QQmlMetaType::qmlRegisteredListTypeCount(), 0); +} + QTEST_MAIN(tst_qqmlenginecleanup) #include "tst_qqmlenginecleanup.moc" diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index ca1e52ad2c..9c865b3f73 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -32,6 +32,7 @@ #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> #include <private/qqmlimport_p.h> +#include <private/qqmlengine_p.h> #include "../../shared/util.h" class tst_QQmlImport : public QQmlDataTest @@ -46,6 +47,7 @@ private slots: void completeQmldirPaths(); void interceptQmldir(); void singletonVersionResolution(); + void removeDynamicPlugin(); void cleanup(); }; @@ -260,6 +262,25 @@ void tst_QQmlImport::singletonVersionResolution() } } +void tst_QQmlImport::removeDynamicPlugin() +{ + qmlClearTypeRegistrations(); + QQmlEngine engine; + { + // Load something that adds a dynamic plugin + QQmlComponent component(&engine); + component.setData(QByteArray("import QtTest 1.0; TestResult{}"), QUrl()); + QVERIFY(component.isReady()); + } + QQmlImportDatabase *imports = &QQmlEnginePrivate::get(&engine)->importDatabase; + const QStringList &plugins = imports->dynamicPlugins(); + QVERIFY(!plugins.isEmpty()); + for (const QString &plugin : plugins) + QVERIFY(imports->removeDynamicPlugin(plugin)); + QVERIFY(imports->dynamicPlugins().isEmpty()); + qmlClearTypeRegistrations(); +} + QTEST_MAIN(tst_QQmlImport) diff --git a/tests/auto/qml/qqmlincubator/data/requiredProperty.qml b/tests/auto/qml/qqmlincubator/data/requiredProperty.qml new file mode 100644 index 0000000000..9e355dce72 --- /dev/null +++ b/tests/auto/qml/qqmlincubator/data/requiredProperty.qml @@ -0,0 +1,5 @@ +import QtQuick 2.12 + +Item { + required property int requiredProperty +} diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp index 8e25079703..549aae8c2b 100644 --- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp +++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp @@ -70,6 +70,7 @@ private slots: void selfDelete(); void contextDelete(); void garbageCollection(); + void requiredProperties(); private: QQmlIncubationController controller; @@ -148,7 +149,7 @@ void tst_qqmlincubator::objectDeleted() QVERIFY(!SelfRegisteringType::me()); while (SelfRegisteringOuterType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -156,14 +157,14 @@ void tst_qqmlincubator::objectDeleted() QVERIFY(incubator.isLoading()); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } delete SelfRegisteringType::me(); { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -204,7 +205,7 @@ void tst_qqmlincubator::clear() component.create(incubator); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -223,7 +224,7 @@ void tst_qqmlincubator::clear() component.create(incubator); { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -317,7 +318,7 @@ void tst_qqmlincubator::forceCompletion() QVERIFY(incubator.isLoading()); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -376,7 +377,7 @@ void tst_qqmlincubator::setInitialState() MyIncubator incubator(QQmlIncubator::Asynchronous); component.create(incubator); QVERIFY(incubator.isLoading()); - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); QVERIFY(incubator.isReady()); QVERIFY(incubator.object()); @@ -413,7 +414,7 @@ void tst_qqmlincubator::clearDuringCompletion() QVERIFY(!CompletionRegisteringType::me()); while (CompletionRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -452,7 +453,7 @@ void tst_qqmlincubator::objectDeletionAfterInit() component.create(incubator); while (!incubator.obj && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -551,7 +552,7 @@ void tst_qqmlincubator::statusChanged() QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -573,7 +574,7 @@ void tst_qqmlincubator::statusChanged() QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -621,7 +622,7 @@ void tst_qqmlincubator::asynchronousIfNested() QVERIFY(incubator.isLoading()); QVERIFY(!SelfRegisteringType::me()); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -634,7 +635,7 @@ void tst_qqmlincubator::asynchronousIfNested() while (nested.isLoading()) { QVERIFY(incubator.isLoading()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -642,7 +643,7 @@ void tst_qqmlincubator::asynchronousIfNested() QVERIFY(incubator.isLoading()); { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -741,7 +742,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNested() QVERIFY(!SelfRegisteringType::me()); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -778,7 +779,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNested() QVERIFY(incubator1.isLoading()); QVERIFY(incubator2.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -791,14 +792,14 @@ void tst_qqmlincubator::chainedAsynchronousIfNested() QVERIFY(incubator1.isReady()); QVERIFY(incubator2.isLoading()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } QVERIFY(incubator1.isReady()); QVERIFY(incubator2.isReady()); if (incubator.isLoading()) { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -855,7 +856,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QVERIFY(!SelfRegisteringType::me()); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -875,7 +876,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QVERIFY(incubator2.isNull()); QVERIFY(incubator3.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -890,7 +891,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QVERIFY(incubator2.isNull()); QVERIFY(incubator3.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -905,7 +906,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QVERIFY(incubator2.isLoading()); QVERIFY(incubator3.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -920,12 +921,12 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QVERIFY(incubator2.isReady()); QVERIFY(incubator3.isLoading()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -983,7 +984,7 @@ void tst_qqmlincubator::chainedAsynchronousClear() QVERIFY(!SelfRegisteringType::me()); while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -1003,7 +1004,7 @@ void tst_qqmlincubator::chainedAsynchronousClear() QVERIFY(incubator2.isNull()); QVERIFY(incubator3.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -1018,7 +1019,7 @@ void tst_qqmlincubator::chainedAsynchronousClear() QVERIFY(incubator2.isNull()); QVERIFY(incubator3.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -1033,7 +1034,7 @@ void tst_qqmlincubator::chainedAsynchronousClear() QVERIFY(incubator2.isLoading()); QVERIFY(incubator3.isNull()); - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -1077,7 +1078,7 @@ void tst_qqmlincubator::selfDelete() #define DELETE_TEST(status, mode) { \ bool done = false; \ component.create(*(new MyIncubator(&done, status, mode))); \ - bool True = true; \ + std::atomic<bool> True{true}; \ controller.incubateWhile(&True); \ QVERIFY(done == true); \ } @@ -1106,7 +1107,7 @@ void tst_qqmlincubator::selfDelete() QVERIFY(!SelfRegisteringType::me()); while (SelfRegisteringType::me() == nullptr && incubator->isLoading()) { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } @@ -1120,7 +1121,7 @@ void tst_qqmlincubator::selfDelete() delete SelfRegisteringType::me(); { - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); } @@ -1141,7 +1142,7 @@ void tst_qqmlincubator::contextDelete() delete context; { - bool b = false; + std::atomic<bool> b{false}; controller.incubateWhile(&b); } } @@ -1154,7 +1155,7 @@ void tst_qqmlincubator::garbageCollection() engine.collectGarbage(); - bool b = true; + std::atomic<bool> b{true}; controller.incubateWhile(&b); // verify incubation completed (the incubator was not prematurely collected) @@ -1174,6 +1175,44 @@ void tst_qqmlincubator::garbageCollection() QVERIFY(weakIncubatorRef.isNullOrUndefined()); } +void tst_qqmlincubator::requiredProperties() +{ + { + QQmlComponent component(&engine, testFileUrl("requiredProperty.qml")); + QVERIFY(component.isReady()); + // forceCompletion immediately after creating an asynchronous object completes it + QQmlIncubator incubator; + incubator.setInitialProperties({{"requiredProperty", 42}}); + QVERIFY(incubator.isNull()); + component.create(incubator); + QVERIFY(incubator.isLoading()); + + incubator.forceCompletion(); + + QVERIFY(incubator.isReady()); + QVERIFY(incubator.object() != nullptr); + QCOMPARE(incubator.object()->property("requiredProperty").toInt(), 42); + + delete incubator.object(); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperty.qml")); + QVERIFY(component.isReady()); + // forceCompletion immediately after creating an asynchronous object completes it + QQmlIncubator incubator; + QVERIFY(incubator.isNull()); + component.create(incubator); + QVERIFY(incubator.isLoading()); + + incubator.forceCompletion(); + + QVERIFY(incubator.isError()); + auto error = incubator.errors().first(); + QVERIFY(error.description().contains(QLatin1String("Required property requiredProperty was not initialized"))); + QVERIFY(incubator.object() == nullptr); + } +} + QTEST_MAIN(tst_qqmlincubator) #include "tst_qqmlincubator.moc" diff --git a/tests/auto/qml/qqmlinstantiator/data/createAndRemove.qml b/tests/auto/qml/qqmlinstantiator/data/createAndRemove.qml index 5dd322b5f5..40e996ee4a 100644 --- a/tests/auto/qml/qqmlinstantiator/data/createAndRemove.qml +++ b/tests/auto/qml/qqmlinstantiator/data/createAndRemove.qml @@ -1,18 +1,19 @@ import QtQml 2.1 import QtQuick 2.1 +import Test 1.0 Rectangle { Instantiator { objectName: "instantiator1" - model: model1 + model: Model1 delegate: QtObject { property string datum: model.text } } Component.onCompleted: { - model1.add("Delta"); - model1.add("Gamma"); - model1.add("Beta"); - model1.add("Alpha"); + Model1.add("Delta"); + Model1.add("Gamma"); + Model1.add("Beta"); + Model1.add("Alpha"); } } diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp index 9c5e09c77c..84e08c471a 100644 --- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp +++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp @@ -190,9 +190,9 @@ void tst_qqmlinstantiator::intModelChange() void tst_qqmlinstantiator::createAndRemove() { QQmlEngine engine; + QScopedPointer<StringModel> model {new StringModel("model1")}; + qmlRegisterSingletonInstance("Test", 1, 0, "Model1", model.get()); QQmlComponent component(&engine, testFileUrl("createAndRemove.qml")); - StringModel *model = new StringModel("model1"); - engine.rootContext()->setContextProperty("model1", model); QObject *rootObject = component.create(); QVERIFY(rootObject != nullptr); diff --git a/tests/auto/qml/qqmllanguage/data/Action.qml b/tests/auto/qml/qqmllanguage/data/Action.qml new file mode 100644 index 0000000000..4db2bacf6e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/Action.qml @@ -0,0 +1,21 @@ +import QtQuick 2.12 + +QtObject { + id:root + property Item parent + property Item displayComponent: null + + property list<QtObject> children + + readonly property var visibleChildren: { + var visible = []; + var child; + for (var i in children) { + child = children[i]; + if (!child.hasOwnProperty("visible") || child.visible) { + visible.push(child) + } + } + return visible; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml new file mode 100644 index 0000000000..0b297a7779 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml @@ -0,0 +1,9 @@ +import QtQml 2.15 + +QtObject { + property alias i: icInstance.i + component IC : QtObject { + property int i: 42 + } + property QtObject ic: IC {id: icInstance} +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml new file mode 100644 index 0000000000..49a90ab7da --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml @@ -0,0 +1,7 @@ +import QtQml 2.15 + +InlineComponentBase { + component IC : QtObject { + property int i: 13 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml new file mode 100644 index 0000000000..6058e32b2f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "red" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml new file mode 100644 index 0000000000..b5a0efaccf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.15 + +Item { + property color myColor: "red" + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: myColor + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml new file mode 100644 index 0000000000..9a4f1fd272 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +Item { + Item { + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "red" + } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml new file mode 100644 index 0000000000..ddb55e55e6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml @@ -0,0 +1 @@ +InlineComponentProvider {} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml new file mode 100644 index 0000000000..24bf6f771e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +QtObject { + component StyledRectangle: InlineComponentProvider.StyledRectangle { + color: "green" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml b/tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml new file mode 100644 index 0000000000..60d45c2b1e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/NonRequiredBase.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property int i +} diff --git a/tests/auto/qml/qqmllanguage/data/RequiredBase.qml b/tests/auto/qml/qqmllanguage/data/RequiredBase.qml new file mode 100644 index 0000000000..4effdbf1c7 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/RequiredBase.qml @@ -0,0 +1,3 @@ +NonRequiredBase { + required i +} diff --git a/tests/auto/qml/qqmllanguage/data/SelfInstantiation.errors.txt b/tests/auto/qml/qqmllanguage/data/SelfInstantiation.errors.txt new file mode 100644 index 0000000000..dfd077941e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SelfInstantiation.errors.txt @@ -0,0 +1 @@ +3:29:SelfInstantiation is instantiated recursively diff --git a/tests/auto/qml/qqmllanguage/data/SelfInstantiation.qml b/tests/auto/qml/qqmllanguage/data/SelfInstantiation.qml new file mode 100644 index 0000000000..b2e4e453a0 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SelfInstantiation.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +QtObject { + property QtObject self: SelfInstantiation { + } +} diff --git a/tests/auto/qml/qqmllanguage/data/SelfReference.qml b/tests/auto/qml/qqmllanguage/data/SelfReference.qml new file mode 100644 index 0000000000..129a171d77 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SelfReference.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 +QtObject { + property SelfReference self + signal blah(selfParam: SelfReference) + function returnSelf() : SelfReference { + return this; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/SimpleItem.qml b/tests/auto/qml/qqmllanguage/data/SimpleItem.qml new file mode 100644 index 0000000000..c7bce2bc78 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/SimpleItem.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property int i: 42 +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt deleted file mode 100644 index 90a3ea4317..0000000000 --- a/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt +++ /dev/null @@ -1 +0,0 @@ -10:34:References to other aliases within the same object are not supported at the moment diff --git a/tests/auto/qml/qqmllanguage/data/alias.18.errors.txt b/tests/auto/qml/qqmllanguage/data/alias.18.errors.txt new file mode 100644 index 0000000000..dda3e7a174 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.18.errors.txt @@ -0,0 +1 @@ +7:24:Duplicate alias name diff --git a/tests/auto/qml/qqmllanguage/data/alias.18.qml b/tests/auto/qml/qqmllanguage/data/alias.18.qml new file mode 100644 index 0000000000..a9be937975 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.18.qml @@ -0,0 +1,9 @@ +import QtQml 2.14 + +QtObject { + id: root + property QtObject o1: QtObject { + property alias a: root + property alias a: root + } +} diff --git a/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml b/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml index ee400eb41f..d8e278a7a1 100644 --- a/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml +++ b/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml @@ -3,5 +3,7 @@ import qt.test 1.0 TestItem { property var vector + property var myset positions: vector + barrays: myset } diff --git a/tests/auto/qml/qqmllanguage/data/cppRequiredProperty.qml b/tests/auto/qml/qqmllanguage/data/cppRequiredProperty.qml new file mode 100644 index 0000000000..76673f6409 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppRequiredProperty.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import example.org 1.0 + +MyClass {test: 42} diff --git a/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInChildAndParent.qml b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInChildAndParent.qml new file mode 100644 index 0000000000..d4c059581c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInChildAndParent.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import example.org 1.0 + +Child2 {test: test2; test2: 18} diff --git a/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInChildAndParentNotSet.qml b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInChildAndParentNotSet.qml new file mode 100644 index 0000000000..082e22dc3f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInChildAndParentNotSet.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import example.org 1.0 + +Child2 { test: 13 } diff --git a/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInParent.qml b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInParent.qml new file mode 100644 index 0000000000..6602684542 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInParent.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import example.org 1.0 + +Child {test: 42} diff --git a/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInParentNotSet.qml b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInParentNotSet.qml new file mode 100644 index 0000000000..5971b0c263 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyInParentNotSet.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import example.org 1.0 + +Child {} diff --git a/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyNotSet.qml b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyNotSet.qml new file mode 100644 index 0000000000..dab48a3d71 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppRequiredPropertyNotSet.qml @@ -0,0 +1,4 @@ +import QtQuick 2.15 +import example.org 1.0 + +MyClass {} diff --git a/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml new file mode 100644 index 0000000000..3b37c29b18 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.2.qml @@ -0,0 +1,5 @@ +import StaticTest 1.0 + +MyStaticSecondNamespacedType { + list: [ MyStaticNamespacedType {} ] +} diff --git a/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml new file mode 100644 index 0000000000..2778baadb9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/cppstaticnamespace.qml @@ -0,0 +1,6 @@ +import StaticTest 1.0 + +MyStaticNamespacedType { + myEnum: MyStaticNamespace.Key5 + property int intProperty: MyStaticNamespace.MyOtherNSEnum.OtherKey2 +} diff --git a/tests/auto/qml/qqmllanguage/data/foreignExtended.qml b/tests/auto/qml/qqmllanguage/data/foreignExtended.qml new file mode 100644 index 0000000000..182d60fd02 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/foreignExtended.qml @@ -0,0 +1,20 @@ +import QtQml 2.12 +import Test 1.0 + +QtObject { + property Foreign foreign: Foreign { + objectName: "foreign" + } + property Extended extended: Extended {} + property ForeignExtended foreignExtended: ForeignExtended { + objectName: "foreignExtended" + } + + property int extendedBase: extended.base + + property int extendedExtension: extended.extension + property int foreignExtendedExtension: foreignExtended.extension + + property string foreignObjectName: foreign.objectName + property string foreignExtendedObjectName: foreignExtended.objectName +} diff --git a/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml b/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml new file mode 100644 index 0000000000..c5aa4cfdf5 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component A : Item { + property var test: B {} + } + component B: A {} + A {} +} diff --git a/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml b/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml new file mode 100644 index 0000000000..69e74f7c96 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml @@ -0,0 +1,6 @@ +import QtQuick 2.15 + +Item { + component A : B {} + component B: A {} +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentDuplicateName.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentDuplicateName.qml new file mode 100644 index 0000000000..12cc79cf14 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentDuplicateName.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 +Item +{ + component IC: Item {} + component IC: Item {} +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentFoundBeforeOtherImports.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentFoundBeforeOtherImports.qml new file mode 100644 index 0000000000..85903727fb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentFoundBeforeOtherImports.qml @@ -0,0 +1,9 @@ +import QtQuick 2.12 + +Item +{ + component Rectangle: Item { + Component.onCompleted: console.info("Created") + } + Rectangle {} +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml new file mode 100644 index 0000000000..a96f68e56a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml @@ -0,0 +1,20 @@ +import QtQuick 2.15 + +Item { + width: 600 + height: 480 + IC2 { + objectName: "icInstance" + anchors.centerIn: parent + } + + component IC2: IC1 {} + component IC0: Rectangle { + height: 200 + width: 200 + color: "blue" + } + component IC1: IC0 {} + + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml new file mode 100644 index 0000000000..8968a20112 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml @@ -0,0 +1,13 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property InlineComponentProvider.StyledRectangle myProp: InlineComponentProvider.StyledRectangle {} + InlineComponentProvider.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + color: "blue" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml new file mode 100644 index 0000000000..dc6e3850db --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property InlineComponentProvider.StyledRectangle myProp: InlineComponentReexporter.StyledRectangle { + objectName: "icInstance" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml new file mode 100644 index 0000000000..c57c4cad01 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml @@ -0,0 +1,16 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "blue" + } + StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml new file mode 100644 index 0000000000..9f3903c8df --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml @@ -0,0 +1,12 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property color myColor: "blue" + InlineComponentProvider2.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml new file mode 100644 index 0000000000..150d0c2ded --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +Item { + width: 600 + height: 480 + property var test: InlineComponentProvider3.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentWithAlias.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentWithAlias.qml new file mode 100644 index 0000000000..ab125e9323 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentWithAlias.qml @@ -0,0 +1,17 @@ +import QtQuick 2.15 + +Item { + id: root + component IC: SimpleItem { + width: i + Rectangle { + id: rect + color: "lime" + } + property alias color: rect.color + } + width: 200 + IC { + objectName: "icInstance" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentWithId.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentWithId.qml new file mode 100644 index 0000000000..c4093bad2f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentWithId.qml @@ -0,0 +1,14 @@ +import QtQuick 2.15 + +Item { + id: root + component IC: SimpleItem { + id: root + width: root.i + property color color: "red" + } + width: 200 + IC { + objectName: "icInstance" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt index 3b90f573a2..41cb0eaac1 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt @@ -1 +1 @@ -3:1:Bar.Item - Bar is not a namespace +3:1:Bar.Item - Bar is neither a type nor a namespace diff --git a/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt index 3b90f573a2..41cb0eaac1 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt @@ -1 +1 @@ -3:1:Bar.Item - Bar is not a namespace +3:1:Bar.Item - Bar is neither a type nor a namespace diff --git a/tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml b/tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml new file mode 100644 index 0000000000..efd273ddc6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml @@ -0,0 +1,36 @@ +import QtQuick 2.12 + +Item { + width: 1024 + height: 800 + + property Component a: Component { + id: a + Item { + property list<QtObject> myList: [ + QtObject { + property bool enabled: true + } + ] + } + } + Component { + id: b + Item { + property list<QtObject> myList + + function test() { + for (var i = 0; i < myList.length; ++i) + console.log(i, "==", myList[i].enabled) + } + } + } + property Item instance + function doAssign(o) { + instance = b.createObject(null, {myList: o.myList}) + } + function use() { + instance.test() + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/listPropertiesChild.qml b/tests/auto/qml/qqmllanguage/data/listPropertiesChild.qml new file mode 100644 index 0000000000..b1635a9409 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/listPropertiesChild.qml @@ -0,0 +1,7 @@ +import QtQuick 2.12 + +Action +{ + id: action + property color color +} diff --git a/tests/auto/qml/qqmllanguage/data/nestedIC.qml b/tests/auto/qml/qqmllanguage/data/nestedIC.qml new file mode 100644 index 0000000000..04cef64d54 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nestedIC.qml @@ -0,0 +1,7 @@ +import QtQuick 2.15 + +Item { + component Outer : Item { + component Inner : Item {} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml new file mode 100644 index 0000000000..a07a6a9838 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property InlineComponentProvider.NonExisting myProp +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml new file mode 100644 index 0000000000..5c24962def --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + InlineComponentProvider.NotExisting {} +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml new file mode 100644 index 0000000000..34595707fc --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml @@ -0,0 +1,3 @@ +import QtQuick 2.15 + +InlineComponentProvider.NotExisting {} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml new file mode 100644 index 0000000000..2be01ccd96 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "singleton" as MySingleton + +Item { + property MySingleton.SingletonTypeWithIC.NonExisting singletonIC +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml new file mode 100644 index 0000000000..a2ca5db6de --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property InlineComponentProviderChild.StyledRectangle myProp +} diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.errors.txt b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.errors.txt new file mode 100644 index 0000000000..d4f3eb3ecf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.errors.txt @@ -0,0 +1 @@ +6:29:Left-hand side may not contain || or && diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml new file mode 100644 index 0000000000..ce6aa9a532 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_And.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Component { + Component.onCompleted: { + // Should cause an error since having either || or && on any side of the coalescing operator is banned by the specification + var bad_lhs_and = 3 && 4 ?? 0; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.errors.txt b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.errors.txt new file mode 100644 index 0000000000..f2f1719acd --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.errors.txt @@ -0,0 +1 @@ +6:28:Left-hand side may not contain || or && diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml new file mode 100644 index 0000000000..8864524d0d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_LHS_Or.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Component { + Component.onCompleted: { + // Should cause an error since having either || or && on any side of the coalescing operator is banned by the specification + var bad_lhs_or = 3 || 4 ?? 0; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.errors.txt b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.errors.txt new file mode 100644 index 0000000000..0ff61be687 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.errors.txt @@ -0,0 +1 @@ +6:34:Right-hand side may not contain || or && diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml new file mode 100644 index 0000000000..97898f055a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_And.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Component { + Component.onCompleted: { + // Should cause an error since having either || or && on any side of the coalescing operator is banned by the specification + var bad_rhs_and = 0 ?? 3 && 4; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.errors.txt b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.errors.txt new file mode 100644 index 0000000000..4b2784ac0a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.errors.txt @@ -0,0 +1 @@ +6:33:Right-hand side may not contain || or && diff --git a/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml new file mode 100644 index 0000000000..f172755e91 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nullishCoalescing_RHS_Or.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Component { + Component.onCompleted: { + // Should cause an error since having either || or && on any side of the coalescing operator is banned by the specification + var bad_rhs_or = 0 ?? 3 || 4; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.1.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.1.qml new file mode 100644 index 0000000000..dac43c6d88 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.1.qml @@ -0,0 +1,8 @@ +import QtQuick 2.13 +Item { + property var required: 32 // required is still allowed as an identifier for properties + function f(required) { // for javascript + required = required + required; + console.log(required); + } +} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml new file mode 100644 index 0000000000..4c12c7b602 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.2.qml @@ -0,0 +1,4 @@ +import QtQuick 2.13 +Item { + required property int test: 42 // cannot specify value for required property +} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml new file mode 100644 index 0000000000..2585cf361e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.3.qml @@ -0,0 +1,6 @@ +import QtQuick 2.15 + +Item { + property int i; + required i; +} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml new file mode 100644 index 0000000000..1126f845c9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.4.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + required objectName +} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml new file mode 100644 index 0000000000..c2d155b123 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.5.qml @@ -0,0 +1 @@ +RequiredBase {} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml new file mode 100644 index 0000000000..e8802aef20 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.6.qml @@ -0,0 +1,3 @@ +RequiredBase { + i: 42 +} diff --git a/tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml b/tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml new file mode 100644 index 0000000000..40987f5c56 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/requiredProperties.7.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + required blub +} diff --git a/tests/auto/qml/qqmllanguage/data/selfreferencingsingletonmodule/TestSingleton.qml b/tests/auto/qml/qqmllanguage/data/selfreferencingsingletonmodule/TestSingleton.qml new file mode 100644 index 0000000000..f70a3b1fea --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/selfreferencingsingletonmodule/TestSingleton.qml @@ -0,0 +1,7 @@ +import QtQml 2.0 +import selfreferencingsingletonmodule 1.0 +pragma Singleton +QtObject { + property SelfReferencingSingleton self + property int dummy: 42 +} diff --git a/tests/auto/qml/qqmllanguage/data/selfreferencingsingletonmodule/qmldir b/tests/auto/qml/qqmllanguage/data/selfreferencingsingletonmodule/qmldir new file mode 100644 index 0000000000..617861b00b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/selfreferencingsingletonmodule/qmldir @@ -0,0 +1 @@ +singleton SelfReferencingSingleton 1.0 TestSingleton.qml diff --git a/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml b/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml new file mode 100644 index 0000000000..bd724c4aeb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 +pragma Singleton + +Item { + id: singletonId + component IC1: Item { + property int iProp: 42 + property string sProp: "Hello, world" + property Rectangle myRect: Rectangle {color: "green"} + } + component IC2: Item { + property int iProp: 13 + property string sProp: "Goodbye, world" + property Rectangle myRect: Rectangle {color: "red"} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/qmldir b/tests/auto/qml/qqmllanguage/data/singleton/qmldir index 533fb6999a..727b09b4e8 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/qmldir +++ b/tests/auto/qml/qqmllanguage/data/singleton/qmldir @@ -1,3 +1,4 @@ singleton SingletonType SingletonType.qml +singleton SingletonTypeWithIC SingletonTypeWithIC.qml diff --git a/tests/auto/qml/qqmllanguage/data/singletonICTest.qml b/tests/auto/qml/qqmllanguage/data/singletonICTest.qml new file mode 100644 index 0000000000..d0d3b079be --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singletonICTest.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "singleton" as MySingleton + +Item { + property MySingleton.SingletonTypeWithIC.IC1 singleton1: MySingleton.SingletonTypeWithIC.IC1 {}; +} diff --git a/tests/auto/qml/qqmllanguage/data/unsupportedProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/unsupportedProperty.errors.txt index 3cd626de86..b45c320c20 100644 --- a/tests/auto/qml/qqmllanguage/data/unsupportedProperty.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/unsupportedProperty.errors.txt @@ -1 +1 @@ -3:13:Invalid property assignment: unsupported type "QMatrix" +3:16:Invalid property assignment: unsupported type "QTransform" diff --git a/tests/auto/qml/qqmllanguage/data/unsupportedProperty.qml b/tests/auto/qml/qqmllanguage/data/unsupportedProperty.qml index 9f19680368..ec8171dd2c 100644 --- a/tests/auto/qml/qqmllanguage/data/unsupportedProperty.qml +++ b/tests/auto/qml/qqmllanguage/data/unsupportedProperty.qml @@ -1,4 +1,4 @@ import Test 1.0 MyQmlObject { - matrix: "1,0,0,0,1,0,0,0,1" + transform: "1,0,0,0,1,0,0,0,1" } diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.pro b/tests/auto/qml/qqmllanguage/qqmllanguage.pro index 724a27320c..6c54525544 100644 --- a/tests/auto/qml/qqmllanguage/qqmllanguage.pro +++ b/tests/auto/qml/qqmllanguage/qqmllanguage.pro @@ -1,4 +1,7 @@ -CONFIG += testcase +CONFIG += testcase qmltypes +QML_IMPORT_NAME = StaticTest +QML_IMPORT_VERSION = 1.0 + TARGET = tst_qqmllanguage macx:CONFIG -= app_bundle diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 6956533196..31a4135d89 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -57,7 +57,7 @@ void registerTypes() qmlRegisterType<MyNamespace::MySecondNamespacedType>("Test",1,0,"MySecondNamespacedType"); qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "Test", 1, 0, "MyNamespace", "Access to enums & flags only"); qmlRegisterType<MyParserStatus>("Test",1,0,"MyParserStatus"); - qmlRegisterType<MyGroupedObject>(); + qmlRegisterAnonymousType<MyGroupedObject>("Test", 1); qmlRegisterType<MyRevisionedClass>("Test",1,0,"MyRevisionedClass"); qmlRegisterType<MyRevisionedClass,1>("Test",1,1,"MyRevisionedClass"); qmlRegisterType<MyRevisionedIllegalOverload>("Test",1,0,"MyRevisionedIllegalOverload"); @@ -118,6 +118,10 @@ void registerTypes() qmlRegisterType<LazyDeferredSubObject>("Test", 1, 0, "LazyDeferredSubObject"); qmlRegisterType<DeferredProperties>("Test", 1, 0, "DeferredProperties"); + + qmlRegisterTypesAndRevisions<Extended, Foreign, ForeignExtended>("Test", 1); + qmlRegisterTypesAndRevisions<BareSingleton>("Test", 1); + qmlRegisterTypesAndRevisions<UncreatableSingleton>("Test", 1); } QVariant myCustomVariantTypeConverter(const QString &data) @@ -210,3 +214,9 @@ bool MyQmlObject::event(QEvent *event) m_childAddedEventCount++; return QObject::event(event); } + +UncreatableSingleton *UncreatableSingleton::instance() +{ + static UncreatableSingleton instance; + return &instance; +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 1aab24841a..8852bf7af9 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -31,7 +31,7 @@ #include <QtCore/qobject.h> #include <QtCore/qrect.h> #include <QtCore/qdatetime.h> -#include <QtGui/qmatrix.h> +#include <QtGui/qtransform.h> #include <QtGui/qcolor.h> #include <QtGui/qvector2d.h> #include <QtGui/qvector3d.h> @@ -104,7 +104,7 @@ class MyQmlObject : public QObject, public MyInterface Q_PROPERTY(QString readOnlyString READ readOnlyString) Q_PROPERTY(bool enabled READ enabled WRITE setEnabled) Q_PROPERTY(QRect rect READ rect WRITE setRect) - Q_PROPERTY(QMatrix matrix READ matrix WRITE setMatrix) //assumed to be unsupported by QML + Q_PROPERTY(QTransform transform READ transform WRITE setTransform) //assumed to be unsupported by QML Q_PROPERTY(MyInterface *interfaceProperty READ interface WRITE setInterface) Q_PROPERTY(int onLiteralSignal READ onLiteralSignal WRITE setOnLiteralSignal) Q_PROPERTY(MyCustomVariantType customType READ customType WRITE setCustomType) @@ -129,8 +129,8 @@ public: QRect rect() const { return QRect(); } void setRect(const QRect&) {} - QMatrix matrix() const { return QMatrix(); } - void setMatrix(const QMatrix&) {} + QTransform transform() const { return QTransform(); } + void setTransform(const QTransform &) {} MyInterface *interface() const { return m_interface; } void setInterface(MyInterface *iface) { m_interface = iface; } @@ -750,6 +750,47 @@ private: bool m_ownRWObj; }; +namespace MyStaticNamespace { + Q_NAMESPACE + QML_ELEMENT + + enum MyNSEnum { + Key1 = 1, + Key2, + Key5 = 5 + }; + Q_ENUM_NS(MyNSEnum); + + enum class MyOtherNSEnum { + OtherKey1 = 1, + OtherKey2 + }; + Q_ENUM_NS(MyOtherNSEnum); + + + class MyNamespacedType : public QObject + { + Q_OBJECT + Q_PROPERTY(MyStaticNamespace::MyNSEnum myEnum MEMBER m_myEnum) + QML_NAMED_ELEMENT(MyStaticNamespacedType) + MyStaticNamespace::MyNSEnum m_myEnum = MyNSEnum::Key1; + }; + + class MySecondNamespacedType : public QObject + { + Q_OBJECT + Q_PROPERTY(QQmlListProperty<MyStaticNamespace::MyNamespacedType> list READ list) + QML_NAMED_ELEMENT(MyStaticSecondNamespacedType) + public: + QQmlListProperty<MyNamespacedType> list() + { + return QQmlListProperty<MyNamespacedType>(this, &m_list); + } + + private: + QList<MyNamespacedType *> m_list; + }; +} namespace MyNamespace { Q_NAMESPACE @@ -1420,6 +1461,71 @@ public: enum class OtherScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 }; }; +class Extension : public QObject +{ + Q_OBJECT + Q_PROPERTY(int extension READ extension CONSTANT) +public: + Extension(QObject *parent = nullptr) : QObject(parent) {} + int extension() const { return 42; } +}; + +class Extended : public QObject +{ + Q_OBJECT + QML_EXTENDED(Extension) + QML_NAMED_ELEMENT(Extended) + Q_PROPERTY(int base READ base CONSTANT) + +public: + int base() const { return 43; } +}; + +class Local : public QObject +{ + Q_OBJECT +}; + +class Foreign +{ + Q_GADGET + QML_FOREIGN(Local) + QML_NAMED_ELEMENT(Foreign) +}; + +class ForeignExtended +{ + Q_GADGET + QML_FOREIGN(Local) + QML_NAMED_ELEMENT(ForeignExtended) + QML_EXTENDED(Extension) +}; + +class BareSingleton : public QObject +{ + Q_OBJECT + QML_SINGLETON + QML_ELEMENT + +public: + BareSingleton(QObject *parent = nullptr) : QObject(parent) + { + setObjectName("statically registered"); + } +}; + +class UncreatableSingleton : public QObject +{ + Q_OBJECT + QML_SINGLETON + QML_ELEMENT + +public: + static UncreatableSingleton *instance(); + +private: + UncreatableSingleton() { setObjectName("uncreatable"); } +}; void registerTypes(); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 4a8ce77f92..5665775258 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -123,6 +123,7 @@ private slots: void dynamicProperties(); void dynamicPropertiesNested(); void listProperties(); + void listPropertiesInheritanceNoCrash(); void badListItemType(); void dynamicObjectProperties(); void dynamicSignalsAndSlots(); @@ -132,6 +133,9 @@ private slots: void autoComponentCreation(); void autoComponentCreationInGroupProperty(); void propertyValueSource(); + void requiredProperty(); + void requiredPropertyFromCpp_data(); + void requiredPropertyFromCpp(); void attachedProperties(); void dynamicObjects(); void customVariantTypes(); @@ -303,7 +307,28 @@ private slots: void typeWrapperToVariant(); + void extendedForeignTypes(); + + void inlineComponent(); + void inlineComponent_data(); + void inlineComponentReferenceCycle_data(); + void inlineComponentReferenceCycle(); + void nestedInlineComponentNotAllowed(); + void inlineComponentStaticTypeResolution(); + void inlineComponentInSingleton(); + void nonExistingInlineComponent_data(); + void nonExistingInlineComponent(); + void inlineComponentFoundBeforeOtherImports(); + void inlineComponentDuplicateNameError(); + + void selfReference(); + void selfReferencingSingleton(); + + void listContainingDeletedObject(); + void overrideSingleton(); + void arrayToContainer(); + void qualifiedScopeInCustomParser(); private: QQmlEngine engine; @@ -473,6 +498,11 @@ void tst_qqmllanguage::errors_data() QTest::newRow("finalOverride") << "finalOverride.qml" << "finalOverride.errors.txt" << false; QTest::newRow("customParserIdNotAllowed") << "customParserIdNotAllowed.qml" << "customParserIdNotAllowed.errors.txt" << false; + QTest::newRow("nullishCoalescing_LHS_Or") << "nullishCoalescing_LHS_Or.qml" << "nullishCoalescing_LHS_Or.errors.txt" << false; + QTest::newRow("nullishCoalescing_LHS_And") << "nullishCoalescing_LHS_And.qml" << "nullishCoalescing_LHS_And.errors.txt" << false; + QTest::newRow("nullishCoalescing_RHS_Or") << "nullishCoalescing_RHS_Or.qml" << "nullishCoalescing_RHS_Or.errors.txt" << false; + QTest::newRow("nullishCoalescing_RHS_And") << "nullishCoalescing_RHS_And.qml" << "nullishCoalescing_RHS_And.errors.txt" << false; + QTest::newRow("invalidGroupedProperty.1") << "invalidGroupedProperty.1.qml" << "invalidGroupedProperty.1.errors.txt" << false; QTest::newRow("invalidGroupedProperty.2") << "invalidGroupedProperty.2.qml" << "invalidGroupedProperty.2.errors.txt" << false; QTest::newRow("invalidGroupedProperty.3") << "invalidGroupedProperty.3.qml" << "invalidGroupedProperty.3.errors.txt" << false; @@ -630,6 +660,8 @@ void tst_qqmllanguage::errors_data() QTest::newRow("typeAnnotations.2") << "typeAnnotations.2.qml" << "typeAnnotations.2.errors.txt" << false; QTest::newRow("propertyUnknownType") << "propertyUnknownType.qml" << "propertyUnknownType.errors.txt" << false; + + QTest::newRow("selfInstantiation") << "SelfInstantiation.qml" << "SelfInstantiation.errors.txt" << false; } void tst_qqmllanguage::errors() @@ -1462,6 +1494,16 @@ void tst_qqmllanguage::listProperties() QCOMPARE(object->property("test").toInt(), 2); } +// Tests that initializing list properties of a base class does not crash +// (QTBUG-82171) +void tst_qqmllanguage::listPropertiesInheritanceNoCrash() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("listPropertiesChild.qml")); + QScopedPointer<QObject> object(component.create()); // should not crash + QVERIFY(object != nullptr); +} + void tst_qqmllanguage::badListItemType() { QQmlComponent component(&engine, testFileUrl("badListItemType.qml")); @@ -1636,6 +1678,120 @@ void tst_qqmllanguage::propertyValueSource() } } +void tst_qqmllanguage::requiredProperty() +{ + QQmlEngine engine; + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.1.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.2.qml")); + QVERIFY(!component.errors().empty()); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.4.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!component.errors().empty()); + QVERIFY(component.errorString().contains("Required property objectName was not initialized")); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.3.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!component.errors().empty()); + QVERIFY(component.errorString().contains("Required property i was not initialized")); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.5.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!component.errors().empty()); + QVERIFY(component.errorString().contains("Required property i was not initialized")); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.6.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object); + } + { + QQmlComponent component(&engine, testFileUrl("requiredProperties.7.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!component.errors().empty()); + QVERIFY(component.errorString().contains("Property blub was marked as required but does not exist")); + } +} + +class MyClassWithRequiredProperty : public QObject +{ +public: + Q_OBJECT + Q_PROPERTY(int test MEMBER m_test REQUIRED NOTIFY testChanged) + Q_SIGNAL void testChanged(); +private: + int m_test; +}; + +class ChildClassWithoutOwnRequired : public MyClassWithRequiredProperty +{ +public: + Q_OBJECT + Q_PROPERTY(int test2 MEMBER m_test2 NOTIFY test2Changed) + Q_SIGNAL void test2Changed(); +private: + int m_test2; +}; + +class ChildClassWithOwnRequired : public MyClassWithRequiredProperty +{ +public: + Q_OBJECT + Q_PROPERTY(int test2 MEMBER m_test2 REQUIRED NOTIFY test2Changed) + Q_SIGNAL void test2Changed(); +private: + int m_test2; +}; + +void tst_qqmllanguage::requiredPropertyFromCpp_data() +{ + qmlRegisterType<MyClassWithRequiredProperty>("example.org", 1, 0, "MyClass"); + qmlRegisterType<ChildClassWithoutOwnRequired>("example.org", 1, 0, "Child"); + qmlRegisterType<ChildClassWithOwnRequired>("example.org", 1, 0, "Child2"); + + + QTest::addColumn<QUrl>("setFile"); + QTest::addColumn<QUrl>("notSetFile"); + QTest::addColumn<QString>("errorMessage"); + QTest::addColumn<int>("expectedValue"); + + QTest::addRow("direct") << testFileUrl("cppRequiredProperty.qml") << testFileUrl("cppRequiredPropertyNotSet.qml") << QString(":4 Required property test was not initialized\n") << 42; + QTest::addRow("in parent") << testFileUrl("cppRequiredPropertyInParent.qml") << testFileUrl("cppRequiredPropertyInParentNotSet.qml") << QString(":4 Required property test was not initialized\n") << 42; + QTest::addRow("in child and parent") << testFileUrl("cppRequiredPropertyInChildAndParent.qml") << testFileUrl("cppRequiredPropertyInChildAndParentNotSet.qml") << QString(":4 Required property test2 was not initialized\n") << 18; +} + +void tst_qqmllanguage::requiredPropertyFromCpp() +{ + QQmlEngine engine; + QFETCH(QUrl, setFile); + QFETCH(QUrl, notSetFile); + QFETCH(QString, errorMessage); + QFETCH(int, expectedValue); + { + QQmlComponent comp(&engine, notSetFile); + QScopedPointer<QObject> o { comp.create() }; + QVERIFY(o.isNull()); + QVERIFY(comp.isError()); + QCOMPARE(comp.errorString(), notSetFile.toString() + errorMessage); + } + { + QQmlComponent comp(&engine, setFile); + QScopedPointer<QObject> o { comp.create() }; + QVERIFY(!o.isNull()); + QCOMPARE(o->property("test").toInt(), expectedValue); + } +} + void tst_qqmllanguage::attachedProperties() { QQmlComponent component(&engine, testFileUrl("attachedProperties.qml")); @@ -1704,21 +1860,30 @@ void tst_qqmllanguage::valueTypes() void tst_qqmllanguage::cppnamespace() { - { - QQmlComponent component(&engine, testFileUrl("cppnamespace.qml")); + QScopedPointer<QObject> object; + + auto create = [&](const char *file) { + QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); - QScopedPointer<QObject> object(component.create()); + object.reset(component.create()); QVERIFY(object != nullptr); + }; - QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2); - } + auto createAndCheck = [&](const char *file) { + create(file); + return !QTest::currentTestFailed(); + }; - { - QQmlComponent component(&engine, testFileUrl("cppnamespace.2.qml")); - VERIFY_ERRORS(0); - QScopedPointer<QObject> object(component.create()); - QVERIFY(object != nullptr); - } + QVERIFY(createAndCheck("cppnamespace.qml")); + QCOMPARE(object->property("intProperty").toInt(), + (int)MyNamespace::MyOtherNSEnum::OtherKey2); + + QVERIFY(createAndCheck("cppstaticnamespace.qml")); + QCOMPARE(object->property("intProperty").toInt(), + (int)MyStaticNamespace::MyOtherNSEnum::OtherKey2); + + QVERIFY(createAndCheck("cppnamespace.2.qml")); + QVERIFY(createAndCheck("cppstaticnamespace.2.qml")); } void tst_qqmllanguage::aliasProperties() @@ -1936,7 +2101,6 @@ void tst_qqmllanguage::aliasProperties() // "Nested" aliases within an object that require iterative resolution { - // This is known to fail at the moment. QQmlComponent component(&engine, testFileUrl("alias.14.qml")); VERIFY_ERRORS(0); @@ -2035,6 +2199,11 @@ void tst_qqmllanguage::aliasProperties() auto text = myText->property("text").toString(); QCOMPARE(text, "alias:\n20"); } + + { + QQmlComponent component(&engine, testFileUrl("alias.18.qml")); + VERIFY_ERRORS("alias.18.errors.txt"); + } } // QTBUG-13374 Test that alias properties and signals can coexist @@ -2294,20 +2463,31 @@ void tst_qqmllanguage::scriptStringJs() QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); } +struct FreeUnitData +{ + static void cleanup(const QV4::CompiledData::Unit *readOnlyQmlUnit) + { + if (readOnlyQmlUnit && !(readOnlyQmlUnit->flags & QV4::CompiledData::Unit::StaticData)) + free(const_cast<QV4::CompiledData::Unit *>(readOnlyQmlUnit)); + } +}; + void tst_qqmllanguage::scriptStringWithoutSourceCode() { QUrl url = testFileUrl("scriptString7.qml"); + QScopedPointer<const QV4::CompiledData::Unit, FreeUnitData> readOnlyQmlUnit; { QQmlEnginePrivate *eng = QQmlEnginePrivate::get(&engine); QQmlRefPointer<QQmlTypeData> td = eng->typeLoader.getType(url); Q_ASSERT(td); - const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->unitData(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); + readOnlyQmlUnit.reset(compilationUnit->unitData()); Q_ASSERT(readOnlyQmlUnit); QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); - memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); + memcpy(qmlUnit, readOnlyQmlUnit.data(), readOnlyQmlUnit->unitSize); + qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit(); compilationUnit->setUnitData(qmlUnit); const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); @@ -2743,12 +2923,12 @@ void tst_qqmllanguage::importsLocal_data() QTest::newRow("local import QTBUG-7721 A") << "subdir.Test {}" // no longer allowed (QTBUG-7721) << "" - << "subdir.Test - subdir is not a namespace"; + << "subdir.Test - subdir is neither a type nor a namespace"; QTest::newRow("local import QTBUG-7721 B") << "import \"subdir\" as X\n" "X.subsubdir.SubTest {}" // no longer allowed (QTBUG-7721) << "" - << "X.subsubdir.SubTest - nested namespaces not allowed"; + << "X.subsubdir.SubTest - subsubdir is not a type"; QTest::newRow("local import as") << "import \"subdir\" as T\n" "T.Test {}" @@ -4709,11 +4889,13 @@ static void beginDeferredOnce(QQmlEnginePrivate *enginePriv, typedef QMultiHash<int, const QV4::CompiledData::Binding *> QV4PropertyBindingHash; auto it = std::reverse_iterator<QV4PropertyBindingHash::iterator>(range.second); auto last = std::reverse_iterator<QV4PropertyBindingHash::iterator>(range.first); + state->creator->beginPopulateDeferred(deferData->context); while (it != last) { - if (!state->creator->populateDeferredBinding(property, deferData, *it)) - state->errors << state->creator->errors; + state->creator->populateDeferredBinding(property, deferData->deferredIdx, *it); ++it; } + state->creator->finalizePopulateDeferred(); + state->errors << state->creator->errors; deferredState->constructionStates += state; @@ -4938,24 +5120,24 @@ void tst_qqmllanguage::instanceof_data() // assert that basic types don't convert to QObject QTest::newRow("1 instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") - << QVariant("TypeError: Type error"); + << QVariant(false); QTest::newRow("true instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") - << QVariant("TypeError: Type error"); + << QVariant(false); QTest::newRow("\"foobar\" instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") - << QVariant("TypeError: Type error"); + << QVariant(false); // assert that Managed don't either QTest::newRow("new String(\"foobar\") instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") - << QVariant("TypeError: Type error"); + << QVariant(false); QTest::newRow("new Object() instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") - << QVariant("TypeError: Type error"); + << QVariant(false); QTest::newRow("new Date() instanceof QtObject") << testFileUrl("instanceof_qtqml.qml") - << QVariant("TypeError: Type error"); + << QVariant(false); // test that simple QtQml comparisons work QTest::newRow("qtobjectInstance instanceof QtObject") @@ -5240,29 +5422,341 @@ void tst_qqmllanguage::typeWrapperToVariant() QVERIFY(target); } +void tst_qqmllanguage::extendedForeignTypes() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("foreignExtended.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->property("extendedBase").toInt(), 43); + QCOMPARE(o->property("extendedExtension").toInt(), 42); + QCOMPARE(o->property("foreignExtendedExtension").toInt(), 42); + QCOMPARE(o->property("foreignObjectName").toString(), QLatin1String("foreign")); + QCOMPARE(o->property("foreignExtendedObjectName").toString(), QLatin1String("foreignExtended")); +} + +void tst_qqmllanguage::selfReference() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("SelfReference.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component); + auto compilationUnit = componentPrivate->compilationUnit; + QVERIFY(compilationUnit); + + const QMetaObject *metaObject = o->metaObject(); + QMetaProperty selfProperty = metaObject->property(metaObject->indexOfProperty("self")); + QCOMPARE(selfProperty.userType(), compilationUnit->metaTypeId); + + QByteArray typeName = selfProperty.typeName(); + QVERIFY(typeName.endsWith('*')); + typeName = typeName.chopped(1); + QCOMPARE(typeName, metaObject->className()); + + QMetaMethod selfFunction = metaObject->method(metaObject->indexOfMethod("returnSelf()")); + QVERIFY(selfFunction.isValid()); + QCOMPARE(selfFunction.returnType(), compilationUnit->metaTypeId); + + QMetaMethod selfSignal; + + for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) { + QMetaMethod method = metaObject->method(i); + if (method.isValid() && method.name().startsWith("blah")) { + selfSignal = method; + break; + } + } + + QVERIFY(selfSignal.isValid()); + QCOMPARE(selfSignal.parameterCount(), 1); + QCOMPARE(selfSignal.parameterType(0), compilationUnit->metaTypeId); +} + +void tst_qqmllanguage::selfReferencingSingleton() +{ + QQmlEngine engine; + engine.addImportPath(dataDirectory()); + + QPointer<QObject> singletonPointer; + { + QQmlComponent component(&engine); + component.setData(QByteArray(R"(import QtQml 2.0 + import selfreferencingsingletonmodule 1.0 + QtObject { + property SelfReferencingSingleton singletonPointer: SelfReferencingSingleton + })"), QUrl()); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + singletonPointer = o->property("singletonPointer").value<QObject*>(); + } + + QVERIFY(!singletonPointer.isNull()); + QCOMPARE(singletonPointer->property("dummy").toInt(), 42); +} + +void tst_qqmllanguage::listContainingDeletedObject() +{ + QQmlEngine engine; + auto url = testFileUrl("listContainingDeleted.qml"); + const QString message = url.toString() + ":24: TypeError: Cannot read property 'enabled' of null"; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, message.toUtf8().data()); + QQmlComponent comp(&engine, url); + QScopedPointer<QObject> root(comp.create()); + QVERIFY(root); + + auto cmp = root->property("a").value<QQmlComponent*>(); + auto o = cmp->create(); + + QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o))); + delete o; + QMetaObject::invokeMethod(root.get(), "use"); + +} + +void tst_qqmllanguage::overrideSingleton() +{ + auto check = [](const QString &name, const QByteArray &singletonElement) { + const QByteArray testQml = "import Test 1.0\n" + "import QtQml 2.0\n" + "QtObject { objectName: " + singletonElement + ".objectName }"; + QQmlEngine engine; + QQmlComponent component(&engine, nullptr); + component.setData(testQml, QUrl("singleton.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QCOMPARE(obj->objectName(), name); + }; + + check("statically registered", "BareSingleton"); + + BareSingleton singleton; + singleton.setObjectName("dynamically registered"); + qmlRegisterSingletonInstance("Test", 1, 0, "BareSingleton", &singleton); + + check("dynamically registered", "BareSingleton"); + + QTest::ignoreMessage( + QtWarningMsg, + "singleton.qml:3: TypeError: Cannot read property 'objectName' of undefined"); + check("", "UncreatableSingleton"); + + qmlRegisterSingletonInstance("Test", 1, 0, "UncreatableSingleton", + UncreatableSingleton::instance()); + check("uncreatable", "UncreatableSingleton"); +} + +void tst_qqmllanguage::inlineComponent() +{ + QFETCH(QUrl, componentUrl); + QFETCH(QColor, color); + QFETCH(int, width); + QQmlEngine engine; + QQmlComponent component(&engine, componentUrl); + QScopedPointer<QObject> o(component.create()); + if (component.isError()) { + qDebug() << component.errorString(); + } + QVERIFY(!o.isNull()); + auto icInstance = o->findChild<QObject *>("icInstance"); + QVERIFY(icInstance); + QCOMPARE(icInstance->property("color").value<QColor>(),color); + QCOMPARE(icInstance->property("width").value<qreal>(), width); +} + +void tst_qqmllanguage::inlineComponent_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QColor>("color"); + QTest::addColumn<int>("width"); + + QTest::newRow("Usage from other component") << testFileUrl("inlineComponentUser1.qml") << QColorConstants::Blue << 24; + QTest::newRow("Reexport") << testFileUrl("inlineComponentUser2.qml") << QColorConstants::Svg::green << 24; + QTest::newRow("Usage in same component") << testFileUrl("inlineComponentUser3.qml") << QColorConstants::Blue << 24; + + QTest::newRow("Resolution happens at instantiation") << testFileUrl("inlineComponentUser4.qml") << QColorConstants::Blue << 24; + QTest::newRow("Non-toplevel IC is found") << testFileUrl("inlineComponentUser5.qml") << QColorConstants::Svg::red << 24; + + QTest::newRow("Resolved in correct order") << testFileUrl("inlineComponentOrder.qml") << QColorConstants::Blue << 200; + + QTest::newRow("ID resolves correctly") << testFileUrl("inlineComponentWithId.qml") << QColorConstants::Svg::red << 42; + QTest::newRow("Alias resolves correctly") << testFileUrl("inlineComponentWithAlias.qml") << QColorConstants::Svg::lime << 42; +} + +void tst_qqmllanguage::inlineComponentReferenceCycle_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + + QTest::newRow("Simple cycle") << testFileUrl("icSimpleCycle.qml"); + QTest::newRow("Via property") << testFileUrl("icCycleViaProperty.qml"); +} + +void tst_qqmllanguage::inlineComponentReferenceCycle() +{ + QFETCH(QUrl, componentUrl); + QQmlEngine engine; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent component(&engine, componentUrl); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o.isNull()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), componentUrl.toString() + QLatin1String(":-1 Inline components form a cycle!\n")); +} + +void tst_qqmllanguage::nestedInlineComponentNotAllowed() +{ + QQmlEngine engine; + auto url = testFileUrl("nestedIC.qml"); + QQmlComponent component(&engine, url); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); + QScopedPointer<QObject> o(component.create()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), QLatin1String("%1:%2").arg(url.toString(), QLatin1String("5 Nested inline components are not supported\n"))); +} + +void tst_qqmllanguage::inlineComponentStaticTypeResolution() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("InlineComponentChild.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QCOMPARE(o->property("i").toInt(), 42); +} + +void tst_qqmllanguage::inlineComponentInSingleton() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("singletonICTest.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + auto untyped = o->property("singleton1"); + QVERIFY(untyped.isValid()); + auto singleton1 = untyped.value<QObject*>(); + QVERIFY(singleton1); + QCOMPARE(singleton1->property("iProp").value<int>(), 42); + QCOMPARE(singleton1->property("sProp").value<QString>(), QString::fromLatin1("Hello, world")); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::nonExistingInlineComponent_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QString>("errorMessage"); + QTest::addColumn<int>("line"); + QTest::addColumn<int>("column"); + + QTest::newRow("Property type") << testFileUrl("nonExistingICUser1.qml") << QString("Type InlineComponentProvider has no inline component type called NonExisting") << 4 << 5; + QTest::newRow("Instantiation") << testFileUrl("nonExistingICUser2.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 4 << 5; + QTest::newRow("Inheritance") << testFileUrl("nonExistingICUser3.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 3 << 1; + QTest::newRow("From singleton") << testFileUrl("nonExistingICUser4.qml") << QString("Type MySingleton.SingletonTypeWithIC has no inline component type called NonExisting") << 5 << 5; + + QTest::newRow("Cannot access parent inline components from child") << testFileUrl("nonExistingICUser5.qml") << QString("Type InlineComponentProviderChild has no inline component type called StyledRectangle") << 4 << 5; +} + +void tst_qqmllanguage::nonExistingInlineComponent() +{ + QFETCH(QUrl, componentUrl); + QFETCH(QString, errorMessage); + QFETCH(int, line); + QFETCH(int, column); + QQmlEngine engine; + QQmlComponent component(&engine, componentUrl); + auto errors = component.errors(); + QCOMPARE(errors.size(), 1); + const auto &error = errors.first(); + QCOMPARE(error.description(), errorMessage); + QCOMPARE(error.line(), line); + QCOMPARE(error.column(), column); +} + +void tst_qqmllanguage::inlineComponentFoundBeforeOtherImports() +{ + QQmlEngine engine; + QUrl url = testFileUrl("inlineComponentFoundBeforeOtherImports.qml"); + QQmlComponent component(&engine, url); + + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "Created"); + QScopedPointer<QObject> root {component.create()}; +} + +void tst_qqmllanguage::inlineComponentDuplicateNameError() +{ + QQmlEngine engine; + QUrl url = testFileUrl("inlineComponentDuplicateName.qml"); + QQmlComponent component(&engine, url); + + QString message = QLatin1String("%1:5 Inline component names must be unique per file\n").arg(url.toString()); + QScopedPointer<QObject> root {component.create()}; + QVERIFY(root.isNull()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), message); +} + class TestItem : public QObject { Q_OBJECT Q_PROPERTY( QVector<QPointF> positions MEMBER m_points ) + Q_PROPERTY( QSet<QByteArray> barrays MEMBER m_barrays ) public: TestItem() = default; QVector< QPointF > m_points; + QSet<QByteArray> m_barrays; }; Q_DECLARE_METATYPE(QVector<QPointF>); +Q_DECLARE_METATYPE(QSet<QByteArray>); void tst_qqmllanguage::arrayToContainer() { QQmlEngine engine; qmlRegisterType<TestItem>("qt.test", 1, 0, "TestItem"); QVector<QPointF> points { QPointF (2.0, 3.0) }; + QSet<QByteArray> barrays { QByteArray("hello"), QByteArray("world") }; engine.rootContext()->setContextProperty("test", QVariant::fromValue(points)); QQmlComponent component(&engine, testFileUrl("arrayToContainer.qml")); VERIFY_ERRORS(0); - QScopedPointer<TestItem> root(qobject_cast<TestItem *>(component.createWithInitialProperties( {{"vector", QVariant::fromValue(points)}} ))); + QScopedPointer<TestItem> root(qobject_cast<TestItem *>(component.createWithInitialProperties( {{"vector", QVariant::fromValue(points)}, {"myset", QVariant::fromValue(barrays)} } ))); QVERIFY(root); QCOMPARE(root->m_points.at(0), QPointF (2.0, 3.0) ); + QVERIFY(root->m_barrays.contains("hello")); + QVERIFY(root->m_barrays.contains("world")); +} + +class EnumTester : public QObject +{ + Q_OBJECT +public: + enum Types + { + FIRST = 0, + SECOND, + THIRD + }; + Q_ENUM(Types) +}; + +void tst_qqmllanguage::qualifiedScopeInCustomParser() +{ + qmlRegisterUncreatableType<EnumTester>("scoped.custom.test", 1, 0, "EnumTester", + "Object only creatable in C++"); + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQml.Models 2.12\n" + "import scoped.custom.test 1.0 as BACKEND\n" + "ListModel {\n" + " ListElement { text: \"a\"; type: BACKEND.EnumTester.FIRST }\n" + "}\n", QUrl()); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); } QTEST_MAIN(tst_qqmllanguage) diff --git a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro index 4d44d6b22b..c0808f8599 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 qmlmodels-private +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 75a932b6f4..d54e3467b7 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -700,7 +700,7 @@ void tst_qqmllistmodel::error_data() QTest::newRow("unknown qualified ListElement not allowed") << "import QtQuick 2.0\nListModel { Foo.ListElement { a: 123 } }" - << "Foo.ListElement - Foo is not a namespace"; + << "Foo.ListElement - Foo is neither a type nor a namespace"; } void tst_qqmllistmodel::error() diff --git a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp index 199f7bc7e4..e25d555d61 100644 --- a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp +++ b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp @@ -45,6 +45,9 @@ class tst_qqmllistreference : public QQmlDataTest public: tst_qqmllistreference() {} +private: + void modeData(); + private slots: void initTestCase(); void qmllistreference(); @@ -56,12 +59,19 @@ private slots: void canAt(); void canClear(); void canCount(); + void canReplace(); + void canRemoveLast(); void isReadable(); void isManipulable(); void append(); void at(); + void clear_data() { modeData(); } void clear(); void count(); + void replace_data() { modeData(); } + void replace(); + void removeLast_data() { modeData(); } + void removeLast(); void copy(); void qmlmetaproperty(); void engineTypes(); @@ -76,7 +86,67 @@ class TestType : public QObject Q_PROPERTY(int intProperty READ intProperty) public: - TestType() : property(this, data) {} + enum Mode { + SyntheticClear, + SyntheticReplace, + SyntheticClearAndReplace, + SyntheticRemoveLast, + SyntheticRemoveLastAndReplace, + AutomaticReference, + AutomaticPointer + }; + + static void append(QQmlListProperty<TestType> *p, TestType *v) { + reinterpret_cast<QList<TestType *> *>(p->data)->append(v); + } + static int count(QQmlListProperty<TestType> *p) { + return reinterpret_cast<QList<TestType *> *>(p->data)->count(); + } + static TestType *at(QQmlListProperty<TestType> *p, int idx) { + return reinterpret_cast<QList<TestType *> *>(p->data)->at(idx); + } + static void clear(QQmlListProperty<TestType> *p) { + return reinterpret_cast<QList<TestType *> *>(p->data)->clear(); + } + static void replace(QQmlListProperty<TestType> *p, int idx, TestType *v) { + return reinterpret_cast<QList<TestType *> *>(p->data)->replace(idx, v); + } + static void removeLast(QQmlListProperty<TestType> *p) { + return reinterpret_cast<QList<TestType *> *>(p->data)->removeLast(); + } + + TestType(Mode mode = AutomaticReference) + { + switch (mode) { + case SyntheticClear: + property = QQmlListProperty<TestType>(this, &data, append, count, at, nullptr, + replace, removeLast); + break; + case SyntheticReplace: + property = QQmlListProperty<TestType>(this, &data, append, count, at, clear, + nullptr, removeLast); + break; + case SyntheticClearAndReplace: + property = QQmlListProperty<TestType>(this, &data, append, count, at, nullptr, + nullptr, removeLast); + break; + case SyntheticRemoveLast: + property = QQmlListProperty<TestType>(this, &data, append, count, at, clear, + replace, nullptr); + break; + case SyntheticRemoveLastAndReplace: + property = QQmlListProperty<TestType>(this, &data, append, count, at, clear, + nullptr, nullptr); + break; + case AutomaticReference: + property = QQmlListProperty<TestType>(this, data); + break; + case AutomaticPointer: + property = QQmlListProperty<TestType>(this, &data); + break; + } + } + QQmlListProperty<TestType> dataProperty() { return property; } int intProperty() const { return 10; } @@ -84,10 +154,24 @@ public: QQmlListProperty<TestType> property; }; +Q_DECLARE_METATYPE(TestType::Mode) + +void tst_qqmllistreference::modeData() +{ + QTest::addColumn<TestType::Mode>("mode"); + QTest::addRow("AutomaticReference") << TestType::AutomaticReference; + QTest::addRow("AutomaticPointer") << TestType::AutomaticPointer; + QTest::addRow("SyntheticClear") << TestType::SyntheticClear; + QTest::addRow("SyntheticReplace") << TestType::SyntheticReplace; + QTest::addRow("SyntheticClearAndReplace") << TestType::SyntheticClearAndReplace; + QTest::addRow("SyntheticRemoveLast") << TestType::SyntheticRemoveLast; + QTest::addRow("SyntheticRemoveLastAndReplace") << TestType::SyntheticRemoveLastAndReplace; +} + void tst_qqmllistreference::initTestCase() { QQmlDataTest::initTestCase(); - qmlRegisterType<TestType>(); + qmlRegisterAnonymousType<TestType>("Test", 1); } void tst_qqmllistreference::qmllistreference() @@ -340,6 +424,64 @@ void tst_qqmllistreference::canCount() } } +void tst_qqmllistreference::canReplace() +{ + QScopedPointer<TestType> tt(new TestType); + + { + QQmlListReference ref; + QVERIFY(!ref.canReplace()); + } + + { + QQmlListReference ref(tt.data(), "blah"); + QVERIFY(!ref.canReplace()); + } + + { + QQmlListReference ref(tt.data(), "data"); + QVERIFY(ref.canReplace()); + tt.reset(); + QVERIFY(!ref.canReplace()); + } + + { + TestType tt; + tt.property.replace = nullptr; + QQmlListReference ref(&tt, "data"); + QVERIFY(!ref.canReplace()); + } +} + +void tst_qqmllistreference::canRemoveLast() +{ + QScopedPointer<TestType> tt(new TestType); + + { + QQmlListReference ref; + QVERIFY(!ref.canRemoveLast()); + } + + { + QQmlListReference ref(tt.data(), "blah"); + QVERIFY(!ref.canRemoveLast()); + } + + { + QQmlListReference ref(tt.data(), "data"); + QVERIFY(ref.canRemoveLast()); + tt.reset(); + QVERIFY(!ref.canRemoveLast()); + } + + { + TestType tt; + tt.property.removeLast = nullptr; + QQmlListReference ref(&tt, "data"); + QVERIFY(!ref.canRemoveLast()); + } +} + void tst_qqmllistreference::isReadable() { TestType *tt = new TestType; @@ -474,7 +616,8 @@ void tst_qqmllistreference::at() void tst_qqmllistreference::clear() { - TestType *tt = new TestType; + QFETCH(TestType::Mode, mode); + TestType *tt = new TestType(mode); tt->data.append(tt); tt->data.append(0); tt->data.append(tt); @@ -540,6 +683,70 @@ void tst_qqmllistreference::count() } } +void tst_qqmllistreference::replace() +{ + QFETCH(TestType::Mode, mode); + QScopedPointer<TestType> tt(new TestType(mode)); + tt->data.append(tt.get()); + tt->data.append(nullptr); + tt->data.append(tt.get()); + + { + QQmlListReference ref(tt.get(), "data"); + QVERIFY(ref.replace(1, tt.get())); + QCOMPARE(ref.at(1), tt.get()); + QVERIFY(ref.replace(2, nullptr)); + QCOMPARE(ref.at(2), nullptr); + QCOMPARE(ref.count(), 3); + tt.reset(); + QVERIFY(!ref.replace(0, tt.get())); + } + + { + TestType tt; + tt.data.append(&tt); + tt.property.replace = nullptr; + QQmlListReference ref(&tt, "data"); + QVERIFY(!ref.replace(0, nullptr)); + } +} + +void tst_qqmllistreference::removeLast() +{ + QFETCH(TestType::Mode, mode); + QScopedPointer<TestType> tt(new TestType(mode)); + tt->data.append(tt.get()); + tt->data.append(nullptr); + tt->data.append(tt.get()); + + { + QQmlListReference ref; + QVERIFY(!ref.removeLast()); + } + + { + QQmlListReference ref(tt.get(), "blah"); + QVERIFY(!ref.removeLast()); + } + + { + QQmlListReference ref(tt.get(), "data"); + QCOMPARE(tt->data.count(), 3); + QVERIFY(ref.removeLast()); + QCOMPARE(tt->data.count(), 2); + tt.reset(); + QVERIFY(!ref.removeLast()); + } + + { + TestType tt; + tt.property.removeLast = nullptr; + QQmlListReference ref(&tt, "data"); + ref.append(&tt); + QVERIFY(!ref.removeLast()); + } +} + void tst_qqmllistreference::copy() { TestType tt; diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index a90749208c..e3094db708 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -102,6 +102,7 @@ private slots: void numberFromLocaleString_data(); void numberFromLocaleString(); void numberConstToLocaleString(); + void numberOptions(); void stringLocaleCompare_data(); void stringLocaleCompare(); @@ -448,7 +449,7 @@ void tst_qqmllocale::firstDayOfWeek() Q_ARG(QVariant, QVariant(locale))); QVariant val = obj->property("firstDayOfWeek"); - QCOMPARE(val.type(), QVariant::Int); + QCOMPARE(val.userType(), QMetaType::Int); int day = int(QLocale(locale).firstDayOfWeek()); if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday) @@ -1157,6 +1158,35 @@ void tst_qqmllocale::numberConstToLocaleString() QCOMPARE(obj->property("const2").toString(), l.toString(1234., 'f', 2)); } +void tst_qqmllocale::numberOptions() +{ + QQmlEngine engine; + QQmlComponent comp(&engine); + comp.setData(R"( + import QtQml 2.15 + QtObject { + id: root + property string formatted + property bool caughtException: false + Component.onCompleted: () => { + const myLocale = Qt.locale("de_DE") + myLocale.numberOptions = Locale.OmitGroupSeparator | Locale.RejectTrailingZeroesAfterDot + root.formatted = Number(10000).toLocaleString(myLocale, 'f', 4) + try { + Number.fromLocaleString(myLocale, "1,10"); + } catch (e) {console.warn(e); root.caughtException = true} + } + } + )", QUrl("testdata")); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Error: Locale: Number.fromLocaleString(): Invalid format"); + QScopedPointer<QObject> root {comp.create()}; + qDebug() << comp.errorString(); + QVERIFY(root); + QCOMPARE(root->property("formatted").toString(), QLatin1String("10000,0000")); + QCOMPARE(root->property("caughtException").toBool(), true); + +} + void tst_qqmllocale::stringLocaleCompare_data() { QTest::addColumn<QString>("string1"); diff --git a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp index ea157a7d15..b21d2a908d 100644 --- a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp +++ b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp @@ -112,9 +112,9 @@ void tst_QQmlMetaObject::property_data() QTest::newRow("date") << "property.date.qml" << QByteArray("QDateTime") << int(QMetaType::QDateTime) << false // default - << QVariant(QDateTime(QDate(2012, 2, 7))) + << QVariant(QDate(2012, 2, 7).startOfDay()) << true // writable - << QVariant(QDateTime(QDate(2010, 7, 2))); + << QVariant(QDate(2010, 7, 2).startOfDay()); QTest::newRow("variant") << "property.variant.qml" << QByteArray("QVariant") << int(QMetaType::QVariant) << true // default diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index 76185a97e0..296d1b14e0 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -157,7 +157,7 @@ void tst_qqmlmetatype::initTestCase() void tst_qqmlmetatype::qmlParserStatusCast() { - QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); + QVERIFY(!QQmlMetaType::qmlType(QMetaType::Int).isValid()); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).parserStatusCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).isValid()); @@ -177,7 +177,7 @@ void tst_qqmlmetatype::qmlParserStatusCast() void tst_qqmlmetatype::qmlPropertyValueSourceCast() { - QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); + QVERIFY(!QQmlMetaType::qmlType(QMetaType::Int).isValid()); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).propertyValueSourceCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid()); @@ -197,7 +197,7 @@ void tst_qqmlmetatype::qmlPropertyValueSourceCast() void tst_qqmlmetatype::qmlPropertyValueInterceptorCast() { - QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); + QVERIFY(!QQmlMetaType::qmlType(QMetaType::Int).isValid()); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid()); QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).propertyValueInterceptorCast(), -1); QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid()); @@ -256,8 +256,8 @@ void tst_qqmlmetatype::prettyTypeName() void tst_qqmlmetatype::isList() { - QCOMPARE(QQmlMetaType::isList(QVariant::Invalid), false); - QCOMPARE(QQmlMetaType::isList(QVariant::Int), false); + QCOMPARE(QQmlMetaType::isList(QMetaType::UnknownType), false); + QCOMPARE(QQmlMetaType::isList(QMetaType::Int), false); QQmlListProperty<TestType> list; @@ -307,7 +307,7 @@ void tst_qqmlmetatype::compositeType() //Loading the test file also loads all composite types it imports QQmlComponent c(&engine, testFileUrl("testImplicitComposite.qml")); - QObject* obj = c.create(); + QScopedPointer<QObject> obj(c.create()); QVERIFY(obj); QQmlType type = QQmlMetaType::qmlType(QString("ImplicitType"), QString(""), 1, 0); @@ -324,13 +324,13 @@ void tst_qqmlmetatype::externalEnums() qmlRegisterSingletonType<ExternalEnums>("x.y.z", 1, 0, "ExternalEnums", ExternalEnums::create); QQmlComponent c(&engine, testFileUrl("testExternalEnums.qml")); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); QVERIFY(obj); QVariant a = obj->property("a"); - QCOMPARE(a.type(), QVariant::Int); + QCOMPARE(a.userType(), QVariant::Int); QCOMPARE(a.toInt(), int(QStandardPaths::DocumentsLocation)); QVariant b = obj->property("b"); - QCOMPARE(b.type(), QVariant::Int); + QCOMPARE(b.userType(), QVariant::Int); QCOMPARE(b.toInt(), int(QStandardPaths::DocumentsLocation)); } @@ -394,10 +394,10 @@ void tst_qqmlmetatype::unregisterCustomType() QObject *controller = obj->findChild<QObject *>("controller"); QVERIFY(qobject_cast<Controller1 *>(controller)); QVariant stringVal = controller->property("string"); - QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.userType(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); QVariant enumVal = controller->property("enumVal"); - QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.userType(), QVariant::Int); QCOMPARE(enumVal.toInt(), 1); } QQmlMetaType::unregisterType(controllerId); @@ -417,10 +417,10 @@ void tst_qqmlmetatype::unregisterCustomType() QObject *controller = obj->findChild<QObject *>("controller"); QVERIFY(qobject_cast<Controller2 *>(controller)); QVariant stringVal = controller->property("string"); - QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.userType(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("Controller #2")); QVariant enumVal = controller->property("enumVal"); - QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.userType(), QVariant::Int); QCOMPARE(enumVal.toInt(), 111); } QQmlMetaType::unregisterType(controllerId); @@ -440,10 +440,10 @@ void tst_qqmlmetatype::unregisterCustomType() QObject *controller = obj->findChild<QObject *>("controller"); QVERIFY(qobject_cast<Controller1 *>(controller)); QVariant stringVal = controller->property("string"); - QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.userType(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); QVariant enumVal = controller->property("enumVal"); - QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.userType(), QVariant::Int); QCOMPARE(enumVal.toInt(), 1); } } @@ -489,7 +489,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType() QScopedPointer<QObject> obj(c.create()); QVERIFY(obj.data()); QVariant stringVal = obj->property("text"); - QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.userType(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); } QQmlMetaType::unregisterType(staticProviderId); @@ -505,7 +505,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType() QScopedPointer<QObject> obj(c.create()); QVERIFY(obj.data()); QVariant stringVal = obj->property("text"); - QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.userType(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2")); } QQmlMetaType::unregisterType(staticProviderId); @@ -521,7 +521,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType() QScopedPointer<QObject> obj(c.create()); QVERIFY(obj.data()); QVariant stringVal = obj->property("text"); - QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.userType(), QVariant::String); QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); } } @@ -552,7 +552,8 @@ void tst_qqmlmetatype::unregisterAttachedProperties() QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)), attachedType.metaObject()); - QVERIFY(c.create()); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj); } qmlClearTypeRegistrations(); @@ -571,7 +572,8 @@ void tst_qqmlmetatype::unregisterAttachedProperties() QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)), attachedType.metaObject()); - QVERIFY(c.create()); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj); } } diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml index 9789be8191..258667be18 100644 --- a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml @@ -1,5 +1,6 @@ pragma Singleton import QtQuick 2.0 +import Test 1.0 QtObject { property Loader _loader: Loader { @@ -7,10 +8,10 @@ QtObject { } Component.onCompleted: { - if (tracker.objectName === "first") - tracker.objectName = "second" + if (Tracker.objectName === "first") + Tracker.objectName = "second" else - tracker.objectName = "first" + Tracker.objectName = "first" //console.log("created singleton", this) } } diff --git a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp index ae8c231aab..bb5bb00adb 100644 --- a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp @@ -47,9 +47,9 @@ public: void registerTypes(const char *uri) { - // Because the module is protected, this plugin should never be loaded + // The module is protected. The plugin can still be loaded, but it cannot register + // any types. Q_UNUSED(uri); - Q_ASSERT(0); } }; diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index f89cc9f24a..75885bc84a 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -609,7 +609,7 @@ void tst_qqmlmoduleplugin::importStrictModule_data() << "import org.qtproject.NonstrictModule 1.0\n" "MyPluginType {}" << "Module 'org.qtproject.NonstrictModule' does not contain a module identifier directive - it cannot be protected from external registrations." - << ":1:1: plugin cannot be loaded for module \"org.qtproject.NonstrictModule\": Cannot install element 'MyPluginType' into protected namespace 'org.qtproject.StrictModule'"; + << ":1:1: plugin cannot be loaded for module \"org.qtproject.NonstrictModule\": Cannot install element 'MyPluginType' into protected module 'org.qtproject.StrictModule' version '1'"; QTest::newRow("non-strict preemption") << "import org.qtproject.PreemptiveModule 1.0\n" @@ -777,7 +777,7 @@ void tst_qqmlmoduleplugin::multiSingleton() { QQmlEngine engine; QObject obj; - engine.rootContext()->setContextProperty("tracker", &obj); + qmlRegisterSingletonInstance("Test", 1, 0, "Tracker", &obj); engine.addImportPath(m_importsDirectory); QQmlComponent component(&engine, testFileUrl("multiSingleton.qml")); QObject *object = component.create(); diff --git a/tests/auto/qml/qqmlnotifier/data/connectnotify.qml b/tests/auto/qml/qqmlnotifier/data/connectnotify.qml index 35226ee5ab..34e70e6afe 100644 --- a/tests/auto/qml/qqmlnotifier/data/connectnotify.qml +++ b/tests/auto/qml/qqmlnotifier/data/connectnotify.qml @@ -3,6 +3,7 @@ import Test 1.0 Item { id: root + required property ExportedClass exportedObject ExportedClass { id: exportedClass objectName: "exportedClass" @@ -22,7 +23,7 @@ Item { } property int foo: exportedClass.qmlObjectProp - property int baz: _exportedObject.cppObjectProp + property int baz: exportedObject.cppObjectProp // v4 bindings that could share a subscription. They don't, though, and the code // relies on that diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index de762d66c5..836b94ad45 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -185,8 +185,7 @@ void tst_qqmlnotifier::createObjects() QQmlComponent component(&engine, testFileUrl("connectnotify.qml")); exportedObject = new ExportedClass(); exportedObject->setObjectName("exportedObject"); - engine.rootContext()->setContextProperty("_exportedObject", exportedObject); - root = component.create(); + root = component.createWithInitialProperties({{"exportedObject", QVariant::fromValue(exportedObject)}}); QVERIFY(root != nullptr); exportedClass = qobject_cast<ExportedClass *>( @@ -324,12 +323,12 @@ void tst_qqmlnotifier::lotsOfBindings() TestObject o; QQmlEngine *e = new QQmlEngine; - e->rootContext()->setContextProperty(QStringLiteral("test"), &o); + qmlRegisterSingletonInstance("Test", 1, 0, "Test", &o); QList<QQmlComponent *> components; for (int i = 0; i < 20000; ++i) { QQmlComponent *component = new QQmlComponent(e); - component->setData("import QtQuick 2.0; Item { width: test.a; }", QUrl()); + component->setData("import QtQuick 2.0; import Test 1.0; Item {width: Test.a; }", QUrl()); component->create(e->rootContext()); components.append(component); } diff --git a/tests/auto/qml/qqmlparser/data/annotations/View1.qml b/tests/auto/qml/qqmlparser/data/annotations/View1.qml new file mode 100644 index 0000000000..2d3d7d2cfd --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/annotations/View1.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +//![2] +import QtQuick 2.0 +//![2] +import QtCharts 2.0 + +@Pippo{ atg1:3 } +@Annotation2{} +Item { + @Annotate{} + anchors.fill: parent + @AnnotateMore{ + property int x: 5 + } + @AnnotateALot{} + property variant othersSlice: 0 + + //![1] + ChartView { + id: chart + title: "Top-5 car brand shares in Finland" + anchors.fill: parent + legend.alignment: Qt.AlignBottom + antialiasing: true + +@ExtraAnnotation{ + signal pippo +} + PieSeries { + id: pieSeries + PieSlice { label: "Volkswagen"; value: 13.5 } + PieSlice { label: "Toyota"; value: 10.9 } + PieSlice { label: "Ford"; value: 8.6 } + PieSlice { label: "Skoda"; value: 8.2 } + PieSlice { label: "Volvo"; value: 6.8 } + } + } + +@SuperComplete{ +binding: late +} + Component.onCompleted: { + // You can also manipulate slices dynamically, like append a slice or set a slice exploded + othersSlice = pieSeries.append("Others", 52.0); + pieSeries.find("Volkswagen").exploded = true; + } + //![1] +} diff --git a/tests/auto/qml/qqmlparser/data/noannotations/View1.qml b/tests/auto/qml/qqmlparser/data/noannotations/View1.qml new file mode 100644 index 0000000000..945bce3a44 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/noannotations/View1.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Charts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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$ +** +****************************************************************************/ + +//![2] +import QtQuick 2.0 +//![2] +import QtCharts 2.0 + + + +Item { + + anchors.fill: parent + + + + + property variant othersSlice: 0 + + //![1] + ChartView { + id: chart + title: "Top-5 car brand shares in Finland" + anchors.fill: parent + legend.alignment: Qt.AlignBottom + antialiasing: true + + + + + PieSeries { + id: pieSeries + PieSlice { label: "Volkswagen"; value: 13.5 } + PieSlice { label: "Toyota"; value: 10.9 } + PieSlice { label: "Ford"; value: 8.6 } + PieSlice { label: "Skoda"; value: 8.2 } + PieSlice { label: "Volvo"; value: 6.8 } + } + } + + + + + Component.onCompleted: { + // You can also manipulate slices dynamically, like append a slice or set a slice exploded + othersSlice = pieSeries.append("Others", 52.0); + pieSeries.find("Volkswagen").exploded = true; + } + //![1] +} diff --git a/tests/auto/qml/qqmlparser/qqmlparser.pro b/tests/auto/qml/qqmlparser/qqmlparser.pro index d8e4b0dd06..7f117b3157 100644 --- a/tests/auto/qml/qqmlparser/qqmlparser.pro +++ b/tests/auto/qml/qqmlparser/qqmlparser.pro @@ -11,3 +11,4 @@ cross_compile: DEFINES += QTEST_CROSS_COMPILED TESTDATA = data/* include (../../shared/util.pri) +include (../../shared/astdump.pri) diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index 9d8818d01e..c7d09f9d6e 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -33,6 +33,7 @@ #include <private/qqmljsast_p.h> #include "../../shared/util.h" +#include "../../shared/qqmljsastdumper.h" #include <qtest.h> #include <QDir> @@ -62,6 +63,11 @@ private slots: void typeAnnotations(); void disallowedTypeAnnotations_data(); void disallowedTypeAnnotations(); + void semicolonPartOfExpressionStatement(); + void typeAssertion_data(); + void typeAssertion(); + void annotations_data(); + void annotations(); private: QStringList excludedDirs; @@ -141,6 +147,30 @@ struct TypeAnnotationObserver: public AST::Visitor } }; +struct ExpressionStatementObserver: public AST::Visitor +{ + int expressionsSeen = 0; + bool endsWithSemicolon = true; + + void operator()(AST::Node *node) + { + AST::Node::accept(node, this); + } + + virtual bool visit(AST::ExpressionStatement *statement) + { + ++expressionsSeen; + endsWithSemicolon = endsWithSemicolon + && (statement->lastSourceLocation().end() == statement->semicolonToken.end()); + return true; + } + + void throwRecursionDepthError() final + { + QFAIL("Maximum statement or expression depth exceeded"); + } +}; + } tst_qqmlparser::tst_qqmlparser() @@ -438,6 +468,123 @@ void tst_qqmlparser::disallowedTypeAnnotations() QVERIFY2(parser.errorMessage().startsWith("Type annotations are not permitted "), qPrintable(parser.errorMessage())); } +void tst_qqmlparser::semicolonPartOfExpressionStatement() +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String("A { property int x: 1+1; property int y: 2+2 \n" + "tt: {'a': 5, 'b': 6}; ff: {'c': 'rrr'}}"), 1); + QQmlJS::Parser parser(&engine); + QVERIFY(parser.parse()); + + check::ExpressionStatementObserver observer; + observer(parser.rootNode()); + + QCOMPARE(observer.expressionsSeen, 4); + QVERIFY(observer.endsWithSemicolon); +} + +void tst_qqmlparser::typeAssertion_data() +{ + QTest::addColumn<QString>("expression"); + QTest::addRow("as A") + << QString::fromLatin1("A { onStuff: (b as A).happen() }"); + QTest::addRow("as double paren") + << QString::fromLatin1("A { onStuff: console.log((12 as double)); }"); + QTest::addRow("as double noparen") + << QString::fromLatin1("A { onStuff: console.log(12 as double); }"); + QTest::addRow("property as double") + << QString::fromLatin1("A { prop: (12 as double); }"); + QTest::addRow("property noparen as double") + << QString::fromLatin1("A { prop: 12 as double; }"); + + // rabbits cannot be discerned from types on a syntactical level. + // We could detect this on a semantical level, once we implement type assertions there. + + QTest::addRow("as rabbit") + << QString::fromLatin1("A { onStuff: (b as rabbit).happen() }"); + QTest::addRow("as rabbit paren") + << QString::fromLatin1("A { onStuff: console.log((12 as rabbit)); }"); + QTest::addRow("as rabbit noparen") + << QString::fromLatin1("A { onStuff: console.log(12 as rabbit); }"); + QTest::addRow("property as rabbit") + << QString::fromLatin1("A { prop: (12 as rabbit); }"); + QTest::addRow("property noparen as rabbit") + << QString::fromLatin1("A { prop: 12 as rabbit; }"); +} + +void tst_qqmlparser::typeAssertion() +{ + QFETCH(QString, expression); + + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(expression, 1); + QQmlJS::Parser parser(&engine); + QVERIFY(parser.parse()); +} + +void tst_qqmlparser::annotations_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QString>("refFile"); + + QString tests = dataDirectory() + "/annotations/"; + QString compare = dataDirectory() + "/noannotations/"; + + QStringList files; + files << findFiles(QDir(tests)); + + QStringList refFiles; + refFiles << findFiles(QDir(compare)); + + for (const QString &file: qAsConst(files)) { + auto fileNameStart = file.lastIndexOf(QDir::separator()); + QStringRef fileName(&file, fileNameStart, file.length()-fileNameStart); + auto ref=std::find_if(refFiles.constBegin(),refFiles.constEnd(), [fileName](const QString &s){ return s.endsWith(fileName); }); + if (ref != refFiles.constEnd()) + QTest::newRow(qPrintable(file)) << file << *ref; + else + QTest::newRow(qPrintable(file)) << file << QString(); + } +} + +void tst_qqmlparser::annotations() +{ + using namespace QQmlJS; + + QFETCH(QString, file); + QFETCH(QString, refFile); + + QString code; + QString refCode; + + QFile f(file); + if (f.open(QFile::ReadOnly)) + code = QString::fromUtf8(f.readAll()); + QFile refF(refFile); + if (!refFile.isEmpty() && refF.open(QFile::ReadOnly)) + refCode = QString::fromUtf8(refF.readAll()); + + const bool qmlMode = true; + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(code, 1, qmlMode); + Parser parser(&engine); + QVERIFY(parser.parse()); + + if (!refCode.isEmpty()) { + Engine engine2; + Lexer lexer2(&engine2); + lexer2.setCode(refCode, 1, qmlMode); + Parser parser2(&engine2); + QVERIFY(parser2.parse()); + + QCOMPARE(AstDumper::diff(parser.ast(), parser2.rootNode(), 3, DumperOptions::NoAnnotations | DumperOptions::NoLocations), QString()); + } +} + QTEST_MAIN(tst_qqmlparser) #include "tst_qqmlparser.moc" diff --git a/tests/auto/qml/qqmlproperty/data/aliasToBinding.qml b/tests/auto/qml/qqmlproperty/data/aliasToBinding.qml new file mode 100644 index 0000000000..54f9e3f944 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/aliasToBinding.qml @@ -0,0 +1,23 @@ +import QtQuick 2.7 + +Item { + id: _window + property bool userFontStrikeout: true + + Component.onCompleted: { + _box.font.strikeout = Qt.binding(function() { return _window.userFontStrikeout; }); + } + + Rectangle { + id: _box + width: 100 + height: 100 + property alias font: _text.font + + Text { + id: _text + anchors.fill: parent + text: "Text" + } + } +} diff --git a/tests/auto/qml/qqmlproperty/data/assignEmptyVariantMap.qml b/tests/auto/qml/qqmlproperty/data/assignEmptyVariantMap.qml index a9e51c1255..440f07ac87 100644 --- a/tests/auto/qml/qqmlproperty/data/assignEmptyVariantMap.qml +++ b/tests/auto/qml/qqmlproperty/data/assignEmptyVariantMap.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 Item { + required property QtObject o Component.onCompleted: { o.variantMap = {}; } } diff --git a/tests/auto/qml/qqmlproperty/data/interfaceBinding2.qml b/tests/auto/qml/qqmlproperty/data/interfaceBinding2.qml new file mode 100644 index 0000000000..e7c5dc7344 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/interfaceBinding2.qml @@ -0,0 +1,27 @@ +import QtQuick 2.12 +import io.qt.bugreports 2.0 +Item { + InterfaceConsumer2 { + objectName: "a1" + i: A2 { + property int i: 42 + } + } + + InterfaceConsumer2 { + objectName: "a2" + property A2 a: A2 { + property int i: 43 + } + i: a + } + + InterfaceConsumer2 { + objectName: "a3" + property A2 a: A2 { + id : aa + property int i: 44 + } + i: aa + } +} diff --git a/tests/auto/qml/qqmlproperty/interfaces.h b/tests/auto/qml/qqmlproperty/interfaces.h new file mode 100644 index 0000000000..2c06c5f594 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/interfaces.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +#ifndef INTERFACES_H +#define INTERFACES_H + +#include <QtQml/qqml.h> + +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 +}; + +struct Interface2 +{ + Q_GADGET + QML_INTERFACE +}; + +QT_BEGIN_NAMESPACE +#define MyInterface2_iid "io.qt.bugreports.Interface2" +Q_DECLARE_INTERFACE(Interface2, MyInterface2_iid); +QT_END_NAMESPACE + +class A2 : public QObject, Interface2 { + Q_OBJECT + QML_ELEMENT + Q_INTERFACES(Interface2) +}; + +class B2 : public QObject, Interface2 { + Q_OBJECT + QML_ELEMENT + Q_INTERFACES(Interface2) +}; + +class C2 : public QObject { + Q_OBJECT + QML_ELEMENT +}; + +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; +}; + + +class InterfaceConsumer2 : public QObject +{ + Q_OBJECT + + Q_PROPERTY(Interface2 *i READ interface WRITE setInterface NOTIFY interfaceChanged) + Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged) + + QML_ELEMENT + +public: + + Interface2* interface() const + { + return m_interface; + } + void setInterface(Interface2* interface2) + { + QObject* object = reinterpret_cast<QObject*>(interface2); + m_testValue = object->property("i").toInt(); + emit testValueChanged(); + if (m_interface == interface2) + return; + + m_interface = interface2; + emit interfaceChanged(); + } + + int testValue() { + return m_testValue; + } + +signals: + void interfaceChanged(); + void testValueChanged(); + +private: + Interface2 *m_interface = nullptr; + int m_testValue = 0; +}; + +#endif // INTERFACES_H diff --git a/tests/auto/qml/qqmlproperty/qqmlproperty.pro b/tests/auto/qml/qqmlproperty/qqmlproperty.pro index b1bcf1f17d..4d42975369 100644 --- a/tests/auto/qml/qqmlproperty/qqmlproperty.pro +++ b/tests/auto/qml/qqmlproperty/qqmlproperty.pro @@ -1,7 +1,10 @@ -CONFIG += testcase +CONFIG += testcase qmltypes TARGET = tst_qqmlproperty macx:CONFIG -= app_bundle +QML_IMPORT_NAME = io.qt.bugreports +QML_IMPORT_VERSION = 2.0 + SOURCES += tst_qqmlproperty.cpp include (../../shared/util.pri) @@ -9,3 +12,6 @@ include (../../shared/util.pri) TESTDATA = data/* QT += core-private gui-private qml-private testlib + +HEADERS += \ + interfaces.h diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 864a47e998..8a96fc52c5 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -25,6 +25,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + +#include "interfaces.h" #include <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -157,6 +159,8 @@ private slots: void copy(); + void bindingToAlias(); + void nestedQQmlPropertyMap(); void underscorePropertyChangeHandler(); @@ -1224,10 +1228,10 @@ void tst_qqmlproperty::read() } { QQmlComponent component(&engine, testFileUrl("readSynthesizedObject.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QQmlProperty p(object, "test", &engine); + QQmlProperty p(object.data(), "test", &engine); QCOMPARE(p.propertyTypeCategory(), QQmlProperty::Object); QVERIFY(p.propertyType() != QMetaType::QObjectStar); @@ -1239,10 +1243,10 @@ void tst_qqmlproperty::read() } { // static QQmlComponent component(&engine, testFileUrl("readSynthesizedObject.qml")); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QVariant v = QQmlProperty::read(object, "test", &engine); + QVariant v = QQmlProperty::read(object.data(), "test", &engine); QCOMPARE(v.userType(), int(QMetaType::QObjectStar)); QCOMPARE(qvariant_cast<QObject *>(v)->property("a").toInt(), 10); QCOMPARE(qvariant_cast<QObject *>(v)->property("b").toInt(), 19); @@ -1252,41 +1256,38 @@ void tst_qqmlproperty::read() { QQmlComponent component(&engine); component.setData("import Test 1.0\nMyContainer { }", QUrl()); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QQmlProperty p(object, "MyContainer.foo", qmlContext(object)); + QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data())); QCOMPARE(p.read(), QVariant(13)); - delete object; } { QQmlComponent component(&engine); component.setData("import Test 1.0\nMyContainer { MyContainer.foo: 10 }", QUrl()); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QQmlProperty p(object, "MyContainer.foo", qmlContext(object)); + QQmlProperty p(object.data(), "MyContainer.foo", qmlContext(object.data())); QCOMPARE(p.read(), QVariant(10)); - delete object; } { QQmlComponent component(&engine); component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QQmlProperty p(object, "Foo.MyContainer.foo", qmlContext(object)); + QQmlProperty p(object.data(), "Foo.MyContainer.foo", qmlContext(object.data())); QCOMPARE(p.read(), QVariant(10)); - delete object; } { // static QQmlComponent component(&engine); component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); - QObject *object = component.create(); + QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QCOMPARE(QQmlProperty::read(object, "Foo.MyContainer.foo", qmlContext(object)), QVariant(10)); - delete object; + QCOMPARE(QQmlProperty::read(object.data(), "Foo.MyContainer.foo", + qmlContext(object.data())), QVariant(10)); } } @@ -1447,11 +1448,12 @@ void tst_qqmlproperty::write() { // QChar -> QString QQmlComponent component(&engine); component.setData("import Test 1.0\nPropertyObject { stringProperty: constQChar }", QUrl()); - PropertyObject *obj = qobject_cast<PropertyObject*>(component.create()); - QVERIFY(obj != nullptr); - if (obj) { - QQmlProperty stringProperty(obj, "stringProperty"); - QCOMPARE(stringProperty.read(), QVariant(QString(obj->constQChar()))); + QScopedPointer<QObject> object(component.create()); + PropertyObject *propertyObject = qobject_cast<PropertyObject*>(object.data()); + QVERIFY(propertyObject != nullptr); + if (propertyObject) { + QQmlProperty stringProperty(propertyObject, "stringProperty"); + QCOMPARE(stringProperty.read(), QVariant(QString(propertyObject->constQChar()))); } } @@ -1628,29 +1630,32 @@ void tst_qqmlproperty::writeObjectToList() { QQmlComponent containerComponent(&engine); containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl()); - MyContainer *container = qobject_cast<MyContainer*>(containerComponent.create()); + QScopedPointer<QObject> object(containerComponent.create()); + MyContainer *container = qobject_cast<MyContainer*>(object.data()); QVERIFY(container != nullptr); QQmlListReference list(container, "children"); QCOMPARE(list.count(), 1); - MyQmlObject *object = new MyQmlObject; + QScopedPointer<MyQmlObject> childObject(new MyQmlObject); QQmlProperty prop(container, "children"); - prop.write(QVariant::fromValue(object)); + prop.write(QVariant::fromValue(childObject.data())); QCOMPARE(list.count(), 1); - QCOMPARE(list.at(0), qobject_cast<QObject*>(object)); + QCOMPARE(list.at(0), qobject_cast<QObject*>(childObject.data())); } void tst_qqmlproperty::writeListToList() { QQmlComponent containerComponent(&engine); containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl()); - MyContainer *container = qobject_cast<MyContainer*>(containerComponent.create()); + QScopedPointer<QObject> object(containerComponent.create()); + MyContainer *container = qobject_cast<MyContainer*>(object.data()); QVERIFY(container != nullptr); QQmlListReference list(container, "children"); QCOMPARE(list.count(), 1); QList<QObject*> objList; - objList << new MyQmlObject() << new MyQmlObject() << new MyQmlObject() << new MyQmlObject(); + objList << new MyQmlObject(this) << new MyQmlObject(this) + << new MyQmlObject(this) << new MyQmlObject(this); QQmlProperty prop(container, "children"); prop.write(QVariant::fromValue(objList)); QCOMPARE(list.count(), 4); @@ -1828,10 +1833,11 @@ void tst_qqmlproperty::crashOnValueProperty() QQmlComponent component(engine); component.setData("import Test 1.0\nPropertyObject { wrectProperty.x: 10 }", QUrl()); - PropertyObject *obj = qobject_cast<PropertyObject*>(component.create()); - QVERIFY(obj != nullptr); + QScopedPointer<QObject> object(component.create()); + PropertyObject *propertyObject = qobject_cast<PropertyObject*>(object.data()); + QVERIFY(propertyObject != nullptr); - QQmlProperty p(obj, "wrectProperty.x", qmlContext(obj)); + QQmlProperty p(propertyObject, "wrectProperty.x", qmlContext(propertyObject)); QCOMPARE(p.name(), QString("wrectProperty.x")); QCOMPARE(p.read(), QVariant(10)); @@ -1999,11 +2005,9 @@ void tst_qqmlproperty::assignEmptyVariantMap() QCOMPARE(o.variantMap().count(), 1); QCOMPARE(o.variantMap().isEmpty(), false); - QQmlContext context(&engine); - context.setContextProperty("o", &o); QQmlComponent component(&engine, testFileUrl("assignEmptyVariantMap.qml")); - QObject *obj = component.create(&context); + QObject *obj = component.createWithInitialProperties({{"o", QVariant::fromValue(&o)}}); QVERIFY(obj); QCOMPARE(o.variantMap().count(), 0); @@ -2090,74 +2094,20 @@ void tst_qqmlproperty::nullPropertyBinding() 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"); + 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 QVector<QUrl> urls = { + testFileUrl("interfaceBinding.qml"), + testFileUrl("interfaceBinding2.qml") + }; + + for (const QUrl &url : urls) { QQmlEngine engine; QQmlComponent component(&engine, url); QScopedPointer<QObject> root(component.create()); @@ -2165,6 +2115,7 @@ void tst_qqmlproperty::interfaceBinding() 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() @@ -2214,6 +2165,15 @@ void tst_qqmlproperty::initTestCase() qmlRegisterType<MyContainer>("Test",1,0,"MyContainer"); } +// QTBUG-60908 +void tst_qqmlproperty::bindingToAlias() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("aliasToBinding.qml")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); +} + void tst_qqmlproperty::nestedQQmlPropertyMap() { QQmlPropertyMap mainPropertyMap; diff --git a/tests/auto/qml/qqmlqt/data/formatting.qml b/tests/auto/qml/qqmlqt/data/formatting.qml index 7a462c8eeb..f2d1e1b5c8 100644 --- a/tests/auto/qml/qqmlqt/data/formatting.qml +++ b/tests/auto/qml/qqmlqt/data/formatting.qml @@ -41,4 +41,9 @@ QtObject { property string err_dateTime1: Qt.formatDateTime() property string err_dateTime2: Qt.formatDateTime(new Date, new Object) + + property var qdate + property var qtime + property var qdatetime + property var qvariant } diff --git a/tests/auto/qml/qqmlqt/data/formattingLocale.qml b/tests/auto/qml/qqmlqt/data/formattingLocale.qml new file mode 100644 index 0000000000..9da349b101 --- /dev/null +++ b/tests/auto/qml/qqmlqt/data/formattingLocale.qml @@ -0,0 +1,12 @@ +import QtQml 2.15 + +QtObject { + required property var myDateTime + required property var myDate + property var myTime + + property string dateTimeString: Qt.formatDateTime(myDateTime, Qt.locale("de_DE"), Locale.NarrowFormat) + property string dateString: Qt.formatDate(myDate, Qt.locale("de_DE")) + + function invalidUsage() { Qt.formatTime(myTime, null, "hello") } +} diff --git a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml index 9d73640c87..65732442af 100644 --- a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml +++ b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml @@ -1,6 +1,8 @@ import QtQuick 2.0 +import Test 1.0 QtObject { + required property TimeProvider tp Component.onCompleted: { var t = tp.time; tp.time = t; diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 2d8115e867..1a54397f1a 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -91,6 +91,7 @@ private slots: void dateTimeFormatting_data(); void dateTimeFormattingVariants(); void dateTimeFormattingVariants_data(); + void dateTimeFormattingWithLocale(); void isQtObject(); void btoa(); void atob(); @@ -777,24 +778,24 @@ void tst_qqmlqt::dateTimeFormatting() QQmlEngine eng; - eng.rootContext()->setContextProperty("qdate", date); - eng.rootContext()->setContextProperty("qtime", time); - eng.rootContext()->setContextProperty("qdatetime", dateTime); - QQmlComponent component(&eng, testFileUrl("formatting.qml")); QStringList warnings; - warnings << component.url().toString() + ":37: Error: Qt.formatDate(): Invalid date format" - << component.url().toString() + ":36: Error: Qt.formatDate(): Invalid arguments" - << component.url().toString() + ":40: Error: Qt.formatTime(): Invalid time format" - << component.url().toString() + ":39: Error: Qt.formatTime(): Invalid arguments" - << component.url().toString() + ":43: Error: Qt.formatDateTime(): Invalid datetime format" - << component.url().toString() + ":42: Error: Qt.formatDateTime(): Invalid arguments"; + warnings << component.url().toString() + ":37: Error: Qt.formatDate(): Bad second argument (must be either string, number or locale)" + << component.url().toString() + ":36: Error: Qt.formatDate(): Missing argument" + << component.url().toString() + ":40: Error: Qt.formatTime(): Bad second argument (must be either string, number or locale)" + << component.url().toString() + ":39: Error: Qt.formatTime(): Missing argument" + << component.url().toString() + ":43: Error: Qt.formatDateTime(): Bad second argument (must be either string, number or locale)" + << component.url().toString() + ":42: Error: Qt.formatDateTime(): Missing argument"; foreach (const QString &warning, warnings) QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - QObject *object = component.create(); + QObject *object = component.createWithInitialProperties({ + {"qdate", date}, + {"qtime", time}, + {"qdatetime", dateTime} + }); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); QVERIFY(object != nullptr); @@ -815,6 +816,8 @@ void tst_qqmlqt::dateTimeFormatting() void tst_qqmlqt::dateTimeFormatting_data() { + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + // Test intentionally uses deprecated enumerators from Qt::DateFormat QTest::addColumn<QString>("method"); QTest::addColumn<QStringList>("inputProperties"); QTest::addColumn<QStringList>("expectedResults"); @@ -844,6 +847,7 @@ void tst_qqmlqt::dateTimeFormatting_data() << (QStringList() << dateTime.toString(Qt::DefaultLocaleShortDate) << dateTime.toString(Qt::DefaultLocaleLongDate) << dateTime.toString("M/d/yy H:m:s a")); + QT_WARNING_POP } void tst_qqmlqt::dateTimeFormattingVariants() @@ -853,21 +857,20 @@ void tst_qqmlqt::dateTimeFormattingVariants() QFETCH(QStringList, expectedResults); QQmlEngine eng; - eng.rootContext()->setContextProperty("qvariant", variant); QQmlComponent component(&eng, testFileUrl("formatting.qml")); QStringList warnings; - warnings << component.url().toString() + ":37: Error: Qt.formatDate(): Invalid date format" - << component.url().toString() + ":36: Error: Qt.formatDate(): Invalid arguments" - << component.url().toString() + ":40: Error: Qt.formatTime(): Invalid time format" - << component.url().toString() + ":39: Error: Qt.formatTime(): Invalid arguments" - << component.url().toString() + ":43: Error: Qt.formatDateTime(): Invalid datetime format" - << component.url().toString() + ":42: Error: Qt.formatDateTime(): Invalid arguments"; + warnings << component.url().toString() + ":37: Error: Qt.formatDate(): Bad second argument (must be either string, number or locale)" + << component.url().toString() + ":36: Error: Qt.formatDate(): Missing argument" + << component.url().toString() + ":40: Error: Qt.formatTime(): Bad second argument (must be either string, number or locale)" + << component.url().toString() + ":39: Error: Qt.formatTime(): Missing argument" + << component.url().toString() + ":43: Error: Qt.formatDateTime(): Bad second argument (must be either string, number or locale)" + << component.url().toString() + ":42: Error: Qt.formatDateTime(): Missing argument"; foreach (const QString &warning, warnings) QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); - QObject *object = component.create(); + QObject *object = component.createWithInitialProperties({{"qvariant", variant}}); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); QVERIFY(object != nullptr); @@ -883,6 +886,8 @@ void tst_qqmlqt::dateTimeFormattingVariants() void tst_qqmlqt::dateTimeFormattingVariants_data() { + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + // Test intentionally uses deprecated enumerators from Qt::DateFormat QTest::addColumn<QString>("method"); QTest::addColumn<QVariant>("variant"); QTest::addColumn<QStringList>("expectedResults"); @@ -923,6 +928,28 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() QTest::newRow("formatDate, int") << "formatDate" << QVariant::fromValue(integer) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy")); QTest::newRow("formatDateTime, int") << "formatDateTime" << QVariant::fromValue(integer) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a")); QTest::newRow("formatTime, int") << "formatTime" << QVariant::fromValue(integer) << (QStringList() << temporary.time().toString(Qt::DefaultLocaleShortDate) << temporary.time().toString(Qt::DefaultLocaleLongDate) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz")); + QT_WARNING_POP +} + +void tst_qqmlqt::dateTimeFormattingWithLocale() +{ + QQmlEngine engine; + auto url = testFileUrl("formattingLocale.qml"); + QQmlComponent comp(&engine, url); + QDateTime dateTime = QDateTime::fromString("M1d1y9800:01:02", + "'M'M'd'd'y'yyhh:mm:ss"); + QDate date(1995, 5, 17); + QScopedPointer<QObject> o(comp.createWithInitialProperties({ {"myDateTime", dateTime}, {"myDate", date} })); + QVERIFY(!o.isNull()); + + auto dateTimeString = o->property("dateTimeString").toString(); + QCOMPARE(dateTimeString, QLocale("de_DE").toString(dateTime, QLocale::NarrowFormat)); + auto dateString = o->property("dateString").toString(); + QCOMPARE(dateString, QLocale("de_DE").toString(date, QLocale::ShortFormat)); + + QString warningMsg = url.toString() + QLatin1String(":11: Error: Qt.formatTime(): Third argument must be a Locale format option"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, warningMsg.toUtf8().constData()); + QMetaObject::invokeMethod(o.get(), "invalidUsage"); } void tst_qqmlqt::isQtObject() @@ -1174,6 +1201,7 @@ void tst_qqmlqt::qtObjectContents() class TimeProvider: public QObject { Q_OBJECT + QML_NAMED_ELEMENT(TimeProvider) Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged) public: @@ -1254,13 +1282,14 @@ void tst_qqmlqt::timeRoundtrip() TimeZoneSwitch tzs(QTest::currentDataTag()); QFETCH(QTime, time); + qmlRegisterTypesAndRevisions<TimeProvider>("Test", 1); TimeProvider tp(time); QQmlEngine eng; - eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp); + //qmlRegisterSingletonInstance("Test", 1, 0, "TimeProvider", &tp); QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml")); - QObject *obj = component.create(); + QObject *obj = component.createWithInitialProperties({{"tp", QVariant::fromValue(&tp)}}); QVERIFY(obj != nullptr); // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without diff --git a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro index 9d298dfdf2..11b11132aa 100644 --- a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro +++ b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro @@ -7,4 +7,4 @@ include (../../shared/util.pri) TESTDATA = data/* -QT += core gui qml-private qml quick-private quick testlib qmlmodels-private +QT += core gui qml-private qml quick-private quick testlib diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp index 321896a8f3..3923824fa2 100644 --- a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp +++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -29,8 +29,8 @@ #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> #include <QtCore/qregularexpression.h> +#include <QtCore/qabstractitemmodel.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> @@ -69,7 +69,7 @@ void tst_QQmlTableModel::appendRemoveRow() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel") .value<QAbstractTableModel *>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -214,7 +214,7 @@ void tst_QQmlTableModel::appendRowToEmptyModel() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 0); QCOMPARE(model->columnCount(), 2); @@ -249,7 +249,7 @@ void tst_QQmlTableModel::clear() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -288,7 +288,7 @@ void tst_QQmlTableModel::getRow() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -319,7 +319,7 @@ void tst_QQmlTableModel::insertRow() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -475,7 +475,7 @@ void tst_QQmlTableModel::moveRow() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->columnCount(), 2); QCOMPARE(model->rowCount(), 2); @@ -603,7 +603,7 @@ void tst_QQmlTableModel::setRow() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->columnCount(), 2); QCOMPARE(model->rowCount(), 2); @@ -763,7 +763,7 @@ void tst_QQmlTableModel::setDataThroughDelegate() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -826,7 +826,7 @@ void tst_QQmlTableModel::setRowsImperatively() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 0); QCOMPARE(model->columnCount(), 2); @@ -863,7 +863,7 @@ void tst_QQmlTableModel::setRowsMultipleTimes() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -920,7 +920,7 @@ void tst_QQmlTableModel::dataAndEditing() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("model").value<QAbstractTableModel*>(); QVERIFY(model); const QHash<int, QByteArray> roleNames = model->roleNames(); @@ -940,7 +940,7 @@ void tst_QQmlTableModel::omitTableModelColumnIndex() QQmlComponent component(&engine, testFileUrl("omitTableModelColumnIndex.qml")); QCOMPARE(component.status(), QQmlComponent::Ready); - QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create())); + QScopedPointer<QAbstractTableModel> model(qobject_cast<QAbstractTableModel*>(component.create())); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -964,7 +964,7 @@ void tst_QQmlTableModel::complexRow() QCOMPARE(tableView->rows(), 2); QCOMPARE(tableView->columns(), 2); - QQmlTableModel *model = tableView->model().value<QQmlTableModel*>(); + auto *model = tableView->model().value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); @@ -983,7 +983,7 @@ void tst_QQmlTableModel::appendRowWithDouble() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + auto *model = view.rootObject()->property("testModel").value<QAbstractTableModel*>(); QVERIFY(model); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->columnCount(), 2); diff --git a/tests/auto/qml/qqmltypeloader/data/declarativeCppType.qml b/tests/auto/qml/qqmltypeloader/data/declarativeCppType.qml new file mode 100644 index 0000000000..9061f3beb5 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/declarativeCppType.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import declarative.import.for.typeloader.test 3.2 + +DeclarativeTestType { + objectName: "ddddd" +} diff --git a/tests/auto/qml/qqmltypeloader/declarativetesttype.h b/tests/auto/qml/qqmltypeloader/declarativetesttype.h new file mode 100644 index 0000000000..a21cdcfd1d --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/declarativetesttype.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef DECLARATIVETESTTYPE_H +#define DECLARATIVETESTTYPE_H + +#include <QObject> +#include <qqml.h> + +class DeclarativeTestType : public QObject +{ + Q_OBJECT + QML_ELEMENT + +public: + explicit DeclarativeTestType(QObject *parent = nullptr) : QObject(parent) {} +}; + +#endif // DECLARATIVETESTTYPE_H diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 9ad53aaa8b..266a4e97d6 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -58,9 +58,11 @@ private slots: void qmlSingletonWithinModule(); void multiSingletonModule(); void implicitComponentModule(); + void customDiskCachePath(); void qrcRootPathUrl(); void implicitImport(); void compositeSingletonCycle(); + void declarativeCppType(); }; void tst_QQMLTypeLoader::testLoadComplete() @@ -94,6 +96,8 @@ void tst_QQMLTypeLoader::trimCache() { QQmlEngine engine; QQmlTypeLoader &loader = QQmlEnginePrivate::get(&engine)->typeLoader; + QVector<QQmlTypeData *> releaseLater; + QVector<QV4::ExecutableCompilationUnit *> releaseCompilationUnitLater; for (int i = 0; i < 256; ++i) { QUrl url = testFileUrl("trim_cache.qml"); url.setQuery(QString::number(i)); @@ -106,8 +110,10 @@ void tst_QQMLTypeLoader::trimCache() // QQmlTypeData or its compiledData() should prevent the trimming. if (i % 10 == 0) { // keep ref on data, don't add ref on data->compiledData() + releaseLater.append(data); } else if (i % 5 == 0) { data->compilationUnit()->addref(); + releaseCompilationUnitLater.append(data->compilationUnit()); data->release(); } else { data->release(); @@ -123,6 +129,12 @@ void tst_QQMLTypeLoader::trimCache() QVERIFY(!loader.isTypeLoaded(url)); // The cache is free to keep the others. } + + for (auto *data : qAsConst(releaseCompilationUnitLater)) + data->release(); + + for (auto *data : qAsConst(releaseLater)) + data->release(); } void tst_QQMLTypeLoader::trimCache2() @@ -508,6 +520,35 @@ void tst_QQMLTypeLoader::implicitComponentModule() checkCleanCacheLoad(QLatin1String("implicitComponentModule")); } +void tst_QQMLTypeLoader::customDiskCachePath() +{ +#if QT_CONFIG(process) + const char *skipKey = "QT_TST_QQMLTYPELOADER_SKIP_MISMATCH"; + if (qEnvironmentVariableIsSet(skipKey)) { + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("Base.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + return; + } + + QTemporaryDir dir; + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("customDiskCachePath"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String(skipKey), QLatin1String("1")); + env.insert(QLatin1String("QML_DISK_CACHE_PATH"), dir.path()); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + QCOMPARE(child.exitCode(), 0); + QDir cacheDir(dir.path()); + QVERIFY(!cacheDir.isEmpty()); +#endif +} + void tst_QQMLTypeLoader::qrcRootPathUrl() { QQmlEngine engine; @@ -542,6 +583,15 @@ void tst_QQMLTypeLoader::compositeSingletonCycle() QCOMPARE(qvariant_cast<QColor>(object->property("color")), QColorConstants::Black); } +void tst_QQMLTypeLoader::declarativeCppType() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("declarativeCppType.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); +} + QTEST_MAIN(tst_QQMLTypeLoader) #include "tst_qqmltypeloader.moc" diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro index 0352561e03..19834ff537 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro @@ -1,4 +1,4 @@ -CONFIG += testcase +CONFIG += testcase qmltypes TARGET = tst_qqmltypeloader QT += qml testlib qml-private quick macx:CONFIG -= app_bundle @@ -8,7 +8,10 @@ SOURCES += \ ../../shared/testhttpserver.cpp HEADERS += \ - ../../shared/testhttpserver.h + ../../shared/testhttpserver.h \ + declarativetesttype.h -include (../../shared/util.pri) +QML_IMPORT_VERSION = 3.2 +QML_IMPORT_NAME = "declarative.import.for.typeloader.test" +include (../../shared/util.pri) diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml index d2f748c4c4..2aa03ed39f 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml +++ b/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 import Test 1.0 Item { + required property TestValueExporter testValueExporter property bool success: false // Test user value type stored as both var and variant diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp index 22074602b7..b44889798c 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp @@ -256,6 +256,7 @@ class TestValueExporter : public QObject { Q_OBJECT Q_PROPERTY(TestValue testValue READ testValue WRITE setTestValue) + QML_NAMED_ELEMENT(TestValueExporter) public: TestValue testValue() const { return m_testValue; } void setTestValue(const TestValue &v) { m_testValue = v; } @@ -275,15 +276,14 @@ void tst_qqmlvaluetypeproviders::userType() qRegisterMetaType<TestValue>(); QMetaType::registerComparators<TestValue>(); - qmlRegisterType<TestValueExporter>("Test", 1, 0, "TestValueExporter"); + qmlRegisterTypesAndRevisions<TestValueExporter>("Test", 1); TestValueExporter exporter; QQmlEngine e; - e.rootContext()->setContextProperty("testValueExporter", &exporter); QQmlComponent component(&e, testFileUrl("userType.qml")); - QScopedPointer<QObject> obj(component.create()); + QScopedPointer<QObject> obj(component.createWithInitialProperties({{"testValueExporter", QVariant::fromValue(&exporter)}})); QVERIFY(obj != nullptr); QCOMPARE(obj->property("success").toBool(), true); } diff --git a/tests/auto/qml/qqmlvaluetypes/data/qmlproperty_read.qml b/tests/auto/qml/qqmlvaluetypes/data/qmlproperty_read.qml new file mode 100644 index 0000000000..b9c9ee779b --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypes/data/qmlproperty_read.qml @@ -0,0 +1,9 @@ +import Test 1.0 +import QtQml 2.0 + +MyTypeObject { + property QtObject colorPropertyObject: colorProperty.object + property string colorPropertyName: colorProperty.name + property QtObject invalidPropertyObject: invalidProperty.object + property string invalidPropertyName: invalidProperty.name +} diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h index 798c96e188..78797f06b1 100644 --- a/tests/auto/qml/qqmlvaluetypes/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h @@ -48,6 +48,8 @@ #include <private/qqmlproperty_p.h> #include <private/qqmlpropertyvalueinterceptor_p.h> +Q_DECLARE_METATYPE(QQmlProperty) + class MyTypeObject : public QObject { Q_OBJECT @@ -71,6 +73,8 @@ class MyTypeObject : public QObject 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) + Q_PROPERTY(QQmlProperty colorProperty READ colorProperty CONSTANT) + Q_PROPERTY(QQmlProperty invalidProperty READ invalidProperty CONSTANT) public: MyTypeObject() : @@ -173,6 +177,10 @@ public: QVariant variant() const { return sizef(); } + QQmlProperty colorProperty() { return QQmlProperty(this, "color"); } + + QQmlProperty invalidProperty() const { return QQmlProperty(); } + void emitRunScript() { emit runScript(); } signals: diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index 3e9047cc5a..7c75743311 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -67,6 +67,7 @@ private slots: void color(); void variant(); void locale(); + void qmlproperty(); void bindingAssignment(); void bindingRead(); @@ -360,6 +361,20 @@ void tst_qqmlvaluetypes::locale() } } +void tst_qqmlvaluetypes::qmlproperty() +{ + QQmlComponent component(&engine, testFileUrl("qmlproperty_read.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != nullptr); + + QCOMPARE(object->property("colorPropertyObject").value<QObject *>(), object); + QCOMPARE(object->property("colorPropertyName").toString(), "color"); + QCOMPARE(object->property("invalidPropertyObject").value<QObject *>(), nullptr); + QCOMPARE(object->property("invalidPropertyName").toString(), ""); + + delete object; +} + void tst_qqmlvaluetypes::sizereadonly() { { diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml b/tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml new file mode 100644 index 0000000000..51020c185e --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +QtObject { + // Inputs + + id: root + + property string writeURL + property string readURL + // Outputs + property bool writeDone: false + property variant readResult + + Component.onCompleted: { + // PUT + var xhrWrite = new XMLHttpRequest; + xhrWrite.open("PUT", writeURL); + xhrWrite.onreadystatechange = function() { + if (xhrWrite.readyState === XMLHttpRequest.DONE) + writeDone = true; + }; + xhrWrite.send("Test-String"); + // GET + var xhrRead = new XMLHttpRequest; + xhrRead.open("GET", readURL); + xhrRead.onreadystatechange = function() { + if (xhrRead.readyState === XMLHttpRequest.DONE) + readResult = xhrRead.responseText; + }; + xhrRead.send(); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 6cf80ccfdb..ae794e76a9 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -35,6 +35,13 @@ #include <QThread> #include <QMutex> #include <QWaitCondition> +#include <QTemporaryFile> + +#if QT_CONFIG(process) +#include <QProcess> +#include <QProcessEnvironment> +#endif + #include "testhttpserver.h" #include "../../shared/util.h" @@ -45,6 +52,8 @@ public: tst_qqmlxmlhttprequest() {} private slots: + void initTestCase(); + void domExceptionCodes(); void callbackException(); void callbackException_data(); @@ -97,6 +106,14 @@ private slots: void nonUtf8(); void nonUtf8_data(); + void sendFileRequest(); + +#if QT_CONFIG(process) + void sendFileRequestNotSet(); + void sendFileRequestNoWrite(); + void sendFileRequestNoRead(); +#endif + // WebDAV void sendPropfind(); void sendPropfind_data(); @@ -119,13 +136,27 @@ private slots: void stateChangeCallingContext(); private: - QQmlEngine engine; + void doFileRequest(std::function<void(QObject *component, QTemporaryFile &writeFile)> verifyFunction); + + QScopedPointer<QQmlEngine> engine; }; +void tst_qqmlxmlhttprequest::initTestCase() +{ + QQmlDataTest::initTestCase(); + + if (!qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + qputenv("QML_XHR_ALLOW_FILE_READ", "1"); + qputenv("QML_XHR_ALLOW_FILE_WRITE", "1"); + } + + engine.reset(new QQmlEngine); +} + // Test that the dom exception codes are correct void tst_qqmlxmlhttprequest::domExceptionCodes() { - QQmlComponent component(&engine, testFileUrl("domExceptionCodes.qml")); + QQmlComponent component(engine.get(), testFileUrl("domExceptionCodes.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -168,8 +199,8 @@ void tst_qqmlxmlhttprequest::callbackException() QString expect = testFileUrl("callbackException.qml").toString() + ":"+QString::number(line)+": Error: Exception from Callback"; QTest::ignoreMessage(QtWarningMsg, expect.toLatin1()); - QQmlComponent component(&engine, testFileUrl("callbackException.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("callbackException.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); object->setProperty("which", which); @@ -182,7 +213,7 @@ void tst_qqmlxmlhttprequest::callbackException() // ### WebKit does not do this, but it seems to fit the standard and QML better void tst_qqmlxmlhttprequest::staticStateValues() { - QQmlComponent component(&engine, testFileUrl("staticStateValues.qml")); + QQmlComponent component(engine.get(), testFileUrl("staticStateValues.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -196,7 +227,7 @@ void tst_qqmlxmlhttprequest::staticStateValues() // Test that the state value properties on instances have the correct values. void tst_qqmlxmlhttprequest::instanceStateValues() { - QQmlComponent component(&engine, testFileUrl("instanceStateValues.qml")); + QQmlComponent component(engine.get(), testFileUrl("instanceStateValues.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -210,7 +241,7 @@ void tst_qqmlxmlhttprequest::instanceStateValues() // Test calling constructor void tst_qqmlxmlhttprequest::constructor() { - QQmlComponent component(&engine, testFileUrl("constructor.qml")); + QQmlComponent component(engine.get(), testFileUrl("constructor.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -221,7 +252,7 @@ void tst_qqmlxmlhttprequest::constructor() // Test that all the properties are set correctly before any request is sent void tst_qqmlxmlhttprequest::defaultState() { - QQmlComponent component(&engine, testFileUrl("defaultState.qml")); + QQmlComponent component(engine.get(), testFileUrl("defaultState.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -248,8 +279,8 @@ void tst_qqmlxmlhttprequest::open() url = server.urlString(url); } - QQmlComponent component(&engine, qmlFile); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), qmlFile); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", url); component.completeCreate(); @@ -281,7 +312,7 @@ void tst_qqmlxmlhttprequest::open_data() // Test that calling XMLHttpRequest.open() with an invalid method raises an exception void tst_qqmlxmlhttprequest::open_invalid_method() { - QQmlComponent component(&engine, testFileUrl("open_invalid_method.qml")); + QQmlComponent component(engine.get(), testFileUrl("open_invalid_method.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -330,8 +361,8 @@ void tst_qqmlxmlhttprequest::open_sync() { TestThreadedHTTPServer server(testFileUrl("open_network.expect"), testFileUrl("open_network.reply"), testFileUrl("testdocument.html")); - QQmlComponent component(&engine, testFileUrl("open_sync.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("open_sync.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.serverBaseUrl.resolved(QStringLiteral("/testdocument.html")).toString()); component.completeCreate(); @@ -343,7 +374,7 @@ void tst_qqmlxmlhttprequest::open_sync() void tst_qqmlxmlhttprequest::open_arg_count() { { - QQmlComponent component(&engine, testFileUrl("open_arg_count.1.qml")); + QQmlComponent component(engine.get(), testFileUrl("open_arg_count.1.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -351,7 +382,7 @@ void tst_qqmlxmlhttprequest::open_arg_count() } { - QQmlComponent component(&engine, testFileUrl("open_arg_count.2.qml")); + QQmlComponent component(engine.get(), testFileUrl("open_arg_count.2.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -368,8 +399,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader() testFileUrl("setRequestHeader.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -386,8 +417,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_caseInsensitive() testFileUrl("setRequestHeader.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader_caseInsensitive.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_caseInsensitive.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -397,7 +428,7 @@ void tst_qqmlxmlhttprequest::setRequestHeader_caseInsensitive() // Test setting headers before open() throws exception void tst_qqmlxmlhttprequest::setRequestHeader_unsent() { - QQmlComponent component(&engine, testFileUrl("setRequestHeader_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_unsent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -443,8 +474,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_illegalName() testFileUrl("open_network.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader_illegalName.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_illegalName.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("header", name); @@ -469,8 +500,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_sent() testFileUrl("open_network.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader_sent.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_sent.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -483,7 +514,7 @@ void tst_qqmlxmlhttprequest::setRequestHeader_sent() // Invalid arg count throws exception void tst_qqmlxmlhttprequest::setRequestHeader_args() { - QQmlComponent component(&engine, testFileUrl("setRequestHeader_args.qml")); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_args.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -493,7 +524,7 @@ void tst_qqmlxmlhttprequest::setRequestHeader_args() // Test that calling send() in UNSENT state throws an exception void tst_qqmlxmlhttprequest::send_unsent() { - QQmlComponent component(&engine, testFileUrl("send_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("send_unsent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -503,7 +534,7 @@ void tst_qqmlxmlhttprequest::send_unsent() // Test attempting to resend a sent request throws an exception void tst_qqmlxmlhttprequest::send_alreadySent() { - QQmlComponent component(&engine, testFileUrl("send_alreadySent.qml")); + QQmlComponent component(engine.get(), testFileUrl("send_alreadySent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -521,8 +552,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("send_ignoreData.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("reqType", "GET"); object->setProperty("url", server.urlString("/testdocument.html")); @@ -538,8 +569,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("send_ignoreData.reply"), QUrl())); - QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("reqType", "HEAD"); object->setProperty("url", server.urlString("/testdocument.html")); @@ -555,8 +586,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("send_ignoreData.reply"), QUrl())); - QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("reqType", "DELETE"); object->setProperty("url", server.urlString("/testdocument.html")); @@ -578,8 +609,8 @@ void tst_qqmlxmlhttprequest::send_withdata() testFileUrl("send_data.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl(file_qml)); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(file_qml)); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -615,8 +646,8 @@ void tst_qqmlxmlhttprequest::send_options() testFileUrl(file_reply), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl(file_qml)); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(file_qml)); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); QString url = server.baseUrl().toString(); if (url_suffix != "/") @@ -652,8 +683,8 @@ void tst_qqmlxmlhttprequest::send_patch() // the content of response file will be ignored due to 204 status code testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("send_patch.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_patch.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/qqmlxmlhttprequest.cpp")); component.completeCreate(); @@ -666,8 +697,8 @@ void tst_qqmlxmlhttprequest::send_patch() // Test abort() has no effect in unsent state void tst_qqmlxmlhttprequest::abort_unsent() { - QQmlComponent component(&engine, testFileUrl("abort_unsent.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort_unsent.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); component.completeCreate(); @@ -685,8 +716,8 @@ void tst_qqmlxmlhttprequest::abort_unsent() // Test abort() cancels an open (but unsent) request void tst_qqmlxmlhttprequest::abort_opened() { - QQmlComponent component(&engine, testFileUrl("abort_opened.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort_opened.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); component.completeCreate(); @@ -710,8 +741,8 @@ void tst_qqmlxmlhttprequest::abort() testFileUrl("abort.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("abort.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); const QUrl url = server.url("/testdocument.html"); QUrl dummyUrl = url; @@ -767,7 +798,7 @@ void tst_qqmlxmlhttprequest::getResponseHeader() // Test getResponseHeader throws an exception in an invalid state void tst_qqmlxmlhttprequest::getResponseHeader_unsent() { - QQmlComponent component(&engine, testFileUrl("getResponseHeader_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getResponseHeader_unsent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -777,7 +808,7 @@ void tst_qqmlxmlhttprequest::getResponseHeader_unsent() // Test getResponseHeader throws an exception in an invalid state void tst_qqmlxmlhttprequest::getResponseHeader_sent() { - QQmlComponent component(&engine, testFileUrl("getResponseHeader_sent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getResponseHeader_sent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -787,7 +818,7 @@ void tst_qqmlxmlhttprequest::getResponseHeader_sent() // Invalid arg count throws exception void tst_qqmlxmlhttprequest::getResponseHeader_args() { - QQmlComponent component(&engine, testFileUrl("getResponseHeader_args.qml")); + QQmlComponent component(engine.get(), testFileUrl("getResponseHeader_args.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -827,7 +858,7 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders() // Test getAllResponseHeaders throws an exception in an invalid state void tst_qqmlxmlhttprequest::getAllResponseHeaders_unsent() { - QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getAllResponseHeaders_unsent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -837,7 +868,7 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_unsent() // Test getAllResponseHeaders throws an exception in an invalid state void tst_qqmlxmlhttprequest::getAllResponseHeaders_sent() { - QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_sent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getAllResponseHeaders_sent.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -847,7 +878,7 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_sent() // Invalid arg count throws exception void tst_qqmlxmlhttprequest::getAllResponseHeaders_args() { - QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_args.qml")); + QQmlComponent component(engine.get(), testFileUrl("getAllResponseHeaders_args.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -862,8 +893,8 @@ void tst_qqmlxmlhttprequest::getBinaryData() testFileUrl("receive_binary_data.reply"), testFileUrl("qml_logo.png"))); - QQmlComponent component(&engine, testFileUrl("receiveBinaryData.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("receiveBinaryData.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/gml_logo.png")); component.completeCreate(); @@ -881,8 +912,8 @@ void tst_qqmlxmlhttprequest::getJsonData() testFileUrl("receive_binary_data.reply"), testFileUrl("json.data"))); - QQmlComponent component(&engine, testFileUrl("receiveJsonData.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("receiveJsonData.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/json.data")); component.completeCreate(); @@ -901,8 +932,8 @@ void tst_qqmlxmlhttprequest::status() replyUrl, testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("status.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("status.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("expectedStatus", status); @@ -943,8 +974,8 @@ void tst_qqmlxmlhttprequest::statusText() replyUrl, testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("statusText.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("statusText.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("expectedStatus", statusText); @@ -983,8 +1014,8 @@ void tst_qqmlxmlhttprequest::responseText() replyUrl, bodyUrl)); - QQmlComponent component(&engine, testFileUrl("responseText.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("responseText.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("expectedText", responseText); @@ -1020,7 +1051,7 @@ void tst_qqmlxmlhttprequest::nonUtf8() QFETCH(QString, responseText); QFETCH(QString, xmlRootNodeValue); - QQmlComponent component(&engine, testFileUrl("utf16.qml")); + QQmlComponent component(engine.get(), testFileUrl("utf16.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1053,6 +1084,163 @@ void tst_qqmlxmlhttprequest::nonUtf8_data() QTest::newRow("responseXML") << "utf16.xml" << "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone='yes'?>\n<root>\n" + uc + "\n</root>\n" << QString('\n' + uc + '\n'); } +static const QString testString = QStringLiteral("Test-String"); + +void tst_qqmlxmlhttprequest::doFileRequest(std::function<void(QObject *component, QTemporaryFile &writeFile)> verifyFunction) +{ + // Create test files + QTemporaryFile writeFile; + QTemporaryFile readFile; + + writeFile.open(); + writeFile.close(); + + QVERIFY(readFile.open()); + readFile.write(testString.toUtf8()); + readFile.close(); + + // Avoid cached environment variables + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("file_request.qml")); + + const QVariantMap properties = { + {"writeURL", QUrl::fromLocalFile(writeFile.fileName()).toString()}, + {"readURL", QUrl::fromLocalFile(readFile.fileName()).toString()} + }; + + QScopedPointer<QObject> object(component.createWithInitialProperties(properties, engine.rootContext())); + QVERIFY(!object.isNull()); + + verifyFunction(object.get(), writeFile); +} + +// Test file:// requests +void tst_qqmlxmlhttprequest::sendFileRequest() +{ + // Test with both writing and reading allowed + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + QTRY_COMPARE(object->property("readResult").toString(), testString); + + QTRY_VERIFY(object->property("writeDone").toBool()); + + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), testString); + writeFile.close(); + }); +} + +#if QT_CONFIG(process) +void tst_qqmlxmlhttprequest::sendFileRequestNotSet() { + if (qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + // Test with no settings + // Should just result in warnings in Qt 5 + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + QTRY_COMPARE(object->property("readResult").toString(), testString); + + QTRY_VERIFY(object->property("writeDone").toBool()); + + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), testString); + writeFile.close(); + }); + return; + } + + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("sendFileRequestNotSet"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("TEST_CUSTOM_PERMISSIONS"), QLatin1String("1")); + env.remove("QML_XHR_ALLOW_FILE_WRITE"); + env.remove("QML_XHR_ALLOW_FILE_READ"); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + + // Check exit code + QCOMPARE(child.exitCode(), 0); + + // Check if all warnings were printed + QString output = QString::fromUtf8(child.readAllStandardOutput()); + + + const QString readingWarning = QLatin1String( + "XMLHttpRequest: Using GET on a local file is dangerous " + "and will be disabled by default in a future Qt version." + "Set QML_XHR_ALLOW_FILE_READ to 1 if you wish to continue using this feature."); + + const QString writingWarning = QLatin1String( + "XMLHttpRequest: Using PUT on a local file is dangerous " + "and will be disabled by default in a future Qt version." + "Set QML_XHR_ALLOW_FILE_WRITE to 1 if you wish to continue using this feature."); + + QVERIFY(output.contains(readingWarning)); + QVERIFY(output.contains(writingWarning)); +} +#endif + +#if QT_CONFIG(process) +void tst_qqmlxmlhttprequest::sendFileRequestNoWrite() { + if (qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + // Test with no writing enabled + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + QTRY_COMPARE(object->property("readResult").toString(), testString); + + // Check that the file stays empty + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), ""); + writeFile.close(); + }); + return; + } + + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("sendFileRequestNoWrite"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("TEST_CUSTOM_PERMISSIONS"), QLatin1String("1")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_WRITE"), QLatin1String("0")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_READ"), QLatin1String("1")); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + QCOMPARE(child.exitCode(), 0); +} +#endif + +#if QT_CONFIG(process) +void tst_qqmlxmlhttprequest::sendFileRequestNoRead() { + if (qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + // Test with no reading enabled + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + // Check that the write happens + QTRY_VERIFY(object->property("writeDone").toBool()); + + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), testString); + writeFile.close(); + + // Verify that the read has not yielded any value + QVERIFY(object->property("readResult").isNull()); + }); + return; + } + + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("sendFileRequestNoRead"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("TEST_CUSTOM_PERMISSIONS"), QLatin1String("1")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_WRITE"), QLatin1String("1")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_READ"), QLatin1String("0")); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + QCOMPARE(child.exitCode(), 0); +} +#endif + void tst_qqmlxmlhttprequest::sendPropfind() { const QString prefix = "WebDAV//"; @@ -1070,8 +1258,8 @@ void tst_qqmlxmlhttprequest::sendPropfind() testFileUrl(prefix + replyHeader), testFileUrl(prefix + replyBody))); - QQmlComponent component(&engine, testFileUrl(prefix + qml)); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(prefix + qml)); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString(resource)); component.completeCreate(); @@ -1097,7 +1285,7 @@ void tst_qqmlxmlhttprequest::sendPropfind_data() // throws an exception void tst_qqmlxmlhttprequest::invalidMethodUsage() { - QQmlComponent component(&engine, testFileUrl("invalidMethodUsage.qml")); + QQmlComponent component(engine.get(), testFileUrl("invalidMethodUsage.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1124,8 +1312,8 @@ void tst_qqmlxmlhttprequest::redirects() server.addRedirect("redirect.html", server.urlString("/redirecttarget.html")); server.serveDirectory(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("redirects.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirects.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); object->setProperty("expectedText", ""); @@ -1141,8 +1329,8 @@ void tst_qqmlxmlhttprequest::redirects() server.addRedirect("redirect.html", server.urlString("/redirectmissing.html")); server.serveDirectory(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("redirectError.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirectError.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); object->setProperty("expectedText", ""); @@ -1158,8 +1346,8 @@ void tst_qqmlxmlhttprequest::redirects() server.addRedirect("redirect.html", server.urlString("/redirect.html")); server.serveDirectory(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("redirectRecur.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirectRecur.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); object->setProperty("expectedText", ""); @@ -1177,7 +1365,7 @@ void tst_qqmlxmlhttprequest::redirects() void tst_qqmlxmlhttprequest::responseXML_invalid() { - QQmlComponent component(&engine, testFileUrl("responseXML_invalid.qml")); + QQmlComponent component(engine.get(), testFileUrl("responseXML_invalid.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1189,7 +1377,7 @@ void tst_qqmlxmlhttprequest::responseXML_invalid() // Test the Document DOM element void tst_qqmlxmlhttprequest::document() { - QQmlComponent component(&engine, testFileUrl("document.qml")); + QQmlComponent component(engine.get(), testFileUrl("document.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1201,7 +1389,7 @@ void tst_qqmlxmlhttprequest::document() // Test the Element DOM element void tst_qqmlxmlhttprequest::element() { - QQmlComponent component(&engine, testFileUrl("element.qml")); + QQmlComponent component(engine.get(), testFileUrl("element.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1213,7 +1401,7 @@ void tst_qqmlxmlhttprequest::element() // Test the Attr DOM element void tst_qqmlxmlhttprequest::attr() { - QQmlComponent component(&engine, testFileUrl("attr.qml")); + QQmlComponent component(engine.get(), testFileUrl("attr.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1225,7 +1413,7 @@ void tst_qqmlxmlhttprequest::attr() // Test the Text DOM element void tst_qqmlxmlhttprequest::text() { - QQmlComponent component(&engine, testFileUrl("text.qml")); + QQmlComponent component(engine.get(), testFileUrl("text.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1238,7 +1426,7 @@ void tst_qqmlxmlhttprequest::text() // Test the CDataSection DOM element void tst_qqmlxmlhttprequest::cdata() { - QQmlComponent component(&engine, testFileUrl("cdata.qml")); + QQmlComponent component(engine.get(), testFileUrl("cdata.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1285,8 +1473,8 @@ void tst_qqmlxmlhttprequest::stateChangeCallingContext() QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); - QQmlComponent component(&engine, testFileUrl("stateChangeCallingContext.qml")); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("stateChangeCallingContext.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("serverBaseUrl", server.baseUrl().toString()); component.completeCreate(); diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp index ae99e35467..dccd3951b3 100644 --- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp +++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp @@ -76,6 +76,7 @@ private slots: void sortCaseSensitive_data(); void sortCaseSensitive(); void updateProperties(); + void importBothVersions(); private: void checkNoErrors(const QQmlComponent& component); QQmlEngine engine; @@ -467,6 +468,22 @@ void tst_qquickfolderlistmodel::updateProperties() QCOMPARE(showHidden.toBool(), true); } +void tst_qquickfolderlistmodel::importBothVersions() +{ + { + QQmlComponent component(&engine, testFileUrl("sortReversed.qml")); + checkNoErrors(component); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); + } + { + QQmlComponent component(&engine, testFileUrl("qrc.qml")); + checkNoErrors(component); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); + } +} + QTEST_MAIN(tst_qquickfolderlistmodel) #include "tst_qquickfolderlistmodel.moc" diff --git a/tests/auto/qml/qquickworkerscript/data/BaseWorker.qml b/tests/auto/qml/qquickworkerscript/data/BaseWorker.qml index 0ac56d9b66..1d3420e186 100644 --- a/tests/auto/qml/qquickworkerscript/data/BaseWorker.qml +++ b/tests/auto/qml/qquickworkerscript/data/BaseWorker.qml @@ -1,11 +1,14 @@ import QtQuick 2.0 +import QtQml.WorkerScript 2.15 WorkerScript { id: worker property variant response + property bool readyChangedCalled : false signal done() + signal ready() function testSend(value) { worker.sendMessage(value) @@ -20,5 +23,7 @@ WorkerScript { worker.response = messageObject worker.done() } + + onReadyChanged: worker.readyChangedCalled = true } diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp index d11e7bde4b..2f79f7157f 100644 --- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp +++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp @@ -47,6 +47,7 @@ public: tst_QQuickWorkerScript() {} private slots: void source(); + void ready(); void messaging(); void messaging_data(); void messaging_sendQObjectList(); @@ -105,6 +106,22 @@ void tst_QQuickWorkerScript::source() qApp->processEvents(); } +void tst_QQuickWorkerScript::ready() +{ + QQmlComponent component(&m_engine, testFileUrl("worker.qml")); + QScopedPointer<QQuickWorkerScript>worker(qobject_cast<QQuickWorkerScript*>(component.create())); + QVERIFY(worker != nullptr); + + const QMetaObject *mo = worker->metaObject(); + + QTRY_VERIFY(worker->ready()); + + QVariant readyChangedCalled = mo->property(mo->indexOfProperty("readyChangedCalled")).read(worker.data()).value<QVariant>(); + + QVERIFY(!readyChangedCalled.isNull()); + QVERIFY(readyChangedCalled.toBool()); +} + void tst_QQuickWorkerScript::messaging() { QFETCH(QVariant, value); @@ -152,6 +169,7 @@ void tst_QQuickWorkerScript::messaging_data() QRegExp::RegExp2)); QTest::newRow("regularexpression") << QVariant::fromValue(QRegularExpression( "^\\d\\d?$", QRegularExpression::CaseInsensitiveOption)); + QTest::newRow("url") << QVariant::fromValue(QUrl("http://example.com/foo/bar")); } void tst_QQuickWorkerScript::messaging_sendQObjectList() |