diff options
Diffstat (limited to 'tests/auto/qml')
510 files changed, 20971 insertions, 6644 deletions
diff --git a/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp b/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp index c81fd37f07..7e19e925b6 100644 --- a/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp +++ b/tests/auto/qml/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp @@ -53,7 +53,7 @@ private slots: class TestableQAbstractAnimation : public QAbstractAnimationJob { public: - TestableQAbstractAnimation() : m_duration(10) {} + TestableQAbstractAnimation() {} virtual ~TestableQAbstractAnimation() {}; int duration() const { return m_duration; } @@ -61,7 +61,7 @@ public: void setDuration(int duration) { m_duration = duration; } private: - int m_duration; + int m_duration = 10; }; class DummyQAnimationGroup : public QAnimationGroupJob diff --git a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp index b7f8280c5b..6bd8c2a2e0 100644 --- a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp @@ -71,10 +71,7 @@ class UncontrolledAnimation : public QObject, public QAbstractAnimationJob { Q_OBJECT public: - UncontrolledAnimation() - : id(0) - { - } + UncontrolledAnimation() { } int duration() const { return -1; /* not time driven */ } @@ -96,7 +93,7 @@ protected: } private: - int id; + int id = 0; }; class StateChangeListener: public QAnimationJobChangeListener @@ -264,15 +261,18 @@ void tst_QAnimationGroupJob::addChildTwice() { QAbstractAnimationJob *subGroup; QAbstractAnimationJob *subGroup2; - QAnimationGroupJob *parent = new QSequentialAnimationGroupJob(); + auto *parent = new QSequentialAnimationGroupJob(); subGroup = new QAbstractAnimationJob; parent->appendAnimation(subGroup); parent->appendAnimation(subGroup); - QVERIFY(parent->firstChild() && !parent->firstChild()->nextSibling()); + QVERIFY(parent->firstChild()); + QVERIFY(!parent->firstChild()->nextSibling()); + QVERIFY(!parent->firstChild()->previousSibling()); parent->clear(); + QCOMPARE(parent->currentAnimation(), nullptr); QVERIFY(!parent->firstChild()); // adding the same item twice to a group will remove the item from its current position diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp index 476ad2e955..a8bcadbc84 100644 --- a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp @@ -89,7 +89,6 @@ class UncontrolledAnimation : public QObject, public QAbstractAnimationJob Q_OBJECT public: UncontrolledAnimation() - : id(0) { } @@ -113,7 +112,7 @@ protected: } private: - int id; + int id = 0; }; class StateChangeListener: public QAnimationJobChangeListener @@ -133,14 +132,14 @@ public: class FinishedListener: public QAnimationJobChangeListener { public: - FinishedListener() : m_count(0) {} + FinishedListener() {} virtual void animationFinished(QAbstractAnimationJob *) { ++m_count; } void clear() { m_count = 0; } int count() { return m_count; } private: - int m_count; + int m_count = 0; }; void tst_QParallelAnimationGroupJob::setCurrentTime() @@ -685,10 +684,10 @@ void tst_QParallelAnimationGroupJob::stopUncontrolledAnimations() } struct AnimState { - AnimState(int time = -1) : time(time), state(-1) {} + AnimState(int time = -1) : time(time) {} AnimState(int time, int state) : time(time), state(state) {} int time; - int state; + int state = -1; }; #define Running QAbstractAnimationJob::Running @@ -848,7 +847,7 @@ void tst_QParallelAnimationGroupJob::addAndRemoveDuration() QCOMPARE(group.duration(), 250); group.removeAnimation(test); // remove the last one (with duration = 250) - QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(0)); + QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(nullptr)); QCOMPARE(group.duration(), 0); delete test; } diff --git a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp index ff295c5409..8f6b6a2ab7 100644 --- a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp +++ b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp @@ -39,17 +39,14 @@ static const char winTimerError[] = "On windows, consistent timing is not workin class TestablePauseAnimation : public QPauseAnimationJob { public: - TestablePauseAnimation() - : m_updateCurrentTimeCount(0) - { - } + TestablePauseAnimation() { } TestablePauseAnimation(int duration) : QPauseAnimationJob(duration), m_updateCurrentTimeCount(0) { } - int m_updateCurrentTimeCount; + int m_updateCurrentTimeCount = 0; protected: void updateCurrentTime(int currentTime) { diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/BLACKLIST b/tests/auto/qml/animation/qsequentialanimationgroupjob/BLACKLIST deleted file mode 100644 index 2afe6074d7..0000000000 --- a/tests/auto/qml/animation/qsequentialanimationgroupjob/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[finishWithUncontrolledAnimation] -* diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp index ee5db3e75a..57b0905a8a 100644 --- a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp @@ -95,7 +95,7 @@ class TestValueAnimation : public TestAnimation { public: TestValueAnimation(int duration = 250) - : TestAnimation(duration), start(0), end(0), value(0) {} + : TestAnimation(duration) {} void updateCurrentTime(int msecs) { @@ -105,8 +105,8 @@ public: value = start + (end - start) * (qreal(msecs) / duration()); } - qreal start, end; - qreal value; + qreal start = 0, end = 0; + qreal value = 0; }; class UncontrolledAnimation : public QObject, public QAbstractAnimationJob @@ -140,14 +140,14 @@ public: class FinishedListener: public QAnimationJobChangeListener { public: - FinishedListener() : m_count(0) {} + FinishedListener() {} virtual void animationFinished(QAbstractAnimationJob *) { ++m_count; } void clear() { m_count = 0; } int count() { return m_count; } private: - int m_count; + int m_count = 0; }; void tst_QSequentialAnimationGroupJob::setCurrentTime() diff --git a/tests/auto/qml/bindingdependencyapi/bindingdependencyapi.pro b/tests/auto/qml/bindingdependencyapi/bindingdependencyapi.pro new file mode 100644 index 0000000000..430d87ab5b --- /dev/null +++ b/tests/auto/qml/bindingdependencyapi/bindingdependencyapi.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TARGET = tst_bindingdependencyapi +macos:CONFIG -= app_bundle + +SOURCES += tst_bindingdependencyapi.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp new file mode 100644 index 0000000000..82c997a5b8 --- /dev/null +++ b/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <private/qqmldata_p.h> +#include <private/qqmlbinding_p.h> +#include <QtQuick/private/qquicktext_p.h> +#include <QtQuick/private/qquickrectangle_p.h> +#include "../../shared/util.h" +#include <qqmlcontext.h> + +class tst_bindingdependencyapi : public QObject +{ + Q_OBJECT +public: + tst_bindingdependencyapi(); + +private slots: + void testSingleDep_data(); + void testSingleDep(); + void testManyDeps_data(); + void testManyDeps(); + void testConditionalDependencies_data(); + void testConditionalDependencies(); + void testBindingLoop(); + +private: + bool findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value); +}; + +tst_bindingdependencyapi::tst_bindingdependencyapi() +{ +} + + +void tst_bindingdependencyapi::testSingleDep_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QString>("referencedObjectName"); + + QTest::addRow("context property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { text: labelText }\n" + "}") << "rect"; + + QTest::addRow("scope property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: \"I am wrong!\"\n" + "Text {\n" + "objectName: \"text\"\n" + "property string labelText: \"Hello world!\"\n" + "text: labelText\n" + "}\n" + "}") << "text"; + + QTest::addRow("id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { text: rect.labelText }\n" + "}") << "rect"; + + QTest::addRow("dynamic context property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return labelText; }); }\n" + "}") << "rect"; + + QTest::addRow("dynamic scope property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: \"I am wrong!\"\n" + "Text {\n" + "objectName: \"text\"\n" + "property string labelText: \"Hello world!\"\n" + "Component.onCompleted: text = Qt.binding(function() { return labelText; });\n" + "}\n" + "}") << "text"; + + QTest::addRow("dynamic id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return rect.labelText; }); }\n" + "}") << "rect"; +} + +void tst_bindingdependencyapi::testSingleDep() +{ + QFETCH(QByteArray, code); + QFETCH(QString, referencedObjectName); + + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(code, QUrl()); + QObject *rect = c.create(); + QTest::qWait(10); + QVERIFY(rect != nullptr); + QObject *text = rect->findChildren<QQuickText *>().front(); + + QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + auto dependency = dependencies.front(); + QVERIFY(dependency.isValid()); + QCOMPARE(quintptr(dependency.object()), quintptr(referencedObject)); + QCOMPARE(dependency.property().name(), "labelText"); + QCOMPARE(dependency.read().toString(), QStringLiteral("Hello world!")); + QCOMPARE(dependency, QQmlProperty(referencedObject, "labelText")); + + delete rect; +} + +bool tst_bindingdependencyapi::findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value) +{ + auto dep = std::find_if(properties.cbegin(), properties.cend(), [&](const QQmlProperty &dep) { + return dep.object() == obj + && dep.property().name() == propertyName + && dep.read() == value; + }); + if (dep == properties.cend()) { + qWarning() << "Searched for property with:" << "{ object:" << obj << ", propertyName:" << propertyName << ", value:" << value << "}" << "but only found:"; + for (auto dep : properties) { + qWarning() << "{ object:" << dep.object() << ", propertyName:" << dep.property().name() << ", value:" << dep.read() << "}"; + } + return false; + } + return true; +} + +void tst_bindingdependencyapi::testManyDeps_data() +{ + QTest::addColumn<QByteArray>("code"); + + QTest::addRow("permanent binding") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: 'rect'\n" + "property string name: 'world'\n" + "Text {\n" + "text: config.helloWorldTemplate.arg(greeting).arg(rect.name) \n" + "property string greeting: 'Hello'\n" + "}\n" + "QtObject { id: config; objectName: 'config'; property string helloWorldTemplate: '%1 %2!' }\n" + "}"); + + QTest::addRow("dynamic binding") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: 'rect'\n" + "property string name: 'world'\n" + "Text {\n" + "Component.onCompleted: text = Qt.binding(function() { return config.helloWorldTemplate.arg(greeting).arg(rect.name); }); \n" + "property string greeting: 'Hello'\n" + "}\n" + "QtObject { id: config; objectName: 'config'; property string helloWorldTemplate: '%1 %2!' }\n" + "}"); +} + +void tst_bindingdependencyapi::testManyDeps() +{ + QFETCH(QByteArray, code); + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(code, QUrl()); + QObject *rect = c.create(); + if (c.isError()) { + qWarning() << c.errorString(); + } + QTest::qWait(100); + QVERIFY(rect != nullptr); + QObject *text = rect->findChildren<QQuickText *>().front(); + QObject *configObj = rect->findChild<QObject *>("config"); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 3); + + QVERIFY(findProperties(dependencies, rect, "name", "world")); + QVERIFY(findProperties(dependencies, text, "greeting", "Hello")); + QVERIFY(findProperties(dependencies, configObj, "helloWorldTemplate", "%1 %2!")); + + delete rect; +} + +void tst_bindingdependencyapi::testConditionalDependencies_data() +{ + QTest::addColumn<QByteArray>("code"); + QTest::addColumn<QString>("referencedObjectName"); + + QTest::addRow("id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Text { text: rect.haveDep ? rect.labelText : '' }\n" + "}") << "rect"; + + QTest::addRow("dynamic context property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "objectName: \"rect\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return haveDep ? labelText : ''; }); }\n" + "}") << "rect"; + + QTest::addRow("dynamic scope property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: \"I am wrong!\"\n" + "Text {\n" + "objectName: \"text\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Component.onCompleted: text = Qt.binding(function() { return haveDep ? labelText : ''; });\n" + "}\n" + "}") << "text"; + + QTest::addRow("dynamic id object property") + << QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "id: rect\n" + "objectName: \"rect\"\n" + "property bool haveDep: false\n" + "property string labelText: \"Hello world!\"\n" + "Text { Component.onCompleted: text = Qt.binding(function() { return rect.haveDep ? rect.labelText : ''; }); }\n" + "}") << "rect"; +} + +void tst_bindingdependencyapi::testConditionalDependencies() +{ + QFETCH(QByteArray, code); + QFETCH(QString, referencedObjectName); + + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(code, QUrl()); + QObject *rect = c.create(); + QTest::qWait(10); + QVERIFY(rect != nullptr); + QObject *text = rect->findChildren<QQuickText *>().front(); + + QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(referencedObjectName); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false)); + + referencedObject->setProperty("haveDep", true); + dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 2); + QVERIFY(findProperties(dependencies, referencedObject, "haveDep", true)); + QVERIFY(findProperties(dependencies, referencedObject, "labelText", "Hello world!")); + + referencedObject->setProperty("haveDep", false); + dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false)); + + delete rect; +} + +void tst_bindingdependencyapi::testBindingLoop() +{ + QQmlEngine engine; + QQmlComponent c(&engine); + c.setData(QByteArray("import QtQuick 2.0\n" + "Rectangle {\n" + "property string labelText: label.text\n" + "Text {\n" + "id: label\n" + "text: labelText\n" + "}\n" + "}"), QUrl()); + QObject *rect = c.create(); + if (c.isError()) { + qWarning() << c.errorString(); + } + QTest::qWait(100); + QVERIFY(rect != nullptr); + QObject *text = rect->findChildren<QQuickText *>().front(); + + auto data = QQmlData::get(text); + QVERIFY(data); + auto b = data->bindings; + QVERIFY(b); + auto binding = dynamic_cast<QQmlBinding*>(b); + QVERIFY(binding); + auto dependencies = binding->dependencies(); + QCOMPARE(dependencies.size(), 1); + auto dependency = dependencies.front(); + QVERIFY(dependency.isValid()); + QCOMPARE(quintptr(dependency.object()), quintptr(rect)); + QCOMPARE(dependency.property().name(), "labelText"); + + delete rect; +} + +QTEST_MAIN(tst_bindingdependencyapi) + +#include "tst_bindingdependencyapi.moc" diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index a50411e18b..5c328fbfcc 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -1,5 +1,7 @@ TEMPLATE = subdirs +SUBDIRS += qqmldebugjsserver + PUBLICTESTS += \ qdebugmessageservice \ qqmlenginedebugservice \ @@ -10,7 +12,9 @@ PUBLICTESTS += \ qqmlenginedebuginspectorintegrationtest \ qqmlenginecontrol \ qqmldebuggingenabler \ - qqmlnativeconnector + qqmlnativeconnector \ + qqmldebugprocess \ + qqmlpreview PRIVATETESTS += \ qqmldebugclient \ @@ -20,6 +24,9 @@ PRIVATETESTS += \ SUBDIRS += $$PUBLICTESTS +qqmldebugjs.depends = qqmldebugjsserver +qqmlprofilerservice.depends = qqmldebugjsserver + qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS diff --git a/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro b/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro index 6c729ab235..3f2c0ca390 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro +++ b/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro @@ -1,12 +1,10 @@ CONFIG += testcase TARGET = tst_qdebugmessageservice -QT += qml network testlib gui-private core-private +QT += network testlib gui-private core-private osx:CONFIG -= app_bundle SOURCES += tst_qdebugmessageservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp index f193d3928a..d2cfd3897a 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp +++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp @@ -28,7 +28,6 @@ //QQmlDebugTest #include "debugutil_p.h" -#include "../../../shared/util.h" #include <private/qqmldebugclient_p.h> #include <private/qqmldebugconnection_p.h> @@ -38,31 +37,19 @@ #include <QtCore/qlibraryinfo.h> #include <QtTest/qtest.h> -const char *NORMALMODE = "-qmljsdebugger=port:3777,3787,block"; const char *QMLFILE = "test.qml"; class QQmlDebugMsgClient; -class tst_QDebugMessageService : public QQmlDataTest +class tst_QDebugMessageService : public QQmlDebugTest { Q_OBJECT -public: - tst_QDebugMessageService(); - - void init(); - private slots: - void initTestCase(); - void cleanupTestCase(); - - void cleanup(); - void retrieveDebugOutput(); private: - QQmlDebugProcess *m_process; - QQmlDebugMsgClient *m_client; - QQmlDebugConnection *m_connection; + QList<QQmlDebugClient *> createClients() override; + QPointer<QQmlDebugMsgClient> m_client; }; struct LogEntry { @@ -102,21 +89,12 @@ public: protected: //inherited from QQmlDebugClient - void stateChanged(State state); void messageReceived(const QByteArray &data); signals: - void enabled(); void debugOutput(); }; -void QQmlDebugMsgClient::stateChanged(State state) -{ - if (state == Enabled) { - emit enabled(); - } -} - void QQmlDebugMsgClient::messageReceived(const QByteArray &data) { QPacket ds(connection()->currentDataStreamVersion(), data); @@ -150,73 +128,16 @@ void QQmlDebugMsgClient::messageReceived(const QByteArray &data) } } -tst_QDebugMessageService::tst_QDebugMessageService() -{ -} - -void tst_QDebugMessageService::initTestCase() +QList<QQmlDebugClient *> tst_QDebugMessageService::createClients() { - QQmlDataTest::initTestCase(); - m_process = 0; - m_client = 0; - m_connection = 0; -} - -void tst_QDebugMessageService::cleanupTestCase() -{ - if (m_process) - delete m_process; - - if (m_client) - delete m_client; - - if (m_connection) - delete m_connection; -} - -void tst_QDebugMessageService::init() -{ - m_connection = new QQmlDebugConnection(); - m_process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", this); m_client = new QQmlDebugMsgClient(m_connection); - - m_process->start(QStringList() << QLatin1String(NORMALMODE) << QQmlDataTest::instance()->testFile(QMLFILE)); - QVERIFY2(m_process->waitForSessionStart(), - "Could not launch application, or did not get 'Waiting for connection'."); - - const int port = m_process->debugPort(); - m_connection->connectToHost("127.0.0.1", port); - QVERIFY(m_connection->waitForConnected()); - - if (m_client->state() != QQmlDebugClient::Enabled) - QQmlDebugTest::waitForSignal(m_client, SIGNAL(enabled())); - - QCOMPARE(m_client->state(), QQmlDebugClient::Enabled); -} - -void tst_QDebugMessageService::cleanup() -{ - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << m_process->state(); - qDebug() << "Application Output:" << m_process->output(); - } - if (m_process) - delete m_process; - - if (m_client) - delete m_client; - - if (m_connection) - delete m_connection; - - m_process = 0; - m_client = 0; - m_connection = 0; + return QList<QQmlDebugClient *>({m_client}); } void tst_QDebugMessageService::retrieveDebugOutput() { - init(); + QCOMPARE(QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + QString(), testFile(QMLFILE), true), ConnectSuccess); QTRY_VERIFY(m_client->logBuffer.size() >= 2); diff --git a/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro b/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro index a7c0fa7f8e..dc0120cd87 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro +++ b/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro @@ -4,7 +4,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qpacketprotocol.cpp -INCLUDEPATH += ../shared include(../shared/debugutil.pri) -QT += qml network testlib gui-private core-private +QT += network testlib gui-private core-private diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp index cadc2a7cc4..21ca921304 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp +++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp @@ -60,7 +60,7 @@ private slots: void tst_QPacketProtocol::init() { m_server = new QTcpServer(this); - m_serverConn = 0; + m_serverConn = nullptr; QVERIFY(m_server->listen(QHostAddress("127.0.0.1"))); m_client = new QTcpSocket(this); diff --git a/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro b/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro index 622b373692..673330a3cf 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro +++ b/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro @@ -7,7 +7,6 @@ HEADERS += ../shared/qqmldebugtestservice.h SOURCES += tst_qqmldebugclient.cpp \ ../shared/qqmldebugtestservice.cpp -INCLUDEPATH += ../shared include(../shared/debugutil.pri) DEFINES += QT_QML_DEBUG_NO_WARNING diff --git a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp index 04bc5c3c1a..ffdbf72ded 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp +++ b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp @@ -66,17 +66,15 @@ void tst_QQmlDebugClient::initTestCase() QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); QQmlDebugConnector::setServices(QStringList() << QStringLiteral("tst_QQmlDebugClient::handshake()")); - QTest::ignoreMessage(QtWarningMsg, - "QML debugger: Cannot set plugin key after loading the plugin."); m_service = new QQmlDebugTestService("tst_QQmlDebugClient::handshake()"); foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT); QTest::ignoreMessage(QtDebugMsg, waitingMsg.toLatin1().constData()); @@ -126,7 +124,7 @@ void tst_QQmlDebugClient::state() QQmlDebugClient client2("tst_QQmlDebugClient::state()", m_conn); QCOMPARE(client2.state(), QQmlDebugClient::NotConnected); - QQmlDebugClient client3("tst_QQmlDebugClient::state3()", 0); + QQmlDebugClient client3("tst_QQmlDebugClient::state3()", nullptr); QCOMPARE(client3.state(), QQmlDebugClient::NotConnected); } diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro index f8014f04f4..bd382ebaab 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro @@ -6,8 +6,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmldebuggingenabler.cpp -INCLUDEPATH += ../../shared -include(../../../../shared/util.pri) include(../../shared/debugutil.pri) OTHER_FILES += data/test.qml diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp index 3aa3a5c87e..37118f4bd0 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp @@ -27,11 +27,14 @@ ****************************************************************************/ #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmldebugclient_p.h> #include <private/qqmldebugconnection_p.h> +#include <QtQml/qqmldebug.h> + #include <QtTest/qtest.h> #include <QtCore/qprocess.h> #include <QtCore/qtimer.h> @@ -40,17 +43,11 @@ #include <QtCore/qmutex.h> #include <QtCore/qlibraryinfo.h> -class tst_QQmlDebuggingEnabler : public QQmlDataTest +class tst_QQmlDebuggingEnabler : public QQmlDebugTest { Q_OBJECT - bool init(bool blockMode, bool qmlscene, int portFrom, int portTo); - private slots: - void initTestCase(); - void cleanupTestCase(); - void cleanup(); - void qmlscene_data(); void qmlscene(); void custom_data(); @@ -58,88 +55,8 @@ private slots: private: void data(); - QQmlDebugProcess *process; - QQmlDebugConnection *connection; - QTime t; }; -void tst_QQmlDebuggingEnabler::initTestCase() -{ - QQmlDataTest::initTestCase(); - t.start(); - process = 0; - connection = 0; -} - -void tst_QQmlDebuggingEnabler::cleanupTestCase() -{ - if (process) { - process->stop(); - delete process; - } - - if (connection) - delete connection; -} - -bool tst_QQmlDebuggingEnabler::init(bool blockMode, bool qmlscene, int portFrom, int portTo) -{ - connection = new QQmlDebugConnection(); - - if (qmlscene) { - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); - process->setMaximumBindErrors(1); - } else { - process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + QLatin1String("/qqmldebuggingenablerserver"), this); - process->setMaximumBindErrors(portTo - portFrom); - } - - if (qmlscene) { - process->start(QStringList() << QLatin1String("-qmljsdebugger=port:") + - QString::number(portFrom) + QLatin1Char(',') + QString::number(portTo) + - QLatin1String(blockMode ? ",block": "") << - testFile(QLatin1String("test.qml"))); - } else { - QStringList args; - if (blockMode) - args << QLatin1String("-block"); - args << QString::number(portFrom) << QString::number(portTo); - process->start(args); - } - - if (!process->waitForSessionStart()) { - return false; - } - - const int port = process->debugPort(); - connection->connectToHost("127.0.0.1", port); - if (!connection->waitForConnected()) { - qDebug() << "could not connect to host!"; - return false; - } - return true; -} - -void tst_QQmlDebuggingEnabler::cleanup() -{ - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << process->state(); - qDebug() << "Application Output:" << process->output(); - } - - if (process) { - process->stop(); - delete process; - } - - - if (connection) - delete connection; - - process = 0; - connection = 0; -} - void tst_QQmlDebuggingEnabler::data() { QTest::addColumn<QString>("connector"); @@ -185,32 +102,34 @@ void tst_QQmlDebuggingEnabler::qmlscene() QFETCH(bool, blockMode); QFETCH(QStringList, services); - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", - this); - process->setMaximumBindErrors(1); - process->start(QStringList() - << QString::fromLatin1("-qmljsdebugger=connector:%1%2%3%4") - .arg(connector + (connector == QLatin1String("QQmlDebugServer") ? - QLatin1String(",port:5555,5565") : QString())) - .arg(blockMode ? QLatin1String(",block") : QString()) - .arg(services.isEmpty() ? QString() : QString::fromLatin1(",services:")) - .arg(services.isEmpty() ? QString() : services.join(",")) - << testFile(QLatin1String("test.qml"))); + m_process = new QQmlDebugProcess( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + m_process->setMaximumBindErrors(1); + m_process->start(QStringList() + << QString::fromLatin1("-qmljsdebugger=connector:%1%2%3%4") + .arg(connector + (connector == QLatin1String("QQmlDebugServer") + ? QLatin1String(",port:5555,5565") : QString())) + .arg(blockMode ? QLatin1String(",block") : QString()) + .arg(services.isEmpty() ? QString() : QString::fromLatin1(",services:")) + .arg(services.isEmpty() ? QString() : services.join(",")) + << testFile(QLatin1String("test.qml"))); if (connector == QLatin1String("QQmlDebugServer")) { - QVERIFY(process->waitForSessionStart()); - connection = new QQmlDebugConnection(); - QList<QQmlDebugClient *> clients = QQmlDebugTest::createOtherClients(connection); - connection->connectToHost("127.0.0.1", process->debugPort()); - QVERIFY(connection->waitForConnected()); - foreach (QQmlDebugClient *client, clients) + QVERIFY(m_process->waitForSessionStart()); + m_connection = new QQmlDebugConnection(); + m_clients = QQmlDebugTest::createOtherClients(m_connection); + m_connection->connectToHost("127.0.0.1", m_process->debugPort()); + QVERIFY(m_connection->waitForConnected()); + foreach (QQmlDebugClient *client, m_clients) QCOMPARE(client->state(), (services.isEmpty() || services.contains(client->name())) ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); } - QCOMPARE(process->state(), QLatin1String("running")); - if (!blockMode) - QTRY_VERIFY(process->output().contains(QLatin1String("qml: Component.onCompleted"))); + QCOMPARE(m_process->state(), QProcess::Running); + if (!blockMode) { + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains( + QLatin1String("Component.onCompleted")), 15000); + } } void tst_QQmlDebuggingEnabler::custom_data() @@ -226,9 +145,9 @@ void tst_QQmlDebuggingEnabler::custom() const int portFrom = 5555; const int portTo = 5565; - process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + - QLatin1String("/qqmldebuggingenablerserver"), this); - process->setMaximumBindErrors(portTo - portFrom); + m_process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + + QLatin1String("/qqmldebuggingenablerserver"), this); + m_process->setMaximumBindErrors(portTo - portFrom); QStringList args; if (blockMode) @@ -240,22 +159,24 @@ void tst_QQmlDebuggingEnabler::custom() if (!services.isEmpty()) args << QLatin1String("-services") << services; - process->start(args); + m_process->start(args); if (connector == QLatin1String("QQmlDebugServer")) { - QVERIFY(process->waitForSessionStart()); - connection = new QQmlDebugConnection(); - QList<QQmlDebugClient *> clients = QQmlDebugTest::createOtherClients(connection); - connection->connectToHost("127.0.0.1", process->debugPort()); - QVERIFY(connection->waitForConnected()); - foreach (QQmlDebugClient *client, clients) + QVERIFY(m_process->waitForSessionStart()); + m_connection = new QQmlDebugConnection(); + m_clients = QQmlDebugTest::createOtherClients(m_connection); + m_connection->connectToHost("127.0.0.1", m_process->debugPort()); + QVERIFY(m_connection->waitForConnected()); + for (QQmlDebugClient *client : qAsConst(m_clients)) QCOMPARE(client->state(), (services.isEmpty() || services.contains(client->name())) ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); } - QCOMPARE(process->state(), QLatin1String("running")); - if (!blockMode) - QTRY_VERIFY(process->output().contains(QLatin1String("QQmlEngine created"))); + QCOMPARE(m_process->state(), QProcess::Running); + if (!blockMode) { + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains(QLatin1String("QQmlEngine created")), + 15000); + } } QTEST_MAIN(tst_QQmlDebuggingEnabler) diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml new file mode 100644 index 0000000000..72a8c9559c --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.11 + + +Item { + visible: true + Text { + anchors.centerIn: parent + text: "bla" + MouseArea { + anchors.fill: parent + } + } + + Timer { + interval: 100; + running: true; + onTriggered: { + Qt.quit(); + } + } +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/breakpointRelocation.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml index 06aabc94f9..06aabc94f9 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/breakpointRelocation.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/changeBreakpoint.qml b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml index 00a85e56ac..00a85e56ac 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/changeBreakpoint.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/condition.qml b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml index 3a50ba2eb7..3a50ba2eb7 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/condition.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/createComponent.qml b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml index 089cc03733..089cc03733 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/createComponent.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/encodeQmlScope.qml b/tests/auto/qml/debugger/qqmldebugjs/data/encodeQmlScope.qml new file mode 100644 index 0000000000..7ea048044f --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/encodeQmlScope.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +Item { + property int a: 0 + property int b: 0 + onAChanged: console.log("inline") + onBChanged: { + console.log("extra braces"); + } + + Timer { + interval: 10 + running: true + onTriggered: { + parent.a += 10; + parent.b -= 10; + } + } +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/exception.qml b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml index 06f11fa016..06f11fa016 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/exception.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/loadjsfile.qml b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml index 088c1b19fd..088c1b19fd 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/loadjsfile.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/oncompleted.qml b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml index deba24cf91..deba24cf91 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/oncompleted.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/quit.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml index bc8c2b90ae..bc8c2b90ae 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/quit.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/stepAction.qml b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml index fb0b6c401c..fb0b6c401c 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/stepAction.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.js b/tests/auto/qml/debugger/qqmldebugjs/data/test.js index 92e61d103c..92e61d103c 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.js +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.js diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.qml b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml index a36d0cae91..a36d0cae91 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/timer.qml b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml index 66e6b96e18..66e6b96e18 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/timer.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro index bd6debcea1..acd5546a02 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro @@ -1,4 +1,24 @@ -TEMPLATE = subdirs -SUBDIRS = qqmldebugjs qqmldebugjsserver +CONFIG += testcase +TARGET = tst_qqmldebugjs +QT += testlib gui-private core-private +macos:CONFIG -= app_bundle -qqmldebugjs.depends = qqmldebugjsserver +SOURCES += tst_qqmldebugjs.cpp + +INCLUDEPATH += ../shared +include(../shared/debugutil.pri) + +TESTDATA = data/* + +OTHER_FILES += data/test.qml data/test.js \ + data/timer.qml \ + data/exception.qml \ + data/oncompleted.qml \ + data/loadjsfile.qml \ + data/condition.qml \ + data/changeBreakpoint.qml \ + data/stepAction.qml \ + data/breakpointRelocation.qml \ + data/createComponent.qml \ + data/encodeQmlScope.qml \ + data/breakOnAnchor.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/qqmldebugjs.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/qqmldebugjs.pro deleted file mode 100644 index cbaf3b5309..0000000000 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/qqmldebugjs.pro +++ /dev/null @@ -1,25 +0,0 @@ -CONFIG += testcase -TARGET = tst_qqmldebugjs -QT += qml testlib gui-private core-private -CONFIG -= debug_and_release_target -osx:CONFIG -= app_bundle - -SOURCES += tst_qqmldebugjs.cpp - -INCLUDEPATH += ../../shared -include(../../../../shared/util.pri) -include(../../shared/debugutil.pri) -include(../../shared/qqmlenginedebugclient.pri) - -TESTDATA = data/* - -OTHER_FILES += data/test.qml data/test.js \ - data/timer.qml \ - data/exception.qml \ - data/oncompleted.qml \ - data/loadjsfile.qml \ - data/condition.qml \ - data/changeBreakpoint.qml \ - data/stepAction.qml \ - data/breakpointRelocation.qml \ - data/createComponent.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp deleted file mode 100644 index d248cf9708..0000000000 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp +++ /dev/null @@ -1,1552 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "debugutil_p.h" -#include "../../shared/qqmlenginedebugclient.h" -#include "../../../../shared/util.h" - -#include <private/qqmldebugclient_p.h> -#include <private/qqmldebugconnection_p.h> -#include <private/qpacket_p.h> - -#include <QtTest/qtest.h> -#include <QtCore/qprocess.h> -#include <QtCore/qtimer.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qdir.h> -#include <QtCore/qmutex.h> -#include <QtCore/qlibraryinfo.h> -#include <QtQml/qjsengine.h> - -const char *V8REQUEST = "v8request"; -const char *V8MESSAGE = "v8message"; -const char *SEQ = "seq"; -const char *TYPE = "type"; -const char *COMMAND = "command"; -const char *ARGUMENTS = "arguments"; -const char *STEPACTION = "stepaction"; -const char *STEPCOUNT = "stepcount"; -const char *EXPRESSION = "expression"; -const char *FRAME = "frame"; -const char *CONTEXT = "context"; -const char *GLOBAL = "global"; -const char *DISABLEBREAK = "disable_break"; -const char *HANDLES = "handles"; -const char *INCLUDESOURCE = "includeSource"; -const char *FROMFRAME = "fromFrame"; -const char *TOFRAME = "toFrame"; -const char *BOTTOM = "bottom"; -const char *NUMBER = "number"; -const char *FRAMENUMBER = "frameNumber"; -const char *TYPES = "types"; -const char *IDS = "ids"; -const char *FILTER = "filter"; -const char *FROMLINE = "fromLine"; -const char *TOLINE = "toLine"; -const char *TARGET = "target"; -const char *LINE = "line"; -const char *COLUMN = "column"; -const char *ENABLED = "enabled"; -const char *CONDITION = "condition"; -const char *IGNORECOUNT = "ignoreCount"; -const char *BREAKPOINT = "breakpoint"; -const char *FLAGS = "flags"; - -const char *CONTINEDEBUGGING = "continue"; -const char *EVALUATE = "evaluate"; -const char *LOOKUP = "lookup"; -const char *BACKTRACE = "backtrace"; -const char *SCOPE = "scope"; -const char *SCOPES = "scopes"; -const char *SCRIPTS = "scripts"; -const char *SOURCE = "source"; -const char *SETBREAKPOINT = "setbreakpoint"; -const char *CLEARBREAKPOINT = "clearbreakpoint"; -const char *SETEXCEPTIONBREAK = "setexceptionbreak"; -const char *VERSION = "version"; -const char *DISCONNECT = "disconnect"; -const char *GARBAGECOLLECTOR = "gc"; - -const char *CONNECT = "connect"; -const char *INTERRUPT = "interrupt"; - -const char *REQUEST = "request"; -const char *IN = "in"; -const char *NEXT = "next"; -const char *OUT = "out"; - -const char *SCRIPT = "script"; -const char *SCRIPTREGEXP = "scriptRegExp"; -const char *EVENT = "event"; - -const char *ALL = "all"; -const char *UNCAUGHT = "uncaught"; - -const char *BLOCKMODE = "-qmljsdebugger=port:3771,3800,block"; -const char *NORMALMODE = "-qmljsdebugger=port:3771,3800"; -const char *BLOCKRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,block,services:V8Debugger"; -const char *NORMALRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,services:V8Debugger"; -const char *TEST_QMLFILE = "test.qml"; -const char *TEST_JSFILE = "test.js"; -const char *TIMER_QMLFILE = "timer.qml"; -const char *LOADJSFILE_QMLFILE = "loadjsfile.qml"; -const char *EXCEPTION_QMLFILE = "exception.qml"; -const char *ONCOMPLETED_QMLFILE = "oncompleted.qml"; -const char *CREATECOMPONENT_QMLFILE = "createComponent.qml"; -const char *CONDITION_QMLFILE = "condition.qml"; -const char *QUIT_QMLFILE = "quit.qml"; -const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml"; -const char *STEPACTION_QMLFILE = "stepAction.qml"; -const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; - -#define VARIANTMAPINIT \ - QString obj("{}"); \ - QJSValue jsonVal = parser.call(QJSValueList() << obj); \ - jsonVal.setProperty(SEQ,QJSValue(seq++)); \ - jsonVal.setProperty(TYPE,REQUEST); - - -#undef QVERIFY -#define QVERIFY(statement) \ -do {\ - if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\ - if (QTest::currentTestFailed()) \ - qDebug().nospace() << "\nDEBUGGEE OUTPUT:\n" << process->output();\ - return;\ - }\ -} while (0) - - -class QJSDebugClient; - -class tst_QQmlDebugJS : public QQmlDataTest -{ - Q_OBJECT - - void init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), bool blockMode = true, - bool restrictServices = false); - -private slots: - void initTestCase(); - void cleanupTestCase(); - - void cleanup(); - - void connect_data(); - void connect(); - void interrupt_data() { targetData(); } - void interrupt(); - void getVersion_data() { targetData(); } - void getVersion(); - void getVersionWhenAttaching_data() { targetData(); } - void getVersionWhenAttaching(); - - void disconnect_data() { targetData(); } - void disconnect(); - - void setBreakpointInScriptOnCompleted_data() { targetData(); } - void setBreakpointInScriptOnCompleted(); - void setBreakpointInScriptOnComponentCreated_data() { targetData(); } - void setBreakpointInScriptOnComponentCreated(); - void setBreakpointInScriptOnTimerCallback_data() { targetData(); } - void setBreakpointInScriptOnTimerCallback(); - void setBreakpointInScriptInDifferentFile_data() { targetData(); } - void setBreakpointInScriptInDifferentFile(); - void setBreakpointInScriptOnComment_data() { targetData(); } - void setBreakpointInScriptOnComment(); - void setBreakpointInScriptOnEmptyLine_data() { targetData(); } - void setBreakpointInScriptOnEmptyLine(); - void setBreakpointInScriptOnOptimizedBinding_data() { targetData(); } - void setBreakpointInScriptOnOptimizedBinding(); - void setBreakpointInScriptWithCondition_data() { targetData(); } - void setBreakpointInScriptWithCondition(); - void setBreakpointInScriptThatQuits_data() { targetData(); } - void setBreakpointInScriptThatQuits(); - void setBreakpointWhenAttaching(); - - void clearBreakpoint_data() { targetData(); } - void clearBreakpoint(); - - void setExceptionBreak_data() { targetData(); } - void setExceptionBreak(); - - void stepNext_data() { targetData(); } - void stepNext(); - void stepIn_data() { targetData(); } - void stepIn(); - void stepOut_data() { targetData(); } - void stepOut(); - void continueDebugging_data() { targetData(); } - void continueDebugging(); - - void backtrace_data() { targetData(); } - void backtrace(); - - void getFrameDetails_data() { targetData(); } - void getFrameDetails(); - - void getScopeDetails_data() { targetData(); } - void getScopeDetails(); - - void evaluateInGlobalScope(); - void evaluateInLocalScope_data() { targetData(); } - void evaluateInLocalScope(); - - void evaluateInContext(); - - void getScripts_data() { targetData(); } - void getScripts(); - -private: - void targetData(); - - QQmlDebugProcess *process; - QJSDebugClient *client; - QQmlDebugConnection *connection; - QTime t; -}; - -class QJSDebugClient : public QQmlDebugClient -{ - Q_OBJECT -public: - enum StepAction - { - Continue, - In, - Out, - Next - }; - - enum Exception - { - All, - Uncaught - }; - - QJSDebugClient(QQmlDebugConnection *connection) - : QQmlDebugClient(QLatin1String("V8Debugger"), connection), - seq(0) - { - parser = jsEngine.evaluate(QLatin1String("JSON.parse")); - stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); - } - - void connect(bool redundantRefs = false, bool namesAsObjects = false); - void interrupt(); - - void continueDebugging(StepAction stepAction); - void evaluate(QString expr, int frame = -1, int context = -1); - void lookup(QList<int> handles, bool includeSource = false); - void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); - void frame(int number = -1); - void scope(int number = -1, int frameNumber = -1); - void scripts(int types = 4, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); - void setBreakpoint(QString target, int line = -1, int column = -1, bool enabled = true, - QString condition = QString(), int ignoreCount = -1); - void clearBreakpoint(int breakpoint); - void setExceptionBreak(Exception type, bool enabled = false); - void version(); - void disconnect(); - -protected: - //inherited from QQmlDebugClient - void stateChanged(State state); - void messageReceived(const QByteArray &data); - -signals: - void enabled(); - void connected(); - void interruptRequested(); - void result(); - void failure(); - void stopped(); - -private: - void sendMessage(const QByteArray &); - void flushSendBuffer(); - QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); - -private: - QJSEngine jsEngine; - int seq; - - QList<QByteArray> sendBuffer; -public: - QJSValue parser; - QJSValue stringify; - QByteArray response; - -}; - -void QJSDebugClient::connect(bool redundantRefs, bool namesAsObjects) -{ - QJSValue jsonVal = parser.call(QJSValueList() << QLatin1String("{}")); - jsonVal.setProperty("redundantRefs", QJSValue(redundantRefs)); - jsonVal.setProperty("namesAsObjects", QJSValue(namesAsObjects)); - sendMessage(packMessage(CONNECT, - stringify.call(QJSValueList() << jsonVal).toString().toUtf8())); -} - -void QJSDebugClient::interrupt() -{ - sendMessage(packMessage(INTERRUPT)); -} - -void QJSDebugClient::continueDebugging(StepAction action) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "continue", - // "arguments" : { "stepaction" : <"in", "next" or "out">, - // "stepcount" : <number of steps (default 1)> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CONTINEDEBUGGING))); - - if (action != Continue) { - QJSValue args = parser.call(QJSValueList() << obj); - switch (action) { - case In: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(IN))); - break; - case Out: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(OUT))); - break; - case Next: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(NEXT))); - break; - default:break; - } - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::evaluate(QString expr, int frame, int context) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "evaluate", - // "arguments" : { "expression" : <expression to evaluate>, - // "frame" : <number>, - // "context" : <object ID> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(EVALUATE))); - - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(EXPRESSION),QJSValue(expr)); - - if (frame != -1) - args.setProperty(QLatin1String(FRAME),QJSValue(frame)); - - if (context != -1) - args.setProperty(QLatin1String(CONTEXT), QJSValue(context)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::lookup(QList<int> handles, bool includeSource) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "lookup", - // "arguments" : { "handles" : <array of handles>, - // "includeSource" : <boolean indicating whether the source will be included when script objects are returned>, - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(LOOKUP))); - - QJSValue args = parser.call(QJSValueList() << obj); - - QString arr("[]"); - QJSValue array = parser.call(QJSValueList() << arr); - int index = 0; - foreach (int handle, handles) { - array.setProperty(index++,QJSValue(handle)); - } - args.setProperty(QLatin1String(HANDLES),array); - - if (includeSource) - args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::backtrace(int fromFrame, int toFrame, bool bottom) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "backtrace", - // "arguments" : { "fromFrame" : <number> - // "toFrame" : <number> - // "bottom" : <boolean, set to true if the bottom of the stack is requested> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(BACKTRACE))); - - QJSValue args = parser.call(QJSValueList() << obj); - - if (fromFrame != -1) - args.setProperty(QLatin1String(FROMFRAME),QJSValue(fromFrame)); - - if (toFrame != -1) - args.setProperty(QLatin1String(TOFRAME),QJSValue(toFrame)); - - if (bottom) - args.setProperty(QLatin1String(BOTTOM),QJSValue(bottom)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::frame(int number) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "frame", - // "arguments" : { "number" : <frame number> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(FRAME))); - - if (number != -1) { - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(NUMBER),QJSValue(number)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::scope(int number, int frameNumber) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "scope", - // "arguments" : { "number" : <scope number> - // "frameNumber" : <frame number, optional uses selected frame if missing> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCOPE))); - - if (number != -1) { - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(NUMBER),QJSValue(number)); - - if (frameNumber != -1) - args.setProperty(QLatin1String(FRAMENUMBER),QJSValue(frameNumber)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::scripts(int types, QList<int> ids, bool includeSource, QVariant /*filter*/) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "scripts", - // "arguments" : { "types" : <types of scripts to retrieve - // set bit 0 for native scripts - // set bit 1 for extension scripts - // set bit 2 for normal scripts - // (default is 4 for normal scripts)> - // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned> - // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned> - // "filter" : <string or number: filter string or script id. - // If a number is specified, then only the script with the same number as its script id will be retrieved. - // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCRIPTS))); - - QJSValue args = parser.call(QJSValueList() << obj); - args.setProperty(QLatin1String(TYPES),QJSValue(types)); - - if (ids.count()) { - QString arr("[]"); - QJSValue array = parser.call(QJSValueList() << arr); - int index = 0; - foreach (int id, ids) { - array.setProperty(index++,QJSValue(id)); - } - args.setProperty(QLatin1String(IDS),array); - } - - if (includeSource) - args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::setBreakpoint(QString target, int line, int column, bool enabled, - QString condition, int ignoreCount) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "setbreakpoint", - // "arguments" : { "type" : "scriptRegExp" - // "target" : <function expression or script identification> - // "line" : <line in script or function> - // "column" : <character position within the line> - // "enabled" : <initial enabled state. True or false, default is true> - // "condition" : <string with break point condition> - // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0> - // } - // } - - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETBREAKPOINT))); - - QJSValue args = parser.call(QJSValueList() << obj); - - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(SCRIPTREGEXP))); - args.setProperty(QLatin1String(TARGET),QJSValue(target)); - - if (line != -1) - args.setProperty(QLatin1String(LINE),QJSValue(line)); - - if (column != -1) - args.setProperty(QLatin1String(COLUMN),QJSValue(column)); - - args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); - - if (!condition.isEmpty()) - args.setProperty(QLatin1String(CONDITION),QJSValue(condition)); - - if (ignoreCount != -1) - args.setProperty(QLatin1String(IGNORECOUNT),QJSValue(ignoreCount)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::clearBreakpoint(int breakpoint) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "clearbreakpoint", - // "arguments" : { "breakpoint" : <number of the break point to clear> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CLEARBREAKPOINT))); - - QJSValue args = parser.call(QJSValueList() << obj); - - args.setProperty(QLatin1String(BREAKPOINT),QJSValue(breakpoint)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::setExceptionBreak(Exception type, bool enabled) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "setexceptionbreak", - // "arguments" : { "type" : <string: "all", or "uncaught">, - // "enabled" : <optional bool: enables the break type if true> - // } - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETEXCEPTIONBREAK))); - - QJSValue args = parser.call(QJSValueList() << obj); - - if (type == All) - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(ALL))); - else if (type == Uncaught) - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(UNCAUGHT))); - - if (enabled) - args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); - - if (!args.isUndefined()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::version() -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "version", - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(VERSION))); - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::disconnect() -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "disconnect", - // } - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(DISCONNECT))); - - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(DISCONNECT, json.toString().toUtf8())); -} - -void QJSDebugClient::stateChanged(State state) -{ - if (state == Enabled) { - flushSendBuffer(); - emit enabled(); - } -} - -void QJSDebugClient::messageReceived(const QByteArray &data) -{ - QPacket ds(connection()->currentDataStreamVersion(), data); - QByteArray command; - ds >> command; - - if (command == "V8DEBUG") { - QByteArray type; - ds >> type >> response; - - if (type == CONNECT) { - emit connected(); - - } else if (type == INTERRUPT) { - emit interruptRequested(); - - } else if (type == V8MESSAGE) { - QString jsonString(response); - QVariantMap value = parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - QString type = value.value("type").toString(); - - if (type == "response") { - - if (!value.value("success").toBool()) { - emit failure(); - qDebug() << "Received success == false response from application"; - return; - } - - QString debugCommand(value.value("command").toString()); - if (debugCommand == "backtrace" || - debugCommand == "lookup" || - debugCommand == "setbreakpoint" || - debugCommand == "evaluate" || - debugCommand == "version" || - debugCommand == "disconnect" || - debugCommand == "gc" || - debugCommand == "changebreakpoint" || - debugCommand == "clearbreakpoint" || - debugCommand == "frame" || - debugCommand == "scope" || - debugCommand == "scopes" || - debugCommand == "scripts" || - debugCommand == "source" || - debugCommand == "setexceptionbreak" /*|| - debugCommand == "profile"*/) { - emit result(); - - } else { - // DO NOTHING - } - - } else if (type == QLatin1String(EVENT)) { - QString event(value.value(QLatin1String(EVENT)).toString()); - - if (event == "break" || - event == "exception") - emit stopped(); - } - - } - } -} - -void QJSDebugClient::sendMessage(const QByteArray &msg) -{ - if (state() == Enabled) { - QQmlDebugClient::sendMessage(msg); - } else { - sendBuffer.append(msg); - } -} - -void QJSDebugClient::flushSendBuffer() -{ - foreach (const QByteArray &msg, sendBuffer) - QQmlDebugClient::sendMessage(msg); - sendBuffer.clear(); -} - -QByteArray QJSDebugClient::packMessage(const QByteArray &type, const QByteArray &message) -{ - QPacket rs(connection()->currentDataStreamVersion()); - QByteArray cmd = "V8DEBUG"; - rs << cmd << type << message; - return rs.data(); -} - -void tst_QQmlDebugJS::initTestCase() -{ - QQmlDataTest::initTestCase(); - t.start(); - process = 0; - client = 0; - connection = 0; -} - -void tst_QQmlDebugJS::cleanupTestCase() -{ - if (process) { - process->stop(); - delete process; - } - - if (client) - delete client; - - if (connection) - delete connection; -} - -void tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile, bool blockMode, - bool restrictServices) -{ - connection = new QQmlDebugConnection(); - if (qmlscene) - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + - "/qmlscene", this); - else - process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + - QLatin1String("/qqmldebugjsserver"), this); - client = new QJSDebugClient(connection); - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(connection); - - const char *args = 0; - if (blockMode) - args = restrictServices ? BLOCKRESTRICTEDMODE : BLOCKMODE; - else - args = restrictServices ? NORMALRESTRICTEDMODE : NORMALMODE; - - process->start(QStringList() << QLatin1String(args) << testFile(qmlFile)); - - QVERIFY(process->waitForSessionStart()); - - const int port = process->debugPort(); - connection->connectToHost("127.0.0.1", port); - QVERIFY(connection->waitForConnected()); - - - if (client->state() != QQmlDebugClient::Enabled) - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(enabled()))); - - foreach (QQmlDebugClient *otherClient, others) - QCOMPARE(otherClient->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); -} - -void tst_QQmlDebugJS::cleanup() -{ - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << process->state(); - qDebug() << "Application Output:" << process->output(); - } - - if (process) { - process->stop(); - delete process; - } - - if (client) - delete client; - - if (connection) - delete connection; - - process = 0; - client = 0; - connection = 0; -} - -void tst_QQmlDebugJS::connect_data() -{ - QTest::addColumn<bool>("blockMode"); - QTest::addColumn<bool>("restrictMode"); - QTest::addColumn<bool>("qmlscene"); - QTest::newRow("normal / unrestricted / custom") << false << false << false; - QTest::newRow("block / unrestricted / custom") << true << false << false; - QTest::newRow("normal / restricted / custom") << false << true << false; - QTest::newRow("block / restricted / custom") << true << true << false; - QTest::newRow("normal / unrestricted / qmlscene") << false << false << true; - QTest::newRow("block / unrestricted / qmlscene") << true << false << true; - QTest::newRow("normal / restricted / qmlscene") << false << true << true; - QTest::newRow("block / restricted / qmlscene") << true << true << true; -} - -void tst_QQmlDebugJS::connect() -{ - QFETCH(bool, blockMode); - QFETCH(bool, restrictMode); - QFETCH(bool, qmlscene); - init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode); - client->connect(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); -} - -void tst_QQmlDebugJS::interrupt() -{ - //void connect() - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - client->connect(redundantRefs, namesAsObjects); - - client->interrupt(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(interruptRequested()))); -} - -void tst_QQmlDebugJS::getVersion() -{ - //void version() - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); - - client->version(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::getVersionWhenAttaching() -{ - //void version() - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - init(qmlscene, QLatin1String(TIMER_QMLFILE), false); - client->connect(redundantRefs, namesAsObjects); - - client->version(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::disconnect() -{ - //void disconnect() - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - client->connect(redundantRefs, namesAsObjects); - - client->disconnect(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(ONCOMPLETED_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, CREATECOMPONENT_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(ONCOMPLETED_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() -{ - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - int sourceLine = 35; - init(qmlscene, TIMER_QMLFILE); - - client->connect(redundantRefs, namesAsObjects); - //We can set the breakpoint after connect() here because the timer is repeating and if we miss - //its first iteration we can still catch the second one. - client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TIMER_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 31; - init(qmlscene, LOADJSFILE_QMLFILE); - - client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TEST_JSFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptOnComment() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - int actualLine = 36; - init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); - - client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 35; - int actualLine = 36; - init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); - - client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 39; - init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); - - client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() -{ - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - int out = 10; - int sourceLine = 37; - init(qmlscene, CONDITION_QMLFILE); - - client->connect(redundantRefs, namesAsObjects); - //The breakpoint is in a timer loop so we can set it after connect(). - client->setBreakpoint(QLatin1String(CONDITION_QMLFILE), sourceLine, 1, true, QLatin1String("a > 10")); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - //Get the frame index - QString jsonString = client->response; - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - { - QVariantMap body = value.value("body").toMap(); - int frameIndex = body.value("index").toInt(); - - //Verify the value of 'result' - client->evaluate(QLatin1String("a"),frameIndex); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - } - - jsonString = client->response; - QJSValue val = client->parser.call(QJSValueList() << QJSValue(jsonString)); - QVERIFY(val.isObject()); - QJSValue body = val.property(QStringLiteral("body")); - QVERIFY(body.isObject()); - val = body.property("value"); - QVERIFY(val.isNumber()); - - const int a = val.toInt(); - QVERIFY(a > out); -} - -void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() -{ - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene, QUIT_QMLFILE); - - int sourceLine = 36; - - client->setBreakpoint(QLatin1String(QUIT_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QUIT_QMLFILE)); - - client->continueDebugging(QJSDebugClient::Continue); - QVERIFY(process->waitForFinished()); - QCOMPARE(process->exitStatus(), QProcess::NormalExit); -} - -void tst_QQmlDebugJS::setBreakpointWhenAttaching() -{ - int sourceLine = 35; - init(true, QLatin1String(TIMER_QMLFILE), false); - - client->connect(); - - QSKIP("\nThe breakpoint may not hit because the engine may run in JIT mode or not have debug\n" - "instructions, as we've connected in non-blocking mode above. That means we may have\n" - "connected after the engine was already running, with all the QML already compiled."); - - //The breakpoint is in a timer loop so we can set it after connect(). - client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine); - - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); -} - -void tst_QQmlDebugJS::clearBreakpoint() -{ - //void clearBreakpoint(int breakpoint); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine1 = 37; - int sourceLine2 = 38; - init(qmlscene, CHANGEBREAKPOINT_QMLFILE); - - client->connect(redundantRefs, namesAsObjects); - //The breakpoints are in a timer loop so we can set them after connect(). - //Furthermore the breakpoints should be hit in the right order because setting of breakpoints - //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) - client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); - client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine2, -1, true); - - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - //Will hit 1st brakpoint, change this breakpoint enable = false - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); - - int breakpoint = breakpointsHit.at(0).toInt(); - client->clearBreakpoint(breakpoint); - - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - - //Continue with debugging - client->continueDebugging(QJSDebugClient::Continue); - //Hit 2nd breakpoint - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - //Continue with debugging - client->continueDebugging(QJSDebugClient::Continue); - //Should stop at 2nd breakpoint - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - jsonString = client->response; - value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); -} - -void tst_QQmlDebugJS::setExceptionBreak() -{ - //void setExceptionBreak(QString type, bool enabled = false); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - init(qmlscene, EXCEPTION_QMLFILE); - client->setExceptionBreak(QJSDebugClient::All,true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); -} - -void tst_QQmlDebugJS::stepNext() -{ - //void continueDebugging(StepAction stepAction, int stepCount = 1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 37; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->continueDebugging(QJSDebugClient::Next); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); -} - -void tst_QQmlDebugJS::stepIn() -{ - //void continueDebugging(StepAction stepAction, int stepCount = 1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 41; - int actualLine = 37; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->continueDebugging(QJSDebugClient::In); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); -} - -void tst_QQmlDebugJS::stepOut() -{ - //void continueDebugging(StepAction stepAction, int stepCount = 1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 37; - int actualLine = 41; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->continueDebugging(QJSDebugClient::Out); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), actualLine); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); -} - -void tst_QQmlDebugJS::continueDebugging() -{ - //void continueDebugging(StepAction stepAction, int stepCount = 1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine1 = 41; - int sourceLine2 = 38; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->continueDebugging(QJSDebugClient::Continue); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); - QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); -} - -void tst_QQmlDebugJS::backtrace() -{ - //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->backtrace(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::getFrameDetails() -{ - //void frame(int number = -1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->frame(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::getScopeDetails() -{ - //void scope(int number = -1, int frameNumber = -1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->scope(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::evaluateInGlobalScope() -{ - //void evaluate(QString expr, int frame = -1); - init(true); - - client->connect(); - - do { - // The engine might not be initialized, yet. We just try until it shows up. - client->evaluate(QLatin1String("console.log('Hello World')")); - } while (!QQmlDebugTest::waitForSignal(client, SIGNAL(result()), 500)); - - //Verify the return value of 'console.log()', which is "undefined" - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - QVariantMap body = value.value("body").toMap(); - QCOMPARE(body.value("type").toString(),QLatin1String("undefined")); -} - -void tst_QQmlDebugJS::evaluateInLocalScope() -{ - //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->frame(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - - //Get the frame index - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - - int frameIndex = body.value("index").toInt(); - - client->evaluate(QLatin1String("root.a"), frameIndex); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - - //Verify the value of 'timer.interval' - jsonString = client->response; - value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - body = value.value("body").toMap(); - - QCOMPARE(body.value("value").toInt(),10); -} - -void tst_QQmlDebugJS::evaluateInContext() -{ - connection = new QQmlDebugConnection(); - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) - + "/qmlscene", this); - client = new QJSDebugClient(connection); - QScopedPointer<QQmlEngineDebugClient> engineClient(new QQmlEngineDebugClient(connection)); - process->start(QStringList() << QLatin1String(BLOCKMODE) << testFile(ONCOMPLETED_QMLFILE)); - - QVERIFY(process->waitForSessionStart()); - - connection->connectToHost("127.0.0.1", process->debugPort()); - QVERIFY(connection->waitForConnected()); - - QTRY_COMPARE(client->state(), QQmlEngineDebugClient::Enabled); - QTRY_COMPARE(engineClient->state(), QQmlEngineDebugClient::Enabled); - client->connect(); - - // "a" not accessible without extra context - client->evaluate(QLatin1String("a + 10"), -1, -1); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(failure()))); - - bool success = false; - engineClient->queryAvailableEngines(&success); - QVERIFY(success); - QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); - - QVERIFY(engineClient->engines().count()); - engineClient->queryRootContexts(engineClient->engines()[0].debugId, &success); - QVERIFY(success); - QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); - - auto contexts = engineClient->rootContext().contexts; - QCOMPARE(contexts.count(), 1); - auto objects = contexts[0].objects; - QCOMPARE(objects.count(), 1); - engineClient->queryObjectRecursive(objects[0], &success); - QVERIFY(success); - QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); - auto object = engineClient->object(); - - // "a" accessible in context of surrounding object - client->evaluate(QLatin1String("a + 10"), -1, object.debugId); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - - QString jsonString = client->response; - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - QTRY_COMPARE(body.value("value").toInt(), 20); -} - -void tst_QQmlDebugJS::getScripts() -{ - //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - - client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->scripts(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() - << QJSValue(jsonString)).toVariant().toMap(); - - QList<QVariant> scripts = value.value("body").toList(); - - QCOMPARE(scripts.count(), 1); - QVERIFY(scripts.first().toMap()[QStringLiteral("name")].toString().endsWith(QStringLiteral("data/test.qml"))); -} - -void tst_QQmlDebugJS::targetData() -{ - QTest::addColumn<bool>("qmlscene"); - QTest::addColumn<bool>("redundantRefs"); - QTest::addColumn<bool>("namesAsObjects"); - QTest::newRow("custom / redundant / objects") << false << true << true; - QTest::newRow("qmlscene / redundant / objects") << true << true << true; - QTest::newRow("custom / redundant / strings") << false << true << false; - QTest::newRow("qmlscene / redundant / strings") << true << true << false; - QTest::newRow("custom / sparse / objects") << false << false << true; - QTest::newRow("qmlscene / sparse / objects") << true << false << true; - QTest::newRow("custom / sparse / strings") << false << false << false; - QTest::newRow("qmlscene / sparse / strings") << true << false << false; -} - -QTEST_MAIN(tst_QQmlDebugJS) - -#include "tst_qqmldebugjs.moc" - diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.pro deleted file mode 100644 index 837eaed9f1..0000000000 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.pro +++ /dev/null @@ -1,12 +0,0 @@ -QT += qml testlib -osx:CONFIG -= app_bundle -CONFIG -= debug_and_release_target -INCLUDEPATH += ../../shared -SOURCES += qqmldebugjsserver.cpp -DEFINES += QT_QML_DEBUG_NO_WARNING - -DESTDIR = ../qqmldebugjs - -target.path = $$[QT_INSTALL_TESTS]/tst_qqmldebugjs -INSTALLS += target - diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp new file mode 100644 index 0000000000..1ac28c473b --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -0,0 +1,1008 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "debugutil_p.h" +#include "qqmldebugprocess_p.h" +#include "../../../shared/util.h" + +#include <private/qqmlenginedebugclient_p.h> +#include <private/qv4debugclient_p.h> +#include <private/qqmldebugconnection_p.h> +#include <private/qpacket_p.h> + +#include <QtTest/qtest.h> +#include <QtTest/qtestsystem.h> +#include <QtCore/qprocess.h> +#include <QtCore/qtimer.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> +#include <QtCore/qmutex.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonarray.h> + +const char *BLOCKMODE = "-qmljsdebugger=port:3771,3800,block"; +const char *NORMALMODE = "-qmljsdebugger=port:3771,3800"; +const char *BLOCKRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,block,services:V8Debugger"; +const char *NORMALRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,services:V8Debugger"; +const char *TEST_QMLFILE = "test.qml"; +const char *TEST_JSFILE = "test.js"; +const char *TIMER_QMLFILE = "timer.qml"; +const char *LOADJSFILE_QMLFILE = "loadjsfile.qml"; +const char *EXCEPTION_QMLFILE = "exception.qml"; +const char *ONCOMPLETED_QMLFILE = "oncompleted.qml"; +const char *CREATECOMPONENT_QMLFILE = "createComponent.qml"; +const char *CONDITION_QMLFILE = "condition.qml"; +const char *QUIT_QMLFILE = "quit.qml"; +const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml"; +const char *STEPACTION_QMLFILE = "stepAction.qml"; +const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; +const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; +const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; + +#undef QVERIFY +#define QVERIFY(statement) \ +do {\ + if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\ + if (QTest::currentTestFailed()) \ + qDebug().nospace() << "\nDEBUGGEE OUTPUT:\n" << m_process->output();\ + return;\ + }\ +} while (0) + +class tst_QQmlDebugJS : public QQmlDebugTest +{ + Q_OBJECT + +private slots: + void initTestCase() override; + + void connect_data(); + void connect(); + void interrupt_data() { targetData(); } + void interrupt(); + void getVersion_data() { targetData(); } + void getVersion(); + void getVersionWhenAttaching_data() { targetData(); } + void getVersionWhenAttaching(); + + void disconnect_data() { targetData(); } + void disconnect(); + + void setBreakpointInScriptOnCompleted_data() { targetData(); } + void setBreakpointInScriptOnCompleted(); + void setBreakpointInScriptOnComponentCreated_data() { targetData(); } + void setBreakpointInScriptOnComponentCreated(); + void setBreakpointInScriptOnTimerCallback_data() { targetData(); } + void setBreakpointInScriptOnTimerCallback(); + void setBreakpointInScriptInDifferentFile_data() { targetData(); } + void setBreakpointInScriptInDifferentFile(); + void setBreakpointInScriptOnComment_data() { targetData(); } + void setBreakpointInScriptOnComment(); + void setBreakpointInScriptOnEmptyLine_data() { targetData(); } + void setBreakpointInScriptOnEmptyLine(); + void setBreakpointInScriptOnOptimizedBinding_data() { targetData(); } + void setBreakpointInScriptOnOptimizedBinding(); + void setBreakpointInScriptWithCondition_data() { targetData(); } + void setBreakpointInScriptWithCondition(); + void setBreakpointInScriptThatQuits_data() { targetData(); } + void setBreakpointInScriptThatQuits(); + void setBreakpointWhenAttaching(); + + void clearBreakpoint_data() { targetData(); } + void clearBreakpoint(); + + void changeBreakpoint_data() { targetData(); } + void changeBreakpoint(); + + void setExceptionBreak_data() { targetData(); } + void setExceptionBreak(); + + void stepNext_data() { targetData(); } + void stepNext(); + void stepIn_data() { targetData(); } + void stepIn(); + void stepOut_data() { targetData(); } + void stepOut(); + void continueDebugging_data() { targetData(); } + void continueDebugging(); + + void backtrace_data() { targetData(); } + void backtrace(); + + void getFrameDetails_data() { targetData(); } + void getFrameDetails(); + + void getScopeDetails_data() { targetData(); } + void getScopeDetails(); + + void evaluateInGlobalScope(); + void evaluateInLocalScope_data() { targetData(); } + void evaluateInLocalScope(); + + void evaluateInContext(); + + void getScripts_data() { targetData(); } + void getScripts(); + + void encodeQmlScope(); + void breakOnAnchor(); + +private: + ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), + bool blockMode = true, bool restrictServices = false); + QList<QQmlDebugClient *> createClients() override; + QPointer<QV4DebugClient> m_client; + + void targetData(); + bool waitForClientSignal(const char *signal, int timeout = 30000); + void checkVersionParameters(); + + QTime t; +}; + + + +void tst_QQmlDebugJS::initTestCase() +{ + QQmlDebugTest::initTestCase(); + t.start(); +} + +QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile, + bool blockMode, bool restrictServices) +{ + const QString executable = qmlscene + ? QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene" + : debugJsServerPath("qqmldebugjs"); + return QQmlDebugTest::connect( + executable, restrictServices ? QStringLiteral("V8Debugger") : QString(), + testFile(qmlFile), blockMode); +} + +void tst_QQmlDebugJS::connect_data() +{ + QTest::addColumn<bool>("blockMode"); + QTest::addColumn<bool>("restrictMode"); + QTest::addColumn<bool>("qmlscene"); + QTest::newRow("normal / unrestricted / custom") << false << false << false; + QTest::newRow("block / unrestricted / custom") << true << false << false; + QTest::newRow("normal / restricted / custom") << false << true << false; + QTest::newRow("block / restricted / custom") << true << true << false; + QTest::newRow("normal / unrestricted / qmlscene") << false << false << true; + QTest::newRow("block / unrestricted / qmlscene") << true << false << true; + QTest::newRow("normal / restricted / qmlscene") << false << true << true; + QTest::newRow("block / restricted / qmlscene") << true << true << true; +} + +void tst_QQmlDebugJS::connect() +{ + QFETCH(bool, blockMode); + QFETCH(bool, restrictMode); + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(connected()))); +} + +void tst_QQmlDebugJS::interrupt() +{ + //void connect() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + m_client->connect(); + + m_client->interrupt(); + QVERIFY(waitForClientSignal(SIGNAL(interrupted()))); +} + +void tst_QQmlDebugJS::getVersion() +{ + //void version() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(connected()))); + + m_client->version(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + checkVersionParameters(); +} + +void tst_QQmlDebugJS::getVersionWhenAttaching() +{ + //void version() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); + m_client->connect(); + + m_client->version(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + checkVersionParameters(); +} + +void tst_QQmlDebugJS::disconnect() +{ + //void disconnect() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + m_client->connect(); + + m_client->disconnect(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(ONCOMPLETED_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(ONCOMPLETED_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() +{ + QFETCH(bool, qmlscene); + + int sourceLine = 35; + QCOMPARE(init(qmlscene, TIMER_QMLFILE), ConnectSuccess); + + m_client->connect(); + //We can set the breakpoint after connect() here because the timer is repeating and if we miss + //its first iteration we can still catch the second one. + m_client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(TIMER_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 31; + QCOMPARE(init(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(TEST_JSFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnComment() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 34; + int actualLine = 36; + QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); + QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1)); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 35; + int actualLine = 36; + QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); + QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1)); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 39; + QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() +{ + QFETCH(bool, qmlscene); + + int out = 10; + int sourceLine = 37; + QCOMPARE(init(qmlscene, CONDITION_QMLFILE), ConnectSuccess); + + m_client->connect(); + //The breakpoint is in a timer loop so we can set it after connect(). + m_client->setBreakpoint(QLatin1String(CONDITION_QMLFILE), sourceLine, 1, true, QLatin1String("a > 10")); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + //Get the frame index + { + const QJsonObject body = m_client->response().body.toObject(); + int frameIndex = body.value("index").toInt(); + + //Verify the value of 'result' + m_client->evaluate(QLatin1String("a"),frameIndex); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + } + + const QJsonObject body = m_client->response().body.toObject(); + QVERIFY(!body.isEmpty()); + QJsonValue val = body.value("value"); + QVERIFY(val.isDouble()); + + const int a = val.toInt(); + QVERIFY(a > out); +} + +void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() +{ + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, QUIT_QMLFILE), ConnectSuccess); + + int sourceLine = 36; + + m_client->setBreakpoint(QLatin1String(QUIT_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(QUIT_QMLFILE)); + + m_client->continueDebugging(QV4DebugClient::Continue); + QVERIFY(m_process->waitForFinished()); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + +void tst_QQmlDebugJS::setBreakpointWhenAttaching() +{ + int sourceLine = 35; + QCOMPARE(init(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); + + m_client->connect(); + + QSKIP("\nThe breakpoint may not hit because the engine may run in JIT mode or not have debug\n" + "instructions, as we've connected in non-blocking mode above. That means we may have\n" + "connected after the engine was already running, with all the QML already compiled."); + + //The breakpoint is in a timer loop so we can set it after connect(). + m_client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine); + + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); +} + +void tst_QQmlDebugJS::clearBreakpoint() +{ + //void clearBreakpoint(int breakpoint); + QFETCH(bool, qmlscene); + + int sourceLine1 = 37; + int sourceLine2 = 38; + QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + + m_client->connect(); + //The breakpoints are in a timer loop so we can set them after connect(). + //Furthermore the breakpoints should be hit in the right order because setting of breakpoints + //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) + m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); + m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine2, -1, true); + + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + { + //Will hit 1st brakpoint, change this breakpoint enable = false + const QJsonObject body = m_client->response().body.toObject(); + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); + + int breakpoint = breakpointsHit.at(0).toInt(); + m_client->clearBreakpoint(breakpoint); + + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + //Continue with debugging + m_client->continueDebugging(QV4DebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + //Continue with debugging + m_client->continueDebugging(QV4DebugClient::Continue); + } + + //Should stop at 2nd breakpoint + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + { + const QJsonObject body = m_client->response().body.toObject(); + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + } +} + +void tst_QQmlDebugJS::changeBreakpoint() +{ + //void clearBreakpoint(int breakpoint); + QFETCH(bool, qmlscene); + + int sourceLine2 = 37; + int sourceLine1 = 38; + QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + + bool isStopped = false; + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; }); + + auto continueDebugging = [&]() { + m_client->continueDebugging(QV4DebugClient::Continue); + isStopped = false; + }; + + m_client->connect(); + + auto extractBody = [&]() { + return m_client->response().body.toObject(); + }; + + auto extractBreakPointId = [&](const QJsonObject &body) { + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); + if (breakpointsHit.size() != 1) + return -1; + return breakpointsHit[0].toInt(); + }; + + auto setBreakPoint = [&](int sourceLine, bool enabled) { + int id = -1; + auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { + id = extractBody().value("breakpoint").toInt(); + }); + + m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine, -1, enabled); + bool success = QTest::qWaitFor([&]() { return id >= 0; }); + Q_UNUSED(success); + + QObject::disconnect(connection); + return id; + }; + + //The breakpoints are in a timer loop so we can set them after connect(). + //Furthermore the breakpoints should be hit in the right order because setting of breakpoints + //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) + const int breakpoint1 = setBreakPoint(sourceLine1, false); + QVERIFY(breakpoint1 >= 0); + + const int breakpoint2 = setBreakPoint(sourceLine2, true); + QVERIFY(breakpoint2 >= 0); + + auto verifyBreakpoint = [&](int sourceLine, int breakpointId) { + QTRY_VERIFY_WITH_TIMEOUT(isStopped, 30000); + const QJsonObject body = extractBody(); + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(extractBreakPointId(body), breakpointId); + }; + + verifyBreakpoint(sourceLine2, breakpoint2); + + continueDebugging(); + verifyBreakpoint(sourceLine2, breakpoint2); + + m_client->changeBreakpoint(breakpoint2, false); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + m_client->changeBreakpoint(breakpoint1, true); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + continueDebugging(); + verifyBreakpoint(sourceLine1, breakpoint1); + + continueDebugging(); + verifyBreakpoint(sourceLine1, breakpoint1); + + m_client->changeBreakpoint(breakpoint2, true); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + m_client->changeBreakpoint(breakpoint1, false); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + for (int i = 0; i < 3; ++i) { + continueDebugging(); + verifyBreakpoint(sourceLine2, breakpoint2); + } +} + +void tst_QQmlDebugJS::setExceptionBreak() +{ + //void setExceptionBreak(QString type, bool enabled = false); + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess); + m_client->setExceptionBreak(QV4DebugClient::All,true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); +} + +void tst_QQmlDebugJS::stepNext() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine = 37; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->continueDebugging(QV4DebugClient::Next); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(STEPACTION_QMLFILE)); +} + +static QJsonObject responseBody(QV4DebugClient *client) +{ + return client->response().body.toObject(); +} + +void tst_QQmlDebugJS::stepIn() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine = 41; + int actualLine = 36; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine); + + m_client->continueDebugging(QV4DebugClient::In); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::stepOut() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine = 37; + int actualLine = 41; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine); + + m_client->continueDebugging(QV4DebugClient::Out); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::continueDebugging() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine1 = 41; + int sourceLine2 = 38; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->continueDebugging(QV4DebugClient::Continue); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = responseBody(m_client); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::backtrace() +{ + //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->backtrace(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getFrameDetails() +{ + //void frame(int number = -1); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->frame(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getScopeDetails() +{ + //void scope(int number = -1, int frameNumber = -1); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->scope(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::evaluateInGlobalScope() +{ + //void evaluate(QString expr, int frame = -1); + QCOMPARE(init(true), ConnectSuccess); + + m_client->connect(); + + for (int i = 0; i < 10; ++i) { + // The engine might not be initialized, yet. We just try until it shows up. + m_client->evaluate(QLatin1String("console.log('Hello World')")); + if (waitForClientSignal(SIGNAL(result()), 500)) + break; + } + + //Verify the return value of 'console.log()', which is "undefined" + QCOMPARE(responseBody(m_client).value("type").toString(), QLatin1String("undefined")); +} + +void tst_QQmlDebugJS::evaluateInLocalScope() +{ + //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->frame(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + { + //Get the frame index + const QJsonObject body = responseBody(m_client); + int frameIndex = body.value("index").toInt(); + m_client->evaluate(QLatin1String("root.a"), frameIndex); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + } + + { + //Verify the value of 'timer.interval' + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("value").toInt(),10); + } +} + +void tst_QQmlDebugJS::evaluateInContext() +{ + m_connection = new QQmlDebugConnection(); + m_process = new QQmlDebugProcess( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + m_client = new QV4DebugClient(m_connection); + QScopedPointer<QQmlEngineDebugClient> engineClient(new QQmlEngineDebugClient(m_connection)); + m_process->start(QStringList() << QLatin1String(BLOCKMODE) << testFile(ONCOMPLETED_QMLFILE)); + + QVERIFY(m_process->waitForSessionStart()); + + m_connection->connectToHost("127.0.0.1", m_process->debugPort()); + QVERIFY(m_connection->waitForConnected()); + + QTRY_COMPARE(m_client->state(), QQmlEngineDebugClient::Enabled); + QTRY_COMPARE(engineClient->state(), QQmlEngineDebugClient::Enabled); + m_client->connect(); + + // "a" not accessible without extra context + m_client->evaluate(QLatin1String("a + 10"), -1, -1); + QVERIFY(waitForClientSignal(SIGNAL(failure()))); + + bool success = false; + engineClient->queryAvailableEngines(&success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); + + QVERIFY(engineClient->engines().count()); + engineClient->queryRootContexts(engineClient->engines()[0], &success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); + + auto contexts = engineClient->rootContext().contexts; + QCOMPARE(contexts.count(), 1); + auto objects = contexts[0].objects; + QCOMPARE(objects.count(), 1); + engineClient->queryObjectRecursive(objects[0], &success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); + auto object = engineClient->object(); + + // "a" accessible in context of surrounding object + m_client->evaluate(QLatin1String("a + 10"), -1, object.debugId); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 20); +} + +void tst_QQmlDebugJS::getScripts() +{ + //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + + m_client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->scripts(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + const QJsonArray scripts = m_client->response().body.toArray(); + + QCOMPARE(scripts.count(), 1); + QVERIFY(scripts.first().toObject()[QStringLiteral("name")].toString() + .endsWith(QStringLiteral("data/test.qml"))); +} + +void tst_QQmlDebugJS::encodeQmlScope() +{ + QString file(ENCODEQMLSCOPE_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + int numFrames = 0; + int numExpectedScopes = 0; + int numReceivedScopes = 0; + bool isStopped = false; + bool scopesFailed = false; + + QObject::connect(m_client.data(), &QV4DebugClient::failure, this, [&]() { + qWarning() << "received failure" << m_client->response().body; + scopesFailed = true; + m_process->stop(); + numFrames = 2; + isStopped = false; + }); + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + m_client->frame(); + isStopped = true; + }); + + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + const QV4DebugClient::Response value = m_client->response(); + + if (value.command == QString("scope")) { + // If the scope commands fail we get a failure() signal above. + if (++numReceivedScopes == numExpectedScopes) { + m_client->continueDebugging(QV4DebugClient::Continue); + isStopped = false; + } + } else if (value.command == QString("frame")) { + + // We want at least a global scope and some kind of local scope here. + const QJsonArray scopes = value.body.toObject().value("scopes").toArray(); + if (scopes.count() < 2) + scopesFailed = true; + + for (const QJsonValue &scope : scopes) { + ++numExpectedScopes; + m_client->scope(scope.toObject().value("index").toInt()); + } + + ++numFrames; + } + }); + + m_client->setBreakpoint(file, 6); + m_client->setBreakpoint(file, 8); + m_client->connect(); + + QTRY_COMPARE(numFrames, 2); + QVERIFY(numExpectedScopes > 3); + QVERIFY(!scopesFailed); + QTRY_VERIFY(!isStopped); + QCOMPARE(numReceivedScopes, numExpectedScopes); +} + +void tst_QQmlDebugJS::breakOnAnchor() +{ + QString file(BREAKONANCHOR_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + int breaks = 0; + bool stopped = false; + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + stopped = true; + ++breaks; + m_client->evaluate("this", 0, -1); + }); + + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + if (stopped) { + m_client->continueDebugging(QV4DebugClient::Continue); + stopped = false; + } + }); + + QObject::connect(m_client.data(), &QV4DebugClient::failure, this, [&]() { + qWarning() << "received failure" << m_client->response().body; + }); + + m_client->setBreakpoint(file, 34); + m_client->setBreakpoint(file, 37); + + QTRY_COMPARE(m_process->state(), QProcess::Running); + + m_client->connect(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); + + QCOMPARE(breaks, 2); +} + +QList<QQmlDebugClient *> tst_QQmlDebugJS::createClients() +{ + m_client = new QV4DebugClient(m_connection); + return QList<QQmlDebugClient *>({m_client}); +} + +void tst_QQmlDebugJS::targetData() +{ + QTest::addColumn<bool>("qmlscene"); + QTest::newRow("custom") << false; + QTest::newRow("qmlscene") << true; +} + +bool tst_QQmlDebugJS::waitForClientSignal(const char *signal, int timeout) +{ + return QQmlDebugTest::waitForSignal(m_client.data(), signal, timeout); +} + +void tst_QQmlDebugJS::checkVersionParameters() +{ + const QV4DebugClient::Response value = m_client->response(); + QCOMPARE(value.command, QString("version")); + const QJsonObject body = value.body.toObject(); + QCOMPARE(body.value("UnpausedEvaluate").toBool(), true); + QCOMPARE(body.value("ContextEvaluate").toBool(), true); + QCOMPARE(body.value("ChangeBreakpoint").toBool(), true); +} + +QTEST_MAIN(tst_QQmlDebugJS) + +#include "tst_qqmldebugjs.moc" + diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.cpp b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp index 6a4ec5cc75..6a4ec5cc75 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.cpp +++ b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp diff --git a/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.pro b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.pro new file mode 100644 index 0000000000..a31da57054 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.pro @@ -0,0 +1,9 @@ +QT += qml testlib +macos:CONFIG -= app_bundle +INCLUDEPATH += ../shared +SOURCES += qqmldebugjsserver.cpp +DEFINES += QT_QML_DEBUG_NO_WARNING + +target.path = $$[QT_INSTALL_TESTS]/qqmldebugjsserver +INSTALLS += target + diff --git a/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro b/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro index 860d39cca4..1dc9de8f34 100644 --- a/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro +++ b/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro @@ -7,7 +7,6 @@ HEADERS += ../shared/qqmldebugtestservice.h SOURCES += tst_qqmldebuglocal.cpp \ ../shared/qqmldebugtestservice.cpp -INCLUDEPATH += ../shared include(../shared/debugutil.pri) QT += qml-private testlib gui-private core-private diff --git a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp index 8d21a8a45a..4e47c92c2a 100644 --- a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp +++ b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp @@ -32,6 +32,8 @@ #include <private/qqmldebugconnector_p.h> #include <private/qqmldebugconnection_p.h> +#include <QtQml/qqmlengine.h> + #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> #include <QtNetwork/qhostaddress.h> @@ -61,10 +63,8 @@ private slots: void tst_QQmlDebugLocal::initTestCase() { - fileName = QString::fromLatin1("tst_QQmlDebugLocal%1").arg(std::time(0)); + fileName = QString::fromLatin1("tst_QQmlDebugLocal%1").arg(std::time(nullptr)); QQmlDebugConnector::setPluginKey("QQmlDebugServer"); - QTest::ignoreMessage(QtWarningMsg, - "QML debugger: Cannot set plugin key after loading the plugin."); m_service = new QQmlDebugTestService("tst_QQmlDebugLocal::handshake()"); const QString waitingMsg = QString("QML Debugger: Connecting to socket %1...").arg(fileName); @@ -116,7 +116,7 @@ void tst_QQmlDebugLocal::state() QQmlDebugClient client2("tst_QQmlDebugLocal::state()", m_conn); QCOMPARE(client2.state(), QQmlDebugClient::NotConnected); - QQmlDebugClient client3("tst_QQmlDebugLocal::state3()", 0); + QQmlDebugClient client3("tst_QQmlDebugLocal::state3()", nullptr); QCOMPARE(client3.state(), QQmlDebugClient::NotConnected); } diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess.pro b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess.pro new file mode 100644 index 0000000000..331d87b9f1 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = qqmldebugprocess qqmldebugprocessprocess + +qqmldebugprocess.depends = qqmldebugprocessprocess diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/qqmldebugprocess.pro b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/qqmldebugprocess.pro new file mode 100644 index 0000000000..9bea2d222c --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/qqmldebugprocess.pro @@ -0,0 +1,14 @@ +CONFIG += testcase +TARGET = tst_qqmldebugprocess +QT = core testlib +CONFIG -= debug_and_release_target +macos:CONFIG -= app_bundle + +SOURCES += \ + ../../shared/qqmldebugprocess.cpp \ + tst_qqmldebugprocess.cpp + +HEADERS += \ + ../../shared/qqmldebugprocess_p.h + +INCLUDEPATH += ../../shared diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp new file mode 100644 index 0000000000..35bd912d9b --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 <qqmldebugprocess_p.h> +#include <QtTest> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qscopedpointer.h> + +class tst_QQmlDebugProcess : public QObject +{ + Q_OBJECT + +private slots: + void sessionStart_data(); + void sessionStart(); +}; + +void tst_QQmlDebugProcess::sessionStart_data() +{ + QTest::addColumn<int>("delay"); + QTest::addColumn<QString>("arg"); + QTest::addColumn<bool>("sessionExpected"); + QTest::addColumn<bool>("outputExpected"); + + QTest::addRow("synchronous / waiting") << -1 + << "QML Debugger: Waiting for connection on port 2423..." + << true << false; + QTest::addRow("synchronous / failed") << -1 << "QML Debugger: Unable to listen to port 242." + << false << false; + QTest::addRow("synchronous / unknown") << -1 << "QML Debugger: You don't know this string." + << false << true; + QTest::addRow("synchronous / appout") << -1 << "Output from app itself." + << false << true; + + QTest::addRow("no delay / waiting") << 0 + << "QML Debugger: Waiting for connection on port 2423..." + << true << false; + QTest::addRow("no delay / failed") << 0 << "QML Debugger: Unable to listen to port 242." + << false << false; + QTest::addRow("no delay / unknown") << 0 << "QML Debugger: You don't know this string." + << false << true; + QTest::addRow("no delay / appout") << 0 << "Output from app itself." + << false << true; + + QTest::addRow("delay / waiting") << 1000 + << "QML Debugger: Waiting for connection on port 2423..." + << true << false; + QTest::addRow("delay / failed") << 1000 << "QML Debugger: Unable to listen to port 242." + << false << false; + QTest::addRow("delay / unknown") << 1000 << "QML Debugger: You don't know this string." + << false << true; + QTest::addRow("delay / appout") << 1000 << "Output from app itself." + << false << true; +} + +void tst_QQmlDebugProcess::sessionStart() +{ + QFETCH(int, delay); + QFETCH(QString, arg); + QFETCH(bool, sessionExpected); + QFETCH(bool, outputExpected); + + QScopedPointer<QQmlDebugProcess> process( + new QQmlDebugProcess(QCoreApplication::applicationDirPath() + + QLatin1String("/qqmldebugprocessprocess"), this)); + QVERIFY(process); + + bool outputReceived = false; + connect(process.data(), &QQmlDebugProcess::readyReadStandardOutput, this, [&]() { + QVERIFY(outputExpected); + QVERIFY(!outputReceived); + QCOMPARE(process->output().trimmed(), arg); + outputReceived = true; + QTimer::singleShot(qMax(delay, 0), process.data(), &QQmlDebugProcess::stop); + }); + + if (!outputExpected && !sessionExpected) + QTest::ignoreMessage(QtWarningMsg, "App was unable to bind to port!"); + process->start(QStringList({arg})); + + bool done = false; + auto wait = [&](){ + QCOMPARE(process->waitForSessionStart(), sessionExpected); + QCOMPARE(outputReceived, outputExpected); + process->stop(); + done = true; + }; + + if (delay < 0) + wait(); + else + QTimer::singleShot(delay, process.data(), wait); + + QTRY_VERIFY(done); + QCOMPARE(process->state(), QProcess::NotRunning); +} + +QTEST_MAIN(tst_QQmlDebugProcess) + +#include "tst_qqmldebugprocess.moc" diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp new file mode 100644 index 0000000000..21cce53fc3 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 <QtCore/qdebug.h> +#include <QtCore/qcoreapplication.h> + +// Process that just outputs a fake "Waiting" message. +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + if (argc > 1) + qDebug() << argv[1]; + return app.exec(); +} diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.pro b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.pro new file mode 100644 index 0000000000..a8eb4885d4 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.pro @@ -0,0 +1,11 @@ +QT = core +macos:CONFIG -= app_bundle +CONFIG -= debug_and_release_target +CONFIG += console +SOURCES += qqmldebugprocessprocess.cpp + +DESTDIR = ../qqmldebugprocess + +target.path = $$[QT_INSTALL_TESTS]/tst_qqmldebugprocess +INSTALLS += target + diff --git a/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro b/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro index 79cbe52331..3101d09ea5 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro +++ b/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro @@ -7,8 +7,6 @@ HEADERS += ../shared/qqmldebugtestservice.h SOURCES += tst_qqmldebugservice.cpp \ ../shared/qqmldebugtestservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp index 8092faba04..3557940386 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp +++ b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp @@ -29,6 +29,7 @@ #include "qqmldebugtestservice.h" #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmldebugclient_p.h> @@ -74,16 +75,14 @@ void tst_QQmlDebugService::initTestCase() QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); QQmlDebugConnector::setServices(QStringList() << QStringLiteral("tst_QQmlDebugService")); - QTest::ignoreMessage(QtWarningMsg, - "QML debugger: Cannot set plugin key after loading the plugin."); m_service = new QQmlDebugTestService("tst_QQmlDebugService", 2); foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT); QTest::ignoreMessage(QtDebugMsg, waitingMsg.toLatin1().constData()); @@ -105,10 +104,13 @@ void tst_QQmlDebugService::initTestCase() void tst_QQmlDebugService::checkPortRange() { - QQmlDebugConnection *connection1 = new QQmlDebugConnection(); - QQmlDebugProcess *process1 = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + QScopedPointer<QQmlDebugConnection> connection1(new QQmlDebugConnection()); + QScopedPointer<QQmlDebugProcess> process1( + new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + + "/qmlscene", this)); - process1->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") << testFile("test.qml")); + process1->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") + << testFile("test.qml")); if (!process1->waitForSessionStart()) QFAIL("could not launch application, or did not get 'Waiting for connection'."); @@ -119,10 +121,13 @@ void tst_QQmlDebugService::checkPortRange() QFAIL("could not connect to host!"); // Second instance - QQmlDebugConnection *connection2 = new QQmlDebugConnection(); - QQmlDebugProcess *process2 = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + QScopedPointer<QQmlDebugConnection> connection2(new QQmlDebugConnection()); + QScopedPointer<QQmlDebugProcess> process2( + new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + + "/qmlscene", this)); - process2->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") << testFile("test.qml")); + process2->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") + << testFile("test.qml")); if (!process2->waitForSessionStart()) QFAIL("could not launch application, or did not get 'Waiting for connection'."); @@ -131,11 +136,6 @@ void tst_QQmlDebugService::checkPortRange() connection2->connectToHost("127.0.0.1", port2); if (!connection2->waitForConnected()) QFAIL("could not connect to host!"); - - delete connection1; - delete process1; - delete connection2; - delete process2; } void tst_QQmlDebugService::name() @@ -207,7 +207,7 @@ void tst_QQmlDebugService::checkSupportForDataStreamVersion() void tst_QQmlDebugService::idForObject() { - QCOMPARE(QQmlDebugService::idForObject(0), -1); + QCOMPARE(QQmlDebugService::idForObject(nullptr), -1); QObject *objA = new QObject; @@ -229,15 +229,15 @@ void tst_QQmlDebugService::idForObject() void tst_QQmlDebugService::objectForId() { - QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(0)); - QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(0)); + QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(nullptr)); + QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(nullptr)); QObject *obj = new QObject; int id = QQmlDebugService::idForObject(obj); QCOMPARE(QQmlDebugService::objectForId(id), obj); delete obj; - QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(0)); + QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(nullptr)); } void tst_QQmlDebugService::checkSupportForOldDataStreamVersion() diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro b/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro index 2518650493..36957628b2 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro +++ b/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro @@ -4,13 +4,11 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlenginecontrol.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* -QT += core qml testlib gui-private core-private +QT += core testlib gui-private core-private OTHER_FILES += \ data/test.qml \ diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp index 3f8731ce6b..a8c43b1c75 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp +++ b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp @@ -37,9 +37,6 @@ #include <QtTest/qtest.h> #include <QtCore/qlibraryinfo.h> -#define STR_PORT_FROM "13773" -#define STR_PORT_TO "13783" - class QQmlEngineBlocker : public QObject { Q_OBJECT @@ -64,77 +61,37 @@ void QQmlEngineBlocker::blockEngine(int engineId, const QString &name) static_cast<QQmlEngineControlClient *>(parent())->blockEngine(engineId); } -class tst_QQmlEngineControl : public QQmlDataTest +class tst_QQmlEngineControl : public QQmlDebugTest { Q_OBJECT -public: - tst_QQmlEngineControl() - : m_process(0) - , m_connection(0) - , m_client(0) - {} - private: - QQmlDebugProcess *m_process; - QQmlDebugConnection *m_connection; - QQmlEngineControlClient *m_client; + ConnectResult connect(const QString &testFile, bool restrictServices); + QList<QQmlDebugClient *> createClients() override; - void connect(const QString &testFile, bool restrictServices); void engine_data(); + QPointer<QQmlEngineControlClient> m_client; private slots: - void cleanup(); - void startEngine_data(); void startEngine(); void stopEngine_data(); void stopEngine(); }; -void tst_QQmlEngineControl::connect(const QString &testFile, bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlEngineControl::connect(const QString &file, + bool restrictServices) { - const QString executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"; - QStringList arguments; - arguments << QString::fromLatin1("-qmljsdebugger=port:%1,%2,block%3") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(restrictServices ? QStringLiteral(",services:EngineControl") : QString()); - - arguments << QQmlDataTest::instance()->testFile(testFile); - - m_process = new QQmlDebugProcess(executable, this); - m_process->start(QStringList() << arguments); - QVERIFY2(m_process->waitForSessionStart(), "Could not launch application, or did not get 'Waiting for connection'."); - - m_connection = new QQmlDebugConnection(); - m_client = new QQmlEngineControlClient(m_connection); - new QQmlEngineBlocker(m_client); - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection); - - const int port = m_process->debugPort(); - m_connection->connectToHost(QLatin1String("127.0.0.1"), port); - - QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); + return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", + restrictServices ? QStringLiteral("EngineControl") : QString(), + testFile(file), true); } -void tst_QQmlEngineControl::cleanup() +QList<QQmlDebugClient *> tst_QQmlEngineControl::createClients() { - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << (m_process ? m_process->state() : QLatin1String("null")); - qDebug() << "Application Output:" << (m_process ? m_process->output() : QLatin1String("null")); - qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(m_connection); - qDebug() << "Client State:" << QQmlDebugTest::clientStateString(m_client); - } - delete m_process; - m_process = 0; - delete m_client; - m_client = 0; - delete m_connection; - m_connection = 0; + m_client = new QQmlEngineControlClient(m_connection); + new QQmlEngineBlocker(m_client); + return QList<QQmlDebugClient *>({m_client}); } void tst_QQmlEngineControl::engine_data() @@ -152,14 +109,16 @@ void tst_QQmlEngineControl::startEngine_data() void tst_QQmlEngineControl::startEngine() { QFETCH(bool, restrictMode); - - connect("test.qml", restrictMode); + QCOMPARE(connect("test.qml", restrictMode), ConnectSuccess); QTRY_VERIFY(!m_client->blockedEngines().empty()); m_client->releaseEngine(m_client->blockedEngines().last()); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineAdded(int,QString))), "No engine start message received in time."); + + QVERIFY(m_client->blockedEngines().isEmpty()); } void tst_QQmlEngineControl::stopEngine_data() @@ -171,19 +130,23 @@ void tst_QQmlEngineControl::stopEngine() { QFETCH(bool, restrictMode); - connect("exit.qml", restrictMode); + QCOMPARE(connect("exit.qml", restrictMode), ConnectSuccess); QTRY_VERIFY(!m_client->blockedEngines().empty()); m_client->releaseEngine(m_client->blockedEngines().last()); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineAdded(int,QString))), "No engine start message received in time."); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineAboutToBeRemoved(int,QString))), "No engine about to stop message received in time."); m_client->releaseEngine(m_client->blockedEngines().last()); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineRemoved(int,QString))), "No engine stop message received in time."); + QVERIFY(m_client->blockedEngines().isEmpty()); } QTEST_MAIN(tst_QQmlEngineControl) diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro index e11ccdc6ca..454a1e3ab0 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro @@ -1,15 +1,11 @@ CONFIG += testcase TARGET = tst_qqmlenginedebuginspectorintegration -QT += qml testlib gui-private core-private +QT += testlib gui-private core-private osx:CONFIG -= app_bundle SOURCES += tst_qqmlenginedebuginspectorintegration.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) -include(../shared/qqmlinspectorclient.pri) -include(../shared/qqmlenginedebugclient.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp index 940f89e936..980e2be1f1 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp @@ -26,12 +26,12 @@ ** ****************************************************************************/ -#include "qqmlinspectorclient.h" -#include "qqmlenginedebugclient.h" #include "../shared/debugutil_p.h" #include "../../../shared/util.h" #include <private/qqmldebugconnection_p.h> +#include <private/qqmlenginedebugclient_p.h> +#include <private/qqmlinspectorclient_p.h> #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> @@ -41,35 +41,21 @@ #include <QtCore/qthread.h> #include <QtCore/qlibraryinfo.h> -#define STR_PORT_FROM "3776" -#define STR_PORT_TO "3786" - -class tst_QQmlEngineDebugInspectorIntegration : public QQmlDataTest +class tst_QQmlEngineDebugInspectorIntegration : public QQmlDebugTest { Q_OBJECT -public: - tst_QQmlEngineDebugInspectorIntegration() - : m_process(0) - , m_inspectorClient(0) - , m_engineDebugClient(0) - , m_recipient(0) - { - } - - private: - void init(bool restrictServices); - QmlDebugObjectReference findRootObject(); + ConnectResult init(bool restrictServices); + QList<QQmlDebugClient *> createClients() override; - QQmlDebugProcess *m_process; - QQmlInspectorClient *m_inspectorClient; - QQmlEngineDebugClient *m_engineDebugClient; - QQmlInspectorResultRecipient *m_recipient; + QQmlEngineDebugObjectReference findRootObject(); -private slots: - void cleanup(); + QPointer<QQmlInspectorClient> m_inspectorClient; + QPointer<QQmlEngineDebugClient> m_engineDebugClient; + QPointer<QQmlInspectorResultRecipient> m_recipient; +private slots: void connect_data(); void connect(); void objectLocationLookup(); @@ -79,73 +65,42 @@ private slots: void destroyObject(); }; -QmlDebugObjectReference tst_QQmlEngineDebugInspectorIntegration::findRootObject() +QQmlEngineDebugObjectReference tst_QQmlEngineDebugInspectorIntegration::findRootObject() { bool success = false; m_engineDebugClient->queryAvailableEngines(&success); if (!QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))) - return QmlDebugObjectReference(); + return QQmlEngineDebugObjectReference(); - m_engineDebugClient->queryRootContexts(m_engineDebugClient->engines()[0].debugId, &success); + m_engineDebugClient->queryRootContexts(m_engineDebugClient->engines()[0], &success); if (!QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))) - return QmlDebugObjectReference(); + return QQmlEngineDebugObjectReference(); int count = m_engineDebugClient->rootContext().contexts.count(); m_engineDebugClient->queryObject( m_engineDebugClient->rootContext().contexts[count - 1].objects[0], &success); if (!QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))) - return QmlDebugObjectReference(); + return QQmlEngineDebugObjectReference(); return m_engineDebugClient->object(); } -void tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices) { - const QString argument = QString::fromLatin1("-qmljsdebugger=port:%1,%2,block%3") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(restrictServices ? QStringLiteral(",services:QmlDebugger,QmlInspector") : - QString()); - - m_process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", - this); - m_process->start(QStringList() << argument << testFile("qtquick2.qml")); - QVERIFY2(m_process->waitForSessionStart(), - "Could not launch application, or did not get 'Waiting for connection'."); - - QQmlDebugConnection *m_connection = new QQmlDebugConnection(this); - m_inspectorClient = new QQmlInspectorClient(m_connection); - m_engineDebugClient = new QQmlEngineDebugClient(m_connection); - m_recipient = new QQmlInspectorResultRecipient(this); - QObject::connect(m_inspectorClient, &QQmlInspectorClient::responseReceived, - m_recipient, &QQmlInspectorResultRecipient::recordResponse); - - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection); - - m_connection->connectToHost(QLatin1String("127.0.0.1"), m_process->debugPort()); - QVERIFY(m_connection->waitForConnected()); - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); - - QTRY_COMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); - QTRY_COMPARE(m_engineDebugClient->state(), QQmlDebugClient::Enabled); + return QQmlDebugTest::connect( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + restrictServices ? QStringLiteral("QmlDebugger,QmlInspector") : QString(), + testFile("qtquick2.qml"), true); } -void tst_QQmlEngineDebugInspectorIntegration::cleanup() +QList<QQmlDebugClient *> tst_QQmlEngineDebugInspectorIntegration::createClients() { - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << m_process->state(); - qDebug() << "Application Output:" << m_process->output(); - } - delete m_process; - m_process = 0; - delete m_engineDebugClient; - m_engineDebugClient = 0; - delete m_inspectorClient; - m_inspectorClient = 0; - delete m_recipient; - m_recipient = 0; + m_inspectorClient = new QQmlInspectorClient(m_connection); + m_engineDebugClient = new QQmlEngineDebugClient(m_connection); + m_recipient = new QQmlInspectorResultRecipient(m_inspectorClient); + QObject::connect(m_inspectorClient.data(), &QQmlInspectorClient::responseReceived, + m_recipient.data(), &QQmlInspectorResultRecipient::recordResponse); + return QList<QQmlDebugClient *>({m_inspectorClient, m_engineDebugClient}); } void tst_QQmlEngineDebugInspectorIntegration::connect_data() @@ -158,15 +113,15 @@ void tst_QQmlEngineDebugInspectorIntegration::connect_data() void tst_QQmlEngineDebugInspectorIntegration::connect() { QFETCH(bool, restrictMode); - init(restrictMode); + QCOMPARE(init(restrictMode), ConnectSuccess); } void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() { - init(true); + QCOMPARE(init(true), ConnectSuccess); bool success = false; - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); int lineNumber = rootObject.source.lineNumber; @@ -176,7 +131,7 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))); - foreach (QmlDebugObjectReference child, rootObject.children) { + foreach (QQmlEngineDebugObjectReference child, rootObject.children) { success = false; lineNumber = child.source.lineNumber; columnNumber = child.source.columnNumber; @@ -189,11 +144,12 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() void tst_QQmlEngineDebugInspectorIntegration::select() { - init(true); - QmlDebugObjectReference rootObject = findRootObject(); + QCOMPARE(init(true), ConnectSuccess); + + QQmlEngineDebugObjectReference rootObject = findRootObject(); QList<int> childIds; int requestId = 0; - foreach (const QmlDebugObjectReference &child, rootObject.children) { + foreach (const QQmlEngineDebugObjectReference &child, rootObject.children) { requestId = m_inspectorClient->select(QList<int>() << child.debugId); QTRY_COMPARE(m_recipient->lastResponseId, requestId); QVERIFY(m_recipient->lastResult); @@ -206,7 +162,7 @@ void tst_QQmlEngineDebugInspectorIntegration::select() void tst_QQmlEngineDebugInspectorIntegration::createObject() { - init(true); + QCOMPARE(init(true), ConnectSuccess); QString qml = QLatin1String("Rectangle {\n" " id: xxxyxxx\n" @@ -215,7 +171,7 @@ void tst_QQmlEngineDebugInspectorIntegration::createObject() " color: \"blue\"\n" "}"); - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); QCOMPARE(rootObject.children.length(), 2); @@ -233,8 +189,10 @@ void tst_QQmlEngineDebugInspectorIntegration::createObject() void tst_QQmlEngineDebugInspectorIntegration::moveObject() { - init(true); - QmlDebugObjectReference rootObject = findRootObject(); + QCOMPARE(init(true), ConnectSuccess); + + QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); QCOMPARE(rootObject.children.length(), 2); @@ -256,8 +214,10 @@ void tst_QQmlEngineDebugInspectorIntegration::moveObject() void tst_QQmlEngineDebugInspectorIntegration::destroyObject() { - init(true); - QmlDebugObjectReference rootObject = findRootObject(); + QCOMPARE(init(true), ConnectSuccess); + + QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); QCOMPARE(rootObject.children.length(), 2); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro b/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro index 06250d9940..5ff65ba276 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro @@ -5,11 +5,8 @@ osx:CONFIG -= app_bundle SOURCES += \ tst_qqmlenginedebugservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) -include(../shared/qqmlenginedebugclient.pri) include(../shared/debugutil.pri) DEFINES += QT_QML_DEBUG_NO_WARNING -QT += core-private qml-private quick-private testlib gui-private +QT += quick qml-private testlib diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index d9a4777115..99c90c142f 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -26,7 +26,6 @@ ** ****************************************************************************/ -#include "qqmlenginedebugclient.h" #include "debugutil_p.h" #include "../../../shared/util.h" @@ -36,6 +35,7 @@ #include <private/qqmlmetatype_p.h> #include <private/qqmlproperty_p.h> #include <private/qqmldebugconnection_p.h> +#include <private/qqmlenginedebugclient_p.h> #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> @@ -53,11 +53,14 @@ #include <QtCore/qdebug.h> #include <QtCore/qthread.h> #include <QtCore/qabstractitemmodel.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> #define QVERIFYOBJECT(statement) \ do {\ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\ - return QmlDebugObjectReference();\ + return QQmlEngineDebugObjectReference();\ }\ } while (0) @@ -77,28 +80,64 @@ class CustomTypes : public QObject Q_OBJECT Q_PROPERTY(QModelIndex modelIndex READ modelIndex) public: - CustomTypes(QObject *parent = 0) : QObject(parent) {} + CustomTypes(QObject *parent = nullptr) : QObject(parent) {} QModelIndex modelIndex() { return QModelIndex(); } }; +class JsonTest : public QObject +{ + Q_OBJECT + Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged) + +public: + JsonTest(QObject *parent = 0) : QObject(parent) + { + m_data["foo"] = QJsonValue(12); + m_data["ttt"] = QJsonArray({4, 5, 4, 3, 2}); + m_data["a"] = QJsonValue(QJsonValue::Null); + m_data["b"] = QJsonValue(QJsonValue::Undefined); + m_data["c"] = QJsonValue("fffff"); + } + + QJsonObject data() const { return m_data; } + +signals: + void dataChanged(const QJsonObject &data); + +public slots: + void setData(const QJsonObject &data) + { + if (data != m_data) { + m_data = data; + emit dataChanged(data); + } + } + +private: + QJsonObject m_data; +}; + + class tst_QQmlEngineDebugService : public QObject { Q_OBJECT public: - tst_QQmlEngineDebugService() : m_conn(0), m_dbg(0), m_engine(0), m_rootItem(0) {} + tst_QQmlEngineDebugService() : m_conn(nullptr), m_dbg(nullptr), m_engine(nullptr), m_rootItem(nullptr) {} private: - QmlDebugObjectReference findRootObject(int context = 0, + QQmlEngineDebugObjectReference findRootObject(int context = 0, bool recursive = false); - QmlDebugPropertyReference findProperty( - const QList<QmlDebugPropertyReference> &props, + QQmlEngineDebugPropertyReference findProperty( + const QList<QQmlEngineDebugPropertyReference> &props, const QString &name) const; void recursiveObjectTest(QObject *o, - const QmlDebugObjectReference &oref, + const QQmlEngineDebugObjectReference &oref, bool recursive) const; + void getContexts(); + QQmlDebugConnection *m_conn; QQmlEngineDebugClient *m_dbg; QQmlEngine *m_engine; @@ -137,10 +176,13 @@ private slots: void regression_QTCREATORBUG_7451(); void queryObjectWithNonStreamableTypes(); + void jsonData(); void asynchronousCreate(); + void invalidContexts(); + void createObjectOnDestruction(); }; -QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( +QQmlEngineDebugObjectReference tst_QQmlEngineDebugService::findRootObject( int context, bool recursive) { bool success = false; @@ -149,7 +191,7 @@ QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QVERIFYOBJECT(m_dbg->engines().count()); - m_dbg->queryRootContexts(m_dbg->engines()[0].debugId, &success); + m_dbg->queryRootContexts(m_dbg->engines()[0], &success); QVERIFYOBJECT(success); QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); @@ -165,18 +207,18 @@ QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( return m_dbg->object(); } -QmlDebugPropertyReference tst_QQmlEngineDebugService::findProperty( - const QList<QmlDebugPropertyReference> &props, const QString &name) const +QQmlEngineDebugPropertyReference tst_QQmlEngineDebugService::findProperty( + const QList<QQmlEngineDebugPropertyReference> &props, const QString &name) const { - foreach (const QmlDebugPropertyReference &p, props) { + foreach (const QQmlEngineDebugPropertyReference &p, props) { if (p.name == name) return p; } - return QmlDebugPropertyReference(); + return QQmlEngineDebugPropertyReference(); } void tst_QQmlEngineDebugService::recursiveObjectTest( - QObject *o, const QmlDebugObjectReference &oref, bool recursive) const + QObject *o, const QQmlEngineDebugObjectReference &oref, bool recursive) const { const QMetaObject *meta = o->metaObject(); @@ -194,8 +236,8 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( int debugId = QQmlDebugService::idForObject(child); QVERIFY(debugId >= 0); - QmlDebugObjectReference cref; - foreach (const QmlDebugObjectReference &ref, oref.children) { + QQmlEngineDebugObjectReference cref; + foreach (const QQmlEngineDebugObjectReference &ref, oref.children) { QVERIFY(!ref.className.isEmpty()); if (ref.debugId == debugId) { cref = ref; @@ -208,7 +250,7 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( recursiveObjectTest(child, cref, true); } - foreach (const QmlDebugPropertyReference &p, oref.properties) { + foreach (const QQmlEngineDebugPropertyReference &p, oref.properties) { QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o)); // signal properties are fake - they are generated from QQmlAbstractBoundSignal children @@ -226,9 +268,24 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( QCOMPARE(p.name, QString::fromUtf8(pmeta.name())); - if (pmeta.type() < QVariant::UserType && pmeta.userType() != - QMetaType::QVariant) // TODO test complex types - QCOMPARE(p.value , pmeta.read(o)); + if (pmeta.userType() == QMetaType::QObjectStar) { + const QQmlEngineDebugObjectReference ref + = qvariant_cast<QQmlEngineDebugObjectReference>(p.value); + QObject *pobj = qvariant_cast<QObject *>(pmeta.read(o)); + if (pobj) { + if (pobj->objectName().isEmpty()) + QCOMPARE(ref.name, QString("<unnamed object>")); + else + QCOMPARE(ref.name, pobj->objectName()); + } else { + QCOMPARE(ref.name, QString("<unknown value>")); + } + } else if (pmeta.type() < QVariant::UserType && 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()); + } if (p.name == "parent") QVERIFY(p.valueTypeName == "QGraphicsObject*" || @@ -248,6 +305,22 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( } } +void tst_QQmlEngineDebugService::getContexts() +{ + bool success = false; + + m_dbg->queryAvailableEngines(&success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); + + QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines(); + QCOMPARE(engines.count(), 1); + m_dbg->queryRootContexts(engines.first(), &success); + + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); +} + void tst_QQmlEngineDebugService::initTestCase() { qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement"); @@ -329,6 +402,11 @@ void tst_QQmlEngineDebugService::initTestCase() "CustomTypes {}" ; + qmlRegisterType<JsonTest>("JsonTest", 1, 0, "JsonTest"); + qml << "import JsonTest 1.0\n" + "JsonTest {}" + ; + for (int i=0; i<qml.count(); i++) { QQmlComponent component(m_engine); component.setData(qml[i], QUrl::fromLocalFile("")); @@ -364,7 +442,7 @@ void tst_QQmlEngineDebugService::cleanupTestCase() void tst_QQmlEngineDebugService::setMethodBody() { bool success; - QmlDebugObjectReference obj = findRootObject(2); + QQmlEngineDebugObjectReference obj = findRootObject(2); QVERIFY(!obj.className.isEmpty()); QObject *root = m_components.at(2); @@ -406,18 +484,18 @@ void tst_QQmlEngineDebugService::setMethodBody() void tst_QQmlEngineDebugService::watch_property() { - QmlDebugObjectReference obj = findRootObject(); + QQmlEngineDebugObjectReference obj = findRootObject(); QVERIFY(!obj.className.isEmpty()); - QmlDebugPropertyReference prop = findProperty(obj.properties, "width"); + QQmlEngineDebugPropertyReference prop = findProperty(obj.properties, "width"); bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->addWatch(prop, &success); QVERIFY(!success); delete unconnected; - m_dbg->addWatch(QmlDebugPropertyReference(), &success); + m_dbg->addWatch(QQmlEngineDebugPropertyReference(), &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->valid(), false); @@ -451,17 +529,17 @@ void tst_QQmlEngineDebugService::watch_property() void tst_QQmlEngineDebugService::watch_object() { - QmlDebugObjectReference obj = findRootObject(); + QQmlEngineDebugObjectReference obj = findRootObject(); QVERIFY(!obj.className.isEmpty()); bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->addWatch(obj, &success); QVERIFY(!success); delete unconnected; - m_dbg->addWatch(QmlDebugObjectReference(), &success); + m_dbg->addWatch(QQmlEngineDebugObjectReference(), &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->valid(), false); @@ -517,17 +595,17 @@ void tst_QQmlEngineDebugService::watch_expression() int origWidth = m_rootItem->property("width").toInt(); - QmlDebugObjectReference obj = findRootObject(); + QQmlEngineDebugObjectReference obj = findRootObject(); QVERIFY(!obj.className.isEmpty()); bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->addWatch(obj, expr, &success); QVERIFY(!success); delete unconnected; - m_dbg->addWatch(QmlDebugObjectReference(), expr, &success); + m_dbg->addWatch(QQmlEngineDebugObjectReference(), expr, &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->valid(), false); @@ -577,7 +655,7 @@ void tst_QQmlEngineDebugService::watch_expression_data() void tst_QQmlEngineDebugService::watch_context() { - QmlDebugContextReference c; + QQmlEngineDebugContextReference c; QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented"); bool success; m_dbg->addWatch(c, QString(), &success); @@ -586,7 +664,7 @@ void tst_QQmlEngineDebugService::watch_context() void tst_QQmlEngineDebugService::watch_file() { - QmlDebugFileReference f; + QQmlEngineDebugFileReference f; QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented"); bool success; m_dbg->addWatch(f, &success); @@ -597,7 +675,7 @@ void tst_QQmlEngineDebugService::queryAvailableEngines() { bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryAvailableEngines(&success); QVERIFY(!success); delete unconnected; @@ -607,10 +685,10 @@ void tst_QQmlEngineDebugService::queryAvailableEngines() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); // TODO test multiple engines - QList<QmlDebugEngineReference> engines = m_dbg->engines(); + QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines(); QCOMPARE(engines.count(), 1); - foreach (const QmlDebugEngineReference &e, engines) { + foreach (const QQmlEngineDebugEngineReference &e, engines) { QCOMPARE(e.debugId, QQmlDebugService::idForObject(m_engine)); QCOMPARE(e.name, m_engine->objectName()); } @@ -623,26 +701,26 @@ void tst_QQmlEngineDebugService::queryRootContexts() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QVERIFY(m_dbg->engines().count()); - int engineId = m_dbg->engines()[0].debugId; + const QQmlEngineDebugEngineReference engine = m_dbg->engines()[0]; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); - unconnected->queryRootContexts(engineId, &success); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + unconnected->queryRootContexts(engine, &success); QVERIFY(!success); delete unconnected; - m_dbg->queryRootContexts(engineId, &success); + m_dbg->queryRootContexts(engine, &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QQmlContext *actualContext = m_engine->rootContext(); - QmlDebugContextReference context = m_dbg->rootContext(); + QQmlEngineDebugContextReference context = m_dbg->rootContext(); QCOMPARE(context.debugId, QQmlDebugService::idForObject(actualContext)); QCOMPARE(context.name, actualContext->objectName()); // root context query sends only root object data - it doesn't fill in // the children or property info QCOMPARE(context.objects.count(), 0); - QCOMPARE(context.contexts.count(), 6); + QCOMPARE(context.contexts.count(), 7); QVERIFY(context.contexts[0].debugId >= 0); QCOMPARE(context.contexts[0].name, QString("tst_QQmlDebug_childContext")); } @@ -653,10 +731,10 @@ void tst_QQmlEngineDebugService::queryObject() bool success; - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success); QVERIFY(!success); delete unconnected; @@ -665,11 +743,11 @@ void tst_QQmlEngineDebugService::queryObject() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - QmlDebugObjectReference obj = m_dbg->object(); + QQmlEngineDebugObjectReference obj = m_dbg->object(); QVERIFY(!obj.className.isEmpty()); // check source as defined in main() - QmlDebugFileReference source = obj.source; + QQmlEngineDebugFileReference source = obj.source; QCOMPARE(source.url, QUrl::fromLocalFile("")); QCOMPARE(source.lineNumber, 3); QCOMPARE(source.columnNumber, 1); @@ -678,14 +756,14 @@ void tst_QQmlEngineDebugService::queryObject() recursiveObjectTest(m_rootItem, obj, recursive); if (recursive) { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QVERIFY(child.properties.count() > 0); } - QmlDebugObjectReference rect; - QmlDebugObjectReference text; - foreach (const QmlDebugObjectReference &child, obj.children) { + QQmlEngineDebugObjectReference rect; + QQmlEngineDebugObjectReference text; + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); if (child.className == "Rectangle") rect = child; @@ -700,7 +778,7 @@ void tst_QQmlEngineDebugService::queryObject() QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); } else { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QCOMPARE(child.properties.count(), 0); } @@ -721,14 +799,14 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() bool success; - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); int lineNumber = rootObject.source.lineNumber; int columnNumber = rootObject.source.columnNumber; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); recursive ? unconnected->queryObjectsForLocationRecursive(fileName, lineNumber, columnNumber, &success) : unconnected->queryObjectsForLocation(fileName, lineNumber, @@ -744,11 +822,11 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->objects().count(), 1); - QmlDebugObjectReference obj = m_dbg->objects().first(); + QQmlEngineDebugObjectReference obj = m_dbg->objects().first(); QVERIFY(!obj.className.isEmpty()); // check source as defined in main() - QmlDebugFileReference source = obj.source; + QQmlEngineDebugFileReference source = obj.source; QCOMPARE(source.url, QUrl(fileName)); QCOMPARE(source.lineNumber, lineNumber); QCOMPARE(source.columnNumber, columnNumber); @@ -757,14 +835,14 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() recursiveObjectTest(m_rootItem, obj, recursive); if (recursive) { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QVERIFY(child.properties.count() > 0); } - QmlDebugObjectReference rect; - QmlDebugObjectReference text; - foreach (const QmlDebugObjectReference &child, obj.children) { + QQmlEngineDebugObjectReference rect; + QQmlEngineDebugObjectReference text; + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); if (child.className == "Rectangle") rect = child; @@ -779,7 +857,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); } else { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QCOMPARE(child.properties.count(), 0); } @@ -796,7 +874,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation_data() void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() { - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); int contextId = rootObject.contextDebugId; QQmlContext *context = qobject_cast<QQmlContext *>(QQmlDebugService::objectForId(contextId)); @@ -823,7 +901,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - foreach (QmlDebugObjectReference child, rootObject.children) { + foreach (QQmlEngineDebugObjectReference child, rootObject.children) { QVERIFY(!child.className.isEmpty()); success = false; lineNumber = child.source.lineNumber; @@ -846,7 +924,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - foreach (QmlDebugObjectReference child, rootObject.children) { + foreach (QQmlEngineDebugObjectReference child, rootObject.children) { QVERIFY(!child.className.isEmpty()); success = false; lineNumber = child.source.lineNumber; @@ -862,10 +940,10 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() { bool success; - QmlDebugObjectReference rootObject = findRootObject(4, true); + QQmlEngineDebugObjectReference rootObject = findRootObject(4, true); QVERIFY(!rootObject.className.isEmpty()); - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryObject(rootObject, &success); QVERIFY(!success); delete unconnected; @@ -874,12 +952,31 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - QmlDebugObjectReference obj = m_dbg->object(); + QQmlEngineDebugObjectReference obj = m_dbg->object(); QVERIFY(!obj.className.isEmpty()); - QCOMPARE(findProperty(obj.properties, "modelIndex").value, QVariant()); + QCOMPARE(findProperty(obj.properties, "modelIndex").value, + QVariant(QLatin1String("QModelIndex()"))); } +void tst_QQmlEngineDebugService::jsonData() +{ + bool success; + + QQmlEngineDebugObjectReference rootObject = findRootObject(5, true); + QVERIFY(!rootObject.className.isEmpty()); + + m_dbg->queryObject(rootObject, &success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); + + QQmlEngineDebugObjectReference obj = m_dbg->object(); + QVERIFY(!obj.className.isEmpty()); + + QCOMPARE(findProperty(obj.properties, "data").value, + QJsonDocument::fromJson("{\"a\":null,\"c\":\"fffff\",\"foo\":12,\"ttt\":[4,5,4,3,2]}") + .toVariant()); +} void tst_QQmlEngineDebugService::queryExpressionResult() { @@ -890,7 +987,7 @@ void tst_QQmlEngineDebugService::queryExpressionResult() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryExpressionResult(objectId, expr, &success); QVERIFY(!success); delete unconnected; @@ -938,7 +1035,7 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryExpressionResultBC(objectId, expr, &success); QVERIFY(!success); delete unconnected; @@ -968,10 +1065,10 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC_data() void tst_QQmlEngineDebugService::setBindingForObject() { - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); QVERIFY(rootObject.debugId != -1); - QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); + QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); QCOMPARE(widthPropertyRef.value, QVariant(10)); QCOMPARE(widthPropertyRef.binding, QString()); @@ -1016,7 +1113,7 @@ void tst_QQmlEngineDebugService::setBindingForObject() rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); QCOMPARE(rootObject.children.size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement - QmlDebugObjectReference mouseAreaObject = rootObject.children.at(2); + QQmlEngineDebugObjectReference mouseAreaObject = rootObject.children.at(2); QVERIFY(!mouseAreaObject.className.isEmpty()); m_dbg->queryObjectRecursive(mouseAreaObject, &success); QVERIFY(success); @@ -1024,11 +1121,11 @@ void tst_QQmlEngineDebugService::setBindingForObject() mouseAreaObject = m_dbg->object(); QCOMPARE(mouseAreaObject.className, QString("MouseArea")); - QmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); + QQmlEngineDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); QCOMPARE(onEnteredRef.name, QString("onEntered")); // Sorry, can't do that anymore: QCOMPARE(onEnteredRef.value, QVariant("{ console.log('hello') }")); - QCOMPARE(onEnteredRef.value, QVariant("function() { [code] }")); + QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }")); m_dbg->setBindingForObject(mouseAreaObject.debugId, "onEntered", "{console.log('hello, world') }", false, @@ -1048,15 +1145,15 @@ void tst_QQmlEngineDebugService::setBindingForObject() QVERIFY(!mouseAreaObject.className.isEmpty()); onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); QCOMPARE(onEnteredRef.name, QString("onEntered")); - QCOMPARE(onEnteredRef.value, QVariant("function() { [code] }")); + QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }")); } void tst_QQmlEngineDebugService::resetBindingForObject() { - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); QVERIFY(rootObject.debugId != -1); - QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); + QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); bool success = false; @@ -1092,7 +1189,7 @@ void tst_QQmlEngineDebugService::resetBindingForObject() rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); - QmlDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold"); + QQmlEngineDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold"); QCOMPARE(boldPropertyRef.value.toBool(), false); QCOMPARE(boldPropertyRef.binding, QString()); @@ -1104,7 +1201,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() const int sourceIndex = 3; - QmlDebugObjectReference obj = findRootObject(sourceIndex); + QQmlEngineDebugObjectReference obj = findRootObject(sourceIndex); QVERIFY(!obj.className.isEmpty()); QVERIFY(obj.debugId != -1); QVERIFY(obj.children.count() >= 2); @@ -1137,11 +1234,11 @@ void tst_QQmlEngineDebugService::setBindingInStates() // change the binding - QmlDebugObjectReference state = obj.children[1]; + QQmlEngineDebugObjectReference state = obj.children[1]; QCOMPARE(state.className, QString("State")); QVERIFY(state.children.count() > 0); - QmlDebugObjectReference propertyChange = state.children[0]; + QQmlEngineDebugObjectReference propertyChange = state.children[0]; QVERIFY(!propertyChange.className.isEmpty()); QVERIFY(propertyChange.debugId != -1); @@ -1220,51 +1317,53 @@ void tst_QQmlEngineDebugService::queryObjectTree() { const int sourceIndex = 3; - QmlDebugObjectReference obj = findRootObject(sourceIndex, true); + QQmlEngineDebugObjectReference obj = findRootObject(sourceIndex, true); QVERIFY(!obj.className.isEmpty()); QVERIFY(obj.debugId != -1); QVERIFY(obj.children.count() >= 2); // check state - QmlDebugObjectReference state = obj.children[1]; + QQmlEngineDebugObjectReference state = obj.children[1]; QCOMPARE(state.className, QString("State")); QVERIFY(state.children.count() > 0); - QmlDebugObjectReference propertyChange = state.children[0]; + QQmlEngineDebugObjectReference propertyChange = state.children[0]; QVERIFY(!propertyChange.className.isEmpty()); QVERIFY(propertyChange.debugId != -1); - QmlDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target"); + QQmlEngineDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target"); QCOMPARE(propertyChangeTarget.objectDebugId, propertyChange.debugId); - QmlDebugObjectReference targetReference = qvariant_cast<QmlDebugObjectReference>(propertyChangeTarget.value); + QQmlEngineDebugObjectReference targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(propertyChangeTarget.value); QVERIFY(!targetReference.className.isEmpty()); - QVERIFY(targetReference.debugId != -1); + QCOMPARE(targetReference.debugId, -1); + QCOMPARE(targetReference.name, QString("<unnamed object>")); // check transition - QmlDebugObjectReference transition = obj.children[0]; + QQmlEngineDebugObjectReference transition = obj.children[0]; QCOMPARE(transition.className, QString("Transition")); QCOMPARE(findProperty(transition.properties,"from").value.toString(), QString("*")); QCOMPARE(findProperty(transition.properties,"to").value, findProperty(state.properties,"name").value); QVERIFY(transition.children.count() > 0); - QmlDebugObjectReference animation = transition.children[0]; + QQmlEngineDebugObjectReference animation = transition.children[0]; QVERIFY(!animation.className.isEmpty()); QVERIFY(animation.debugId != -1); - QmlDebugPropertyReference animationTarget = findProperty(animation.properties,"target"); + QQmlEngineDebugPropertyReference animationTarget = findProperty(animation.properties,"target"); QCOMPARE(animationTarget.objectDebugId, animation.debugId); - targetReference = qvariant_cast<QmlDebugObjectReference>(animationTarget.value); + targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(animationTarget.value); QVERIFY(!targetReference.className.isEmpty()); - QVERIFY(targetReference.debugId != -1); + QCOMPARE(targetReference.debugId, -1); + QCOMPARE(targetReference.name, QString("<unnamed object>")); QCOMPARE(findProperty(animation.properties,"property").value.toString(), QString("width")); QCOMPARE(findProperty(animation.properties,"duration").value.toInt(), 100); } void tst_QQmlEngineDebugService::asynchronousCreate() { - QmlDebugObjectReference object; + QQmlEngineDebugObjectReference object; auto connection = connect(m_dbg, &QQmlEngineDebugClient::newObject, this, [&](int objectId) { object.debugId = objectId; }); @@ -1289,6 +1388,49 @@ void tst_QQmlEngineDebugService::asynchronousCreate() { QTRY_COMPARE(m_dbg->object().idString, QLatin1String("asyncRect")); } +void tst_QQmlEngineDebugService::invalidContexts() +{ + getContexts(); + const int base = m_dbg->rootContext().contexts.count(); + QQmlContext context(m_engine); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), base + 1); + QQmlContextData *contextData = QQmlContextData::get(&context); + contextData->invalidate(); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), base); + QQmlContextData *rootData = QQmlContextData::get(m_engine->rootContext()); + rootData->invalidate(); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), 0); + contextData->setParent(rootData); // makes context valid again, but not root. + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), 0); +} + +void tst_QQmlEngineDebugService::createObjectOnDestruction() +{ + QSignalSpy spy(m_dbg, SIGNAL(newObject(int))); + { + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQml 2.0;" + "QtObject {" + "property Component x:" + "Qt.createQmlObject('import QtQml 2.0; Component { QtObject { } }'," + "this, 'x.qml');" + "Component.onDestruction: x.createObject(this, {});" + "}", QUrl::fromLocalFile("x.qml")); + QVERIFY(component.isReady()); + QVERIFY(component.create()); + QTRY_COMPARE(spy.count(), 2); + } + // Doesn't crash and doesn't give us another signal for the object created on destruction. + QTest::qWait(500); + QCOMPARE(spy.count(), 2); +} + int main(int argc, char *argv[]) { int _argc = argc + 1; diff --git a/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro b/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro index ee5f3c708a..0d42030b52 100644 --- a/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro +++ b/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro @@ -1,14 +1,11 @@ CONFIG += testcase TARGET = tst_qqmlinspector -QT += qml testlib gui-private core-private +QT += testlib gui-private core-private osx:CONFIG -= app_bundle SOURCES += tst_qqmlinspector.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) -include(../shared/qqmlinspectorclient.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp index 9461922eff..6685558bb5 100644 --- a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp +++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp @@ -26,11 +26,12 @@ ** ****************************************************************************/ -#include "qqmlinspectorclient.h" #include "../shared/debugutil_p.h" +#include "../shared/qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmldebugconnection_p.h> +#include <private/qqmlinspectorclient_p.h> #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> @@ -40,64 +41,32 @@ #include <QtCore/qlibraryinfo.h> #include <QtNetwork/qhostaddress.h> -#define STR_PORT_FROM "3772" -#define STR_PORT_TO "3782" - - -class tst_QQmlInspector : public QQmlDataTest +class tst_QQmlInspector : public QQmlDebugTest { Q_OBJECT private: - void startQmlProcess(const QString &qmlFile, bool restrictMode = true); + ConnectResult startQmlProcess(const QString &qmlFile, bool restrictMode = true); void checkAnimationSpeed(int targetMillisPerDegree); + QList<QQmlDebugClient *> createClients() override; + QQmlDebugProcess *createProcess(const QString &executable) override; -private: - QScopedPointer<QQmlDebugProcess> m_process; - QScopedPointer<QQmlDebugConnection> m_connection; - QScopedPointer<QQmlInspectorClient> m_client; - QScopedPointer<QQmlInspectorResultRecipient> m_recipient; + QPointer<QQmlInspectorClient> m_client; + QPointer<QQmlInspectorResultRecipient> m_recipient; private slots: - void cleanup(); - void connect_data(); void connect(); void setAnimationSpeed(); void showAppOnTop(); }; -void tst_QQmlInspector::startQmlProcess(const QString &qmlFile, bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlInspector::startQmlProcess(const QString &qmlFile, + bool restrictServices) { - const QString argument = QString::fromLatin1("-qmljsdebugger=port:%1,%2,block%3") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(restrictServices ? QStringLiteral(",services:QmlInspector") : QString()); - - m_process.reset(new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + - "/qml")); - // Make sure the animation timing is exact - m_process->addEnvironment(QLatin1String("QSG_RENDER_LOOP=basic")); - m_process->start(QStringList() << argument << testFile(qmlFile)); - QVERIFY2(m_process->waitForSessionStart(), - "Could not launch application, or did not get 'Waiting for connection'."); - - m_client.reset(); - m_connection.reset(new QQmlDebugConnection); - m_client.reset(new QQmlInspectorClient(m_connection.data())); - - m_recipient.reset(new QQmlInspectorResultRecipient); - QObject::connect(m_client.data(), &QQmlInspectorClient::responseReceived, - m_recipient.data(), &QQmlInspectorResultRecipient::recordResponse); - - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection.data()); - - m_connection->connectToHost(QLatin1String("127.0.0.1"), m_process->debugPort()); - QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); - - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); + return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + restrictServices ? QStringLiteral("QmlInspector") : QString(), + testFile(qmlFile), true); } void tst_QQmlInspector::checkAnimationSpeed(int targetMillisPerDegree) @@ -114,8 +83,7 @@ void tst_QQmlInspector::checkAnimationSpeed(int targetMillisPerDegree) QString output = m_process->output(); int position = output.length(); do { - QVERIFY(QQmlDebugTest::waitForSignal(m_process.data(), - SIGNAL(readyReadStandardOutput()))); + QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); output = m_process->output(); } while (!output.mid(position).contains(markerString)); @@ -144,12 +112,21 @@ void tst_QQmlInspector::checkAnimationSpeed(int targetMillisPerDegree) .arg(targetMillisPerDegree).toLocal8Bit().constData()); } -void tst_QQmlInspector::cleanup() +QList<QQmlDebugClient *> tst_QQmlInspector::createClients() { - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << m_process->state(); - qDebug() << "Application Output:" << m_process->output(); - } + m_client = new QQmlInspectorClient(m_connection); + m_recipient = new QQmlInspectorResultRecipient(m_client); + QObject::connect(m_client.data(), &QQmlInspectorClient::responseReceived, + m_recipient.data(), &QQmlInspectorResultRecipient::recordResponse); + return QList<QQmlDebugClient *>({m_client}); +} + +QQmlDebugProcess *tst_QQmlInspector::createProcess(const QString &executable) +{ + QQmlDebugProcess *process = QQmlDebugTest::createProcess(executable); + // Make sure the animation timing is exact + process->addEnvironment(QLatin1String("QSG_RENDER_LOOP=basic")); + return process; } void tst_QQmlInspector::connect_data() @@ -166,7 +143,7 @@ void tst_QQmlInspector::connect() { QFETCH(QString, file); QFETCH(bool, restrictMode); - startQmlProcess(file, restrictMode); + QCOMPARE(startQmlProcess(file, restrictMode), ConnectSuccess); QVERIFY(m_client); QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); @@ -181,7 +158,7 @@ void tst_QQmlInspector::connect() void tst_QQmlInspector::showAppOnTop() { - startQmlProcess("qtquick2.qml"); + QCOMPARE(startQmlProcess("qtquick2.qml"), ConnectSuccess); QVERIFY(m_client); QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); @@ -196,7 +173,7 @@ void tst_QQmlInspector::showAppOnTop() void tst_QQmlInspector::setAnimationSpeed() { - startQmlProcess("qtquick2.qml"); + QCOMPARE(startQmlProcess("qtquick2.qml"), ConnectSuccess); QVERIFY(m_client); QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); checkAnimationSpeed(10); diff --git a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp index 33d3c6369f..6283a93881 100644 --- a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp +++ b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp @@ -113,7 +113,7 @@ int main(int argc, char *argv[]) argv2[i] = argv[i]; argv2[argc] = strdup("-qmljsdebugger=native,services:NativeQmlDebugger"); ++argc; - argv2[argc] = 0; + argv2[argc] = nullptr; Application app(argc, argv2); return QTest::qExec(&app, argc, argv); } diff --git a/tests/auto/qml/debugger/qqmlpreview/data/broken.qml b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml new file mode 100644 index 0000000000..4ebbf7576a --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 + +Item { diff --git a/tests/auto/qml/debugger/qqmlpreview/data/i18n/qml_fr_FR.qm b/tests/auto/qml/debugger/qqmlpreview/data/i18n/qml_fr_FR.qm new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/i18n/qml_fr_FR.qm diff --git a/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml new file mode 100644 index 0000000000..b525ab4fa0 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 + +Rectangle { + width: 100 + height: 100 + color: "blue" + + RotationAnimation on rotation { + duration: 3600 + loops: Animation.Infinite + from: 0 + to: 360 + } + + Timer { + interval: 300 + repeat: true + running: true + property int prevHit: -1 + property int prevRotation: -1 + onTriggered: { + var date = new Date; + var millis = date.getMilliseconds() + + if (prevHit < 0) { + prevHit = millis; + prevRotation = parent.rotation + return; + } + + var milliDelta = millis - prevHit; + if (milliDelta <= 0) + milliDelta += 1000; + prevHit = millis; + + var delta = parent.rotation - prevRotation; + if (delta < 0) + delta += 360 + prevRotation = parent.rotation + console.log(milliDelta, delta, "ms/degrees "); + } + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window.qml b/tests/auto/qml/debugger/qqmlpreview/data/window.qml new file mode 100644 index 0000000000..f9f8d5aeb1 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/window.qml @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.Window 2.0 + +Window { + visible: true + + height: 100 + width: 100 + + Timer { + repeat: false + interval: 1000 + running: true + onTriggered: console.log("window.qml"); + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window1.qml b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml new file mode 100644 index 0000000000..e8e9ad706d --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.Window 2.3 + +Window { + visible: true + + height: 200 + width: 100 + + Timer { + interval: 1000 + running: true + onTriggered: console.log("window1.qml"); + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window2.qml b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml new file mode 100644 index 0000000000..9ad42d2ee2 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.Window 2.3 + +Window { + visible: true + + height: 100 + width: 200 + + Timer { + interval: 1000 + running: true + onTriggered: console.log("window2.qml"); + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml new file mode 100644 index 0000000000..0aca235de1 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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.Window 2.2 + +Window { + id: w + height: 100 + width: 100 + visible: true + + Rectangle { + width: 50 + height: 50 + color: "blue" + anchors.centerIn: parent + } + + Timer { + interval: 100 + running: true + repeat: true + onTriggered: console.log("zoom", w.screen.devicePixelRatio) + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/qqmlpreview.pro b/tests/auto/qml/debugger/qqmlpreview/qqmlpreview.pro new file mode 100644 index 0000000000..2c27306517 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/qqmlpreview.pro @@ -0,0 +1,25 @@ +CONFIG += testcase +TARGET = tst_qqmlpreview + +QT += qml testlib core qmldebug-private +macos:CONFIG -= app_bundle + +INCLUDEPATH += ../../../../../src/plugins/qmltooling/qmldbg_preview/ + +SOURCES += \ + tst_qqmlpreview.cpp \ + ../../../../../src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp + +HEADERS += \ + ../../../../../src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h + +include(../shared/debugutil.pri) + +TESTDATA = \ + data/window.qml \ + data/qtquick2.qml \ + data/window2.qml \ + data/window1.qml \ + data/broken.qml \ + data/zoom.qml \ + data/i18n/qml_fr_FR.qm diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp new file mode 100644 index 0000000000..4c4c514832 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "qqmldebugprocess_p.h" +#include "debugutil_p.h" +#include "qqmlpreviewblacklist.h" + +#include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> +#include <QtCore/qthread.h> +#include <QtCore/qlibraryinfo.h> +#include <QtNetwork/qhostaddress.h> + +#include <private/qqmldebugconnection_p.h> +#include <private/qqmlpreviewclient_p.h> + +class tst_QQmlPreview : public QQmlDebugTest +{ + Q_OBJECT + +private: + ConnectResult startQmlProcess(const QString &qmlFile); + void serveRequest(const QString &path); + QList<QQmlDebugClient *> createClients() override; + void verifyProcessOutputContains(const QString &string) const; + + QPointer<QQmlPreviewClient> m_client; + + QStringList m_files; + QStringList m_filesNotFound; + QStringList m_directories; + QStringList m_serviceErrors; + QQmlPreviewClient::FpsInfo m_frameStats; + +private slots: + void cleanup() final; + + void connect(); + void load(); + void rerun(); + void blacklist(); + void error(); + void zoom(); + void fps(); + void language(); +}; + +QQmlDebugTest::ConnectResult tst_QQmlPreview::startQmlProcess(const QString &qmlFile) +{ + return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + QStringLiteral("QmlPreview"), testFile(qmlFile), true); +} + +void tst_QQmlPreview::serveRequest(const QString &path) +{ + QFileInfo info(path); + + if (info.isDir()) { + m_directories.append(path); + m_client->sendDirectory(path, QDir(path).entryList()); + } else { + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + m_files.append(path); + m_client->sendFile(path, file.readAll()); + } else { + m_filesNotFound.append(path); + m_client->sendError(path); + } + } +} + +QList<QQmlDebugClient *> tst_QQmlPreview::createClients() +{ + m_client = new QQmlPreviewClient(m_connection); + + QObject::connect(m_client.data(), &QQmlPreviewClient::request, this, &tst_QQmlPreview::serveRequest); + QObject::connect(m_client.data(), &QQmlPreviewClient::error, this, [this](const QString &error) { + m_serviceErrors.append(error); + }); + QObject::connect(m_client.data(), &QQmlPreviewClient::fps, + this, [this](const QQmlPreviewClient::FpsInfo &info) { + m_frameStats = info; + }); + + return QList<QQmlDebugClient *>({m_client}); +} + +void tst_QQmlPreview::verifyProcessOutputContains(const QString &string) const +{ + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains(string), 30000); +} + +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() +{ + // Use a separate function so that we don't return early from cleanup() on failure. + checkFiles(m_files); + + QQmlDebugTest::cleanup(); + if (QTest::currentTestFailed()) { + qDebug() << "Files loaded:" << m_files; + qDebug() << "Files not loaded:" << m_filesNotFound; + qDebug() << "Directories loaded:" << m_directories; + qDebug() << "Errors reported:" << m_serviceErrors; + } + + m_directories.clear(); + m_files.clear(); + m_filesNotFound.clear(); + m_serviceErrors.clear(); + m_frameStats = QQmlPreviewClient::FpsInfo(); +} + +void tst_QQmlPreview::connect() +{ + const QString file("window.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + m_client->triggerLoad(testFileUrl(file)); + QTRY_VERIFY(m_files.contains(testFile(file))); + verifyProcessOutputContains(file); + m_process->stop(); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::NotConnected); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::load() +{ + const QString file("qtquick2.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + m_client->triggerLoad(testFileUrl(file)); + QTRY_VERIFY(m_files.contains(testFile(file))); + verifyProcessOutputContains("ms/degrees"); + + const QStringList files({"window2.qml", "window1.qml", "window.qml"}); + for (const QString &newFile : files) { + m_client->triggerLoad(testFileUrl(newFile)); + QTRY_VERIFY(m_files.contains(testFile(newFile))); + verifyProcessOutputContains(newFile); + } + + m_process->stop(); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::NotConnected); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::rerun() +{ + const QString file("window.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl(file)); + const QLatin1String message("window.qml"); + verifyProcessOutputContains(message); + const int pos = m_process->output().lastIndexOf(message) + message.size(); + QVERIFY(pos >= 0); + + m_client->triggerRerun(); + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().indexOf(message, pos) >= pos, 30000); + + m_process->stop(); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::blacklist() +{ + QQmlPreviewBlacklist blacklist; + + QStringList strings({ + "lalala", "lulul", "trakdkd", "suppe", "zack" + }); + + for (const QString &string : strings) + QVERIFY(!blacklist.isBlacklisted(string)); + + for (const QString &string : strings) + blacklist.blacklist(string); + + for (const QString &string : strings) { + QVERIFY(blacklist.isBlacklisted(string)); + QVERIFY(!blacklist.isBlacklisted(string.left(string.size() / 2))); + QVERIFY(!blacklist.isBlacklisted(string + "45")); + QVERIFY(!blacklist.isBlacklisted(" " + string)); + QVERIFY(blacklist.isBlacklisted(string + "/45")); + } + + for (auto begin = strings.begin(), it = begin, end = strings.end(); it != end; ++it) { + std::rotate(begin, it, end); + QString path = "/" + strings.join('/'); + blacklist.blacklist(path); + QVERIFY(blacklist.isBlacklisted(path)); + QVERIFY(blacklist.isBlacklisted(path + "/file")); + QVERIFY(!blacklist.isBlacklisted(path + "more")); + path.chop(1); + QVERIFY(!blacklist.isBlacklisted(path)); + std::reverse(begin, end); + } + + blacklist.clear(); + for (const QString &string : strings) + QVERIFY(!blacklist.isBlacklisted(string)); + + blacklist.blacklist(":/qt-project.org"); + QVERIFY(blacklist.isBlacklisted(":/qt-project.org/QmlRuntime/conf/configuration.qml")); + QVERIFY(!blacklist.isBlacklisted(":/qt-project.orgQmlRuntime/conf/configuration.qml")); + + QQmlPreviewBlacklist blacklist2; + + blacklist2.blacklist(":/qt-project.org"); + blacklist2.blacklist(":/QtQuick/Controls/Styles"); + blacklist2.blacklist(":/ExtrasImports/QtQuick/Controls/Styles"); + blacklist2.blacklist(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + blacklist2.blacklist("/home/ulf/.local/share/QtProject/Qml Runtime/configuration.qml"); + blacklist2.blacklist("/usr/share"); + blacklist2.blacklist("/usr/share/QtProject/Qml Runtime/configuration.qml"); + QVERIFY(blacklist2.isBlacklisted(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath))); + blacklist2.blacklist("/usr/local/share/QtProject/Qml Runtime/configuration.qml"); + blacklist2.blacklist("qml"); + blacklist2.blacklist(""); // This should not remove all other paths. + + QVERIFY(blacklist2.isBlacklisted(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) + + "/QtQuick/Window.2.0")); + QVERIFY(blacklist2.isBlacklisted(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath))); + QVERIFY(blacklist2.isBlacklisted("/usr/share/QtProject/Qml Runtime/configuration.qml")); + QVERIFY(blacklist2.isBlacklisted("/usr/share/stuff")); + QVERIFY(blacklist2.isBlacklisted("")); + + QQmlPreviewBlacklist blacklist3; + blacklist3.blacklist("/usr/share"); + blacklist3.blacklist("/usr"); + blacklist3.blacklist("/usrdings"); + QVERIFY(blacklist3.isBlacklisted("/usrdings")); + QVERIFY(blacklist3.isBlacklisted("/usr/src")); + QVERIFY(!blacklist3.isBlacklisted("/opt/share")); + QVERIFY(!blacklist3.isBlacklisted("/opt")); + + blacklist3.whitelist("/usr/share"); + QVERIFY(blacklist3.isBlacklisted("/usrdings")); + QVERIFY(!blacklist3.isBlacklisted("/usr")); + QVERIFY(!blacklist3.isBlacklisted("/usr/share")); + QVERIFY(!blacklist3.isBlacklisted("/usr/src")); + QVERIFY(!blacklist3.isBlacklisted("/opt/share")); + QVERIFY(!blacklist3.isBlacklisted("/opt")); +} + +void tst_QQmlPreview::error() +{ + QCOMPARE(startQmlProcess("window.qml"), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl("broken.qml")); + QTRY_COMPARE_WITH_TIMEOUT(m_serviceErrors.count(), 1, 10000); + QVERIFY(m_serviceErrors.first().contains("broken.qml:32 Expected token `}'")); +} + +static float parseZoomFactor(const QString &output) +{ + const QString prefix("zoom "); + const int start = output.lastIndexOf(prefix) + prefix.length(); + if (start < 0) + return -1; + const int end = output.indexOf('\n', start); + if (end < 0) + return -1; + bool ok = false; + const float zoomFactor = output.mid(start, end - start).toFloat(&ok); + if (!ok) + return -1; + return zoomFactor; +} + +static void verifyZoomFactor(const QQmlDebugProcess *process, float factor) +{ + QTRY_VERIFY_WITH_TIMEOUT(qFuzzyCompare(parseZoomFactor(process->output()), factor), 30000); +} + +void tst_QQmlPreview::zoom() +{ + const QString file("zoom.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl(file)); + QTRY_VERIFY(m_files.contains(testFile(file))); + float baseZoomFactor = -1; + QTRY_VERIFY_WITH_TIMEOUT((baseZoomFactor = parseZoomFactor(m_process->output())) > 0, 30000); + m_client->triggerZoom(2.0f); + verifyZoomFactor(m_process, baseZoomFactor * 2.0f); + m_client->triggerZoom(1.5f); + verifyZoomFactor(m_process, baseZoomFactor * 1.5f); + m_client->triggerZoom(0.5f); + verifyZoomFactor(m_process, baseZoomFactor * 0.5f); + m_client->triggerZoom(-1.0f); + verifyZoomFactor(m_process, baseZoomFactor); + m_process->stop(); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::fps() +{ + const QString file("qtquick2.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl(file)); + if (QGuiApplication::platformName() != "offscreen") { + QTRY_VERIFY(m_frameStats.numSyncs > 10); + QVERIFY(m_frameStats.minSync <= m_frameStats.maxSync); + QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs >= m_frameStats.minSync - 1); + QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs <= m_frameStats.maxSync); + + QVERIFY(m_frameStats.numRenders > 0); + QVERIFY(m_frameStats.minRender <= m_frameStats.maxRender); + QVERIFY(m_frameStats.totalRender / m_frameStats.numRenders >= m_frameStats.minRender - 1); + QVERIFY(m_frameStats.totalRender / m_frameStats.numRenders <= m_frameStats.maxRender); + } else { + QSKIP("offscreen rendering doesn't produce any frames"); + } +} + +void tst_QQmlPreview::language() +{ + QCOMPARE(startQmlProcess("window.qml"), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLanguage(dataDirectoryUrl(), "fr_FR"); + QTRY_VERIFY_WITH_TIMEOUT(m_files.contains(testFile("i18n/qml_fr_FR.qm")), 30000); +} + +QTEST_MAIN(tst_QQmlPreview) + +#include "tst_qqmlpreview.moc" diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/batchOverflow.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/batchOverflow.qml new file mode 100644 index 0000000000..dde1def947 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/batchOverflow.qml @@ -0,0 +1,20 @@ +import QtQml 2.2 + +QtObject { + function iterate(dictionaryTable, j) { + var word = "a" + j.toString() + dictionaryTable[word] = null; + } + + Component.onCompleted: { + var dictionaryTable = {}; + for (var j = 0; j < 256; ++j) + iterate(dictionaryTable, j); + } + + property Timer timer: Timer { + interval: 1 + running: true; + onTriggered: Qt.quit(); + } +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml index dd7cb2055d..4235a2d55f 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml @@ -43,13 +43,6 @@ QtObject { interval: 1000 onTriggered: { console.profileEnd(); - endTimer.start(); } } - - property var endTimer: Timer { - id: endTimer - interval: 1000 - onTriggered: Qt.quit(); - } } diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml index 4236d70ea3..3b28e65174 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml @@ -1,7 +1,7 @@ import QtQml 2.0 QtObject { - Timer { + property Timer timer: Timer { running: true interval: 1 onTriggered: Qt.quit(); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml new file mode 100644 index 0000000000..f39dcdf16a --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml @@ -0,0 +1,17 @@ +import QtQml 2.0 + +Timer { + interval: 1 + running: true + + function recurse(i) { + var x = { t: [1, 2, 3, 4] } + console.log(x.t[i]); + if (i < 3) + recurse(i + 1); + else + Qt.quit(); + } + + onTriggered: recurse(0) +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/qstr.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/qstr.qml new file mode 100644 index 0000000000..09dcd34b5c --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/qstr.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +Timer { + property string stuff: qsTr("foo") + + running: true + interval: 1 + onTriggered: Qt.quit(); +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml new file mode 100644 index 0000000000..bc8c2b90ae --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + Timer { + running: true + triggeredOnStart: true + onTriggered: Qt.quit(); + } +} + diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro index 71a58d6f34..0cd4b331f2 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro +++ b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro @@ -4,13 +4,11 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlprofilerservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* -QT += core qml testlib gui-private core-private +QT += testlib gui-private OTHER_FILES += \ data/pixmapCacheTest.qml \ @@ -21,4 +19,7 @@ OTHER_FILES += \ data/TestImage_2x2.png \ data/signalSourceLocation.qml \ data/javascript.qml \ - data/timer.qml + data/timer.qml \ + data/qstr.qml \ + data/memory.qml \ + data/batchOverflow.qml diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 692e70d7da..085eb7b87a 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -27,254 +27,160 @@ ****************************************************************************/ #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmlprofilerclient_p.h> #include <private/qqmldebugconnection_p.h> #include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> #include <QtCore/qlibraryinfo.h> -#define STR_PORT_FROM "13773" -#define STR_PORT_TO "13783" +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> -struct QQmlProfilerData -{ - QQmlProfilerData(qint64 time = -2, int messageType = -1, int detailType = -1, - const QString &detailData = QString()) : - time(time), messageType(messageType), detailType(detailType), detailData(detailData), - line(-1), column(-1), framerate(-1), animationcount(-1), amount(-1) - {} - - qint64 time; - int messageType; - int detailType; - - //### - QString detailData; //used by RangeData and RangeLocation - int line; //used by RangeLocation - int column; //used by RangeLocation - int framerate; //used by animation events - int animationcount; //used by animation events - qint64 amount; //used by heap events -}; - -class QQmlProfilerTestClient : public QQmlProfilerClient +class QQmlProfilerTestClient : public QQmlProfilerEventReceiver { Q_OBJECT public: - QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection), - lastTimestamp(-1) {} + QQmlProfilerTestClient(QQmlDebugConnection *connection) : + client(new QQmlProfilerClient(connection, this)) + { + connect(client.data(), &QQmlProfilerClient::traceStarted, + this, &QQmlProfilerTestClient::startTrace); + connect(client.data(), &QQmlProfilerClient::traceFinished, + this, &QQmlProfilerTestClient::endTrace); + } + + void startTrace(qint64 timestamp, const QList<int> &engineIds); + void endTrace(qint64 timestamp, const QList<int> &engineIds); - QVector<QQmlProfilerData> qmlMessages; - QVector<QQmlProfilerData> javascriptMessages; - QVector<QQmlProfilerData> jsHeapMessages; - QVector<QQmlProfilerData> asynchronousMessages; - QVector<QQmlProfilerData> pixmapMessages; + QPointer<QQmlProfilerClient> client; // Owned by QQmlDebugTest + QVector<QQmlProfilerEventType> types; - qint64 lastTimestamp; + QVector<QQmlProfilerEvent> qmlMessages; + QVector<QQmlProfilerEvent> javascriptMessages; + QVector<QQmlProfilerEvent> jsHeapMessages; + QVector<QQmlProfilerEvent> asynchronousMessages; + QVector<QQmlProfilerEvent> pixmapMessages; -signals: - void recordingFinished(); + int numLoadedEventTypes() const override; + void addEventType(const QQmlProfilerEventType &type) override; + void addEvent(const QQmlProfilerEvent &event) override; private: - void traceStarted(qint64 time, int engineId); - void traceFinished(qint64 time, int engineId); - void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime); - void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, const QString &data); - void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location); - void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime); - void animationFrame(qint64 time, int frameRate, int animationCount, int threadId); - void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5); - void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2); - void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount); - void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b); - void complete(); - - void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, int detailType); - void unknownData(QPacket &stream); + qint64 lastTimestamp = -1; }; -void QQmlProfilerTestClient::traceStarted(qint64 time, int engineId) +void QQmlProfilerTestClient::startTrace(qint64 timestamp, const QList<int> &engineIds) { - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::StartTrace, - QString::number(engineId))); + types.append(QQmlProfilerEventType(Event, MaximumRangeType, StartTrace)); + asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1, + engineIds.toVector())); } -void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId) +void QQmlProfilerTestClient::endTrace(qint64 timestamp, const QList<int> &engineIds) { - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::EndTrace, - QString::number(engineId))); + types.append(QQmlProfilerEventType(Event, MaximumRangeType, EndTrace)); + asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1, + engineIds.toVector())); } -void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) +int QQmlProfilerTestClient::numLoadedEventTypes() const { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= startTime); - lastTimestamp = startTime; - QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + return types.length(); } -void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &string) +void QQmlProfilerTestClient::addEventType(const QQmlProfilerEventType &type) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + types.append(type); } -void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) +void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(location.line >= -2); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename); - data.line = location.line; - data.column = location.column; - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); -} + const int typeIndex = event.typeIndex(); + QVERIFY(typeIndex < types.length()); -void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) -{ - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= endTime); - lastTimestamp = endTime; - QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); -} + const QQmlProfilerEventType &type = types[typeIndex]; -void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int animationCount, int threadId) -{ - QVERIFY(threadId >= 0); - QVERIFY(frameRate != -1); - QVERIFY(animationCount != -1); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::AnimationFrame); - data.framerate = frameRate; - data.animationcount = animationCount; - asynchronousMessages.append(data); -} + QVERIFY(event.timestamp() >= lastTimestamp); + lastTimestamp = event.timestamp(); -void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) -{ - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); - Q_UNUSED(numericData3); - Q_UNUSED(numericData4); - Q_UNUSED(numericData5); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame, - type)); -} - -void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &url, int numericData1, - int numericData2) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url); - switch (type) { - case QQmlProfilerDefinitions::PixmapSizeKnown: - data.line = numericData1; - data.column = numericData2; + switch (type.message()) { + case Event: { + switch (type.detailType()) { + case StartTrace: + QFAIL("StartTrace should not be passed on as event"); + break; + case EndTrace: + QFAIL("EndTrace should not be passed on as event"); + break; + case AnimationFrame: + asynchronousMessages.append(event); + break; + case Mouse: + case Key: + qmlMessages.append(event); + break; + default: + QFAIL(qPrintable(QString::fromLatin1("Event with unknown detailType %1 received at %2.") + .arg(type.detailType()).arg(event.timestamp()))); + break; + } + break; + } + case RangeStart: + case RangeData: + case RangeLocation: + case RangeEnd: + QFAIL("Range stages are transmitted as part of events"); + break; + case Complete: + QFAIL("Complete should not be passed on as event"); + break; + case PixmapCacheEvent: + pixmapMessages.append(event); break; - case QQmlProfilerDefinitions::PixmapReferenceCountChanged: - case QQmlProfilerDefinitions::PixmapCacheCountChanged: - data.animationcount = numericData1; + case SceneGraphFrame: + asynchronousMessages.append(event); break; - default: + case MemoryAllocation: + jsHeapMessages.append(event); + break; + case DebugMessage: + // Unhandled + break; + case MaximumMessage: + switch (type.rangeType()) { + case Painting: + QFAIL("QtQuick1 paint message received."); + break; + case Compiling: + case Creating: + case Binding: + case HandlingSignal: + qmlMessages.append(event); + break; + case Javascript: + javascriptMessages.append(event); + break; + default: + QFAIL(qPrintable( + QString::fromLatin1("Unknown range event %1 received at %2.") + .arg(type.rangeType()).arg(event.timestamp()))); + break; + } break; } - pixmapMessages.append(data); -} - -void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type); - data.amount = amount; - jsHeapMessages.append(data); } -void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type, - QString::number(a) + QLatin1Char('x') + - QString::number(b))); -} - -void QQmlProfilerTestClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType) -{ - QFAIL(qPrintable(QString::fromLatin1("Unknown event %1 with detail type %2 received at %3.") - .arg(messageType).arg(detailType).arg(time))); -} - -void QQmlProfilerTestClient::unknownData(QPacket &stream) -{ - QFAIL(qPrintable(QString::fromLatin1("%1 bytes of extra data after receiving message.") - .arg(stream.device()->bytesAvailable()))); -} - -void QQmlProfilerTestClient::complete() -{ - emit recordingFinished(); -} - -class tst_QQmlProfilerService : public QQmlDataTest +class tst_QQmlProfilerService : public QQmlDebugTest { Q_OBJECT -public: - tst_QQmlProfilerService() - : m_process(0) - , m_connection(0) - , m_client(0) - { - } - - private: - QQmlDebugProcess *m_process; - QQmlDebugConnection *m_connection; - QQmlProfilerTestClient *m_client; - enum MessageListType { MessageListQML, MessageListJavaScript, @@ -289,18 +195,28 @@ private: CheckLine = 1 << 2, CheckColumn = 1 << 3, CheckDataEndsWith = 1 << 4, + CheckFileEndsWith = 1 << 5, + CheckNumbers = 1 << 6, - CheckAll = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckDataEndsWith + CheckType = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckFileEndsWith }; - void connect(bool block, const QString &testFile, bool restrictServices = true); + ConnectResult connect(bool block, const QString &file, bool recordFromStart = true, + uint flushInterval = 0, bool restrictServices = true, + const QString &executable + = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"); + void checkProcessTerminated(); void checkTraceReceived(); void checkJsHeap(); - bool verify(MessageListType type, int expectedPosition, const QQmlProfilerData &expected, - quint32 checks); + bool verify(MessageListType type, int expectedPosition, + const QQmlProfilerEventType &expected, quint32 checks, + const QVector<qint64> &expectedNumbers); + + QList<QQmlDebugClient *> createClients() override; + QScopedPointer<QQmlProfilerTestClient> m_client; private slots: - void cleanup(); + void cleanup() override; void connect_data(); void connect(); @@ -311,58 +227,75 @@ private slots: void signalSourceLocation(); void javascript(); void flushInterval(); + void translationBinding(); + void memory(); + void compile(); + void multiEngine(); + void batchOverflow(); + +private: + bool m_recordFromStart = true; + bool m_flushInterval = false; + bool m_isComplete = false; + + // Don't use ({...}) here as MSVC will interpret that as the "QVector(int size)" ctor. + const QVector<qint64> m_rangeStart = (QVector<qint64>() << RangeStart); + const QVector<qint64> m_rangeEnd = (QVector<qint64>() << RangeEnd); }; -#define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks)) +#define VERIFY(type, position, expected, checks, numbers) \ + QVERIFY(verify(type, position, expected, checks, numbers)) -void tst_QQmlProfilerService::connect(bool block, const QString &testFile, bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect( + bool block, const QString &file, bool recordFromStart, uint flushInterval, + bool restrictServices, const QString &executable) { + m_recordFromStart = recordFromStart; + m_flushInterval = flushInterval; + m_isComplete = false; + // ### Still using qmlscene due to QTBUG-33377 - const QString executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"; - QStringList arguments; - arguments << QString::fromLatin1("-qmljsdebugger=port:%1,%2%3%4") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(block ? QStringLiteral(",block") : QString()) - .arg(restrictServices ? QStringLiteral(",services:CanvasFrameRate") : QString()) - << QQmlDataTest::instance()->testFile(testFile); - - m_process = new QQmlDebugProcess(executable, this); - m_process->start(QStringList() << arguments); - QVERIFY2(m_process->waitForSessionStart(), "Could not launch application, or did not get 'Waiting for connection'."); - - m_connection = new QQmlDebugConnection(); - m_client = new QQmlProfilerTestClient(m_connection); - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection); - - const int port = m_process->debugPort(); - m_connection->connectToHost(QLatin1String("127.0.0.1"), port); - QVERIFY(m_client); - QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + return QQmlDebugTest::connect( + executable, + restrictServices ? "CanvasFrameRate,EngineControl,DebugMessages" : QString(), + testFile(file), block); +} - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); +void tst_QQmlProfilerService::checkProcessTerminated() +{ + // If the process ends before connect(), we get a non-success value from connect() + // That's not a problem as we will still receive the trace. We check that process has terminated + // cleanly here. + + // Wait for the process to finish by itself, if that hasn't happened already + QVERIFY(m_client); + QVERIFY(m_client->client); + QTRY_COMPARE(m_client->client->state(), QQmlDebugClient::NotConnected); + QVERIFY(m_process); + QVERIFY(m_process->exitStatus() != QProcess::CrashExit); + QTRY_COMPARE(m_process->exitStatus(), QProcess::NormalExit); } void tst_QQmlProfilerService::checkTraceReceived() { - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(recordingFinished())), - "No trace received in time."); + QVERIFY(m_process->exitStatus() != QProcess::CrashExit); + QTRY_VERIFY2(m_isComplete, "No trace received in time."); + + QVector<qint64> numbers; // must start with "StartTrace" - QQmlProfilerData expected(0, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::StartTrace); - VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType); + QQmlProfilerEventType expected(Event, MaximumRangeType, StartTrace); + VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType, numbers); // must end with "EndTrace" - expected.detailType = QQmlProfilerDefinitions::EndTrace; + expected = QQmlProfilerEventType(Event, MaximumRangeType, EndTrace); VERIFY(MessageListAsynchronous, m_client->asynchronousMessages.length() - 1, expected, - CheckMessageType | CheckDetailType); + CheckMessageType | CheckDetailType, numbers); } void tst_QQmlProfilerService::checkJsHeap() { + QVERIFY(m_client); QVERIFY2(m_client->jsHeapMessages.count() > 0, "no JavaScript heap messages received"); bool seen_alloc = false; @@ -371,33 +304,36 @@ void tst_QQmlProfilerService::checkJsHeap() qint64 allocated = 0; qint64 used = 0; qint64 lastTimestamp = -1; - foreach (const QQmlProfilerData &message, m_client->jsHeapMessages) { - switch (message.detailType) { - case QV4::Profiling::HeapPage: - allocated += message.amount; + foreach (const QQmlProfilerEvent &message, m_client->jsHeapMessages) { + const auto amount = message.number<qint64>(0); + const QQmlProfilerEventType &type = m_client->types.at(message.typeIndex()); + switch (type.detailType()) { + case HeapPage: + allocated += amount; seen_alloc = true; break; - case QV4::Profiling::SmallItem: - used += message.amount; + case SmallItem: + used += amount; seen_small = true; break; - case QV4::Profiling::LargeItem: - allocated += message.amount; - used += message.amount; + case LargeItem: + allocated += amount; + used += amount; seen_large = true; break; } - QVERIFY(message.time >= lastTimestamp); + QVERIFY(message.timestamp() >= lastTimestamp); // The heap will only be consistent after all events of the same timestamp are processed. if (lastTimestamp == -1) { - lastTimestamp = message.time; - continue; - } else if (message.time == lastTimestamp) { + lastTimestamp = message.timestamp(); continue; } - lastTimestamp = message.time; + if (message.timestamp() == lastTimestamp) + continue; + + lastTimestamp = message.timestamp(); QVERIFY2(used >= 0, QString::fromLatin1("Negative memory usage seen: %1") .arg(used).toUtf8().constData()); @@ -416,10 +352,15 @@ void tst_QQmlProfilerService::checkJsHeap() } bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType type, - int expectedPosition, const QQmlProfilerData &expected, - quint32 checks) + int expectedPosition, const QQmlProfilerEventType &expected, + quint32 checks, const QVector<qint64> &expectedNumbers) { - QVector<QQmlProfilerData> *target = 0; + if (!m_client) { + qWarning() << "No debug client available"; + return false; + } + + const QVector<QQmlProfilerEvent> *target = nullptr; switch (type) { case MessageListQML: target = &(m_client->qmlMessages); break; case MessageListJavaScript: target = &(m_client->javascriptMessages); break; @@ -428,48 +369,93 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty case MessageListPixmap: target = &(m_client->pixmapMessages); break; } + if (!target) { + qWarning() << "Invalid MessageListType" << type; + return false; + } + if (target->length() <= expectedPosition) { qWarning() << "Not enough events. expected position:" << expectedPosition << "length:" << target->length(); return false; } - uint position = expectedPosition; - qint64 timestamp = target->at(expectedPosition).time; - while (position > 0 && target->at(position - 1).time == timestamp) + int position = expectedPosition; + qint64 timestamp = target->at(expectedPosition).timestamp(); + while (position > 0 && target->at(position - 1).timestamp() == timestamp) --position; QStringList warnings; do { - const QQmlProfilerData &actual = target->at(position); - if ((checks & CheckMessageType) && actual.messageType != expected.messageType) { - warnings << QString::fromLatin1("%1: unexpected messageType. actual: %2 - expected: %3") - .arg(position).arg(actual.messageType).arg(expected.messageType); + const QQmlProfilerEvent &event = target->at(position); + const QQmlProfilerEventType &actual = m_client->types.at(event.typeIndex()); + if ((checks & CheckMessageType) && + (actual.message() != expected.message() + || actual.rangeType() != expected.rangeType())) { + warnings << QString::fromLatin1("%1: unexpected messageType or rangeType. " + "actual: %2, %3 - expected: %4, %5") + .arg(position).arg(actual.message()).arg(actual.rangeType()) + .arg(expected.message()).arg(expected.rangeType()); continue; } - if ((checks & CheckDetailType) && actual.detailType != expected.detailType) { + if ((checks & CheckDetailType) && actual.detailType() != expected.detailType()) { warnings << QString::fromLatin1("%1: unexpected detailType. actual: %2 - expected: %3") - .arg(position).arg(actual.detailType).arg(expected.detailType); + .arg(position).arg(actual.detailType()).arg(expected.detailType()); continue; } - if ((checks & CheckLine) && actual.line != expected.line) { + + const QQmlProfilerEventLocation expectedLocation = expected.location(); + const QQmlProfilerEventLocation actualLocation = actual.location(); + + if ((checks & CheckLine) && actualLocation.line() != expectedLocation.line()) { warnings << QString::fromLatin1("%1: unexpected line. actual: %2 - expected: %3") - .arg(position).arg(actual.line).arg(expected.line); + .arg(position).arg(actualLocation.line()) + .arg(expectedLocation.line()); continue; } - if ((checks & CheckColumn) && actual.column != expected.column) { + if ((checks & CheckColumn) && actualLocation.column() != expectedLocation.column()) { warnings << QString::fromLatin1("%1: unexpected column. actual: %2 - expected: %3") - .arg(position).arg(actual.column).arg(expected.column); + .arg(position).arg(actualLocation.column()) + .arg(expectedLocation.column()); continue; } - if ((checks & CheckDataEndsWith) && !actual.detailData.endsWith(expected.detailData)) { + if ((checks & CheckFileEndsWith) && + !actualLocation.filename().endsWith(expectedLocation.filename())) { + warnings << QString::fromLatin1("%1: unexpected fileName. actual: %2 - expected: %3") + .arg(position).arg(actualLocation.filename()) + .arg(expectedLocation.filename()); + continue; + } + + if ((checks & CheckDataEndsWith) && !actual.data().endsWith(expected.data())) { warnings << QString::fromLatin1("%1: unexpected detailData. actual: %2 - expected: %3") - .arg(position).arg(actual.detailData).arg(expected.detailData); + .arg(position).arg(actual.data()).arg(expected.data()); continue; } + + if (checks & CheckNumbers) { + const QVector<qint64> actualNumbers = event.numbers<QVector<qint64>>(); + if (actualNumbers != expectedNumbers) { + + QStringList expectedList; + for (qint64 number : expectedNumbers) + expectedList.append(QString::number(number)); + QStringList actualList; + for (qint64 number : actualNumbers) + actualList.append(QString::number(number)); + + warnings << QString::fromLatin1( + "%1: unexpected numbers. actual [%2] - expected: [%3]") + .arg(position) + .arg(actualList.join(QLatin1String(", "))) + .arg(expectedList.join(QLatin1String(", "))); + continue; + } + } + return true; - } while (target->at(++position).time == timestamp); + } while (++position < target->length() && target->at(position).timestamp() == timestamp); foreach (const QString &message, warnings) qWarning() << message.toLocal8Bit().constData(); @@ -477,54 +463,62 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty return false; } +QList<QQmlDebugClient *> tst_QQmlProfilerService::createClients() +{ + m_client.reset(new QQmlProfilerTestClient(m_connection)); + m_client->client->setRecording(m_recordFromStart); + m_client->client->setFlushInterval(m_flushInterval); + QObject::connect(m_client->client.data(), &QQmlProfilerClient::complete, + this, [this](){ m_isComplete = true; }); + return QList<QQmlDebugClient *>({m_client->client}); +} + void tst_QQmlProfilerService::cleanup() { + auto log = [this](const QQmlProfilerEvent &data, int i) { + const QQmlProfilerEventType &type = m_client->types.at(data.typeIndex()); + const QQmlProfilerEventLocation location = type.location(); + qDebug() << i << data.timestamp() << type.message() << type.rangeType() << type.detailType() + << location.filename() << location.line() << location.column() + << data.numbers<QVector<qint64>>(); + }; + if (m_client && QTest::currentTestFailed()) { qDebug() << "QML Messages:" << m_client->qmlMessages.count(); int i = 0; - foreach (const QQmlProfilerData &data, m_client->qmlMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->qmlMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "JavaScript Messages:" << m_client->javascriptMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->javascriptMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + + for (const QQmlProfilerEvent &data : qAsConst(m_client->javascriptMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Asynchronous Messages:" << m_client->asynchronousMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->asynchronousMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.framerate << data.animationcount << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->asynchronousMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Pixmap Cache Messages:" << m_client->pixmapMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->pixmapMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->pixmapMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Javascript Heap Messages:" << m_client->jsHeapMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->jsHeapMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->jsHeapMessages)) + log(data, i++); + qDebug() << " "; - qDebug() << "Process State:" << (m_process ? m_process->state() : QLatin1String("null")); - qDebug() << "Application Output:" << (m_process ? m_process->output() : QLatin1String("null")); - qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(m_connection); - qDebug() << "Client State:" << QQmlDebugTest::clientStateString(m_client); } - delete m_process; - m_process = 0; - delete m_client; - m_client = 0; - delete m_connection; - m_connection = 0; + + m_client.reset(); + QQmlDebugTest::cleanup(); } void tst_QQmlProfilerService::connect_data() @@ -548,63 +542,63 @@ void tst_QQmlProfilerService::connect() QFETCH(bool, restrictMode); QFETCH(bool, traceEnabled); - connect(blockMode, "test.qml", restrictMode); + QCOMPARE(connect(blockMode, "test.qml", traceEnabled, 0, restrictMode), ConnectSuccess); - // if the engine is waiting, then the first message determines if it starts with trace enabled if (!traceEnabled) - m_client->sendRecordingStatus(false); - m_client->sendRecordingStatus(true); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(true); + + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } void tst_QQmlProfilerService::pixmapCacheData() { - connect(true, "pixmapCacheTest.qml"); - m_client->sendRecordingStatus(true); - QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); + QCOMPARE(connect(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 && m_process->output().indexOf(QLatin1String("image error")) == -1) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::PixmapCacheEvent); + auto createType = [](PixmapEventType type) { + return QQmlProfilerEventType(PixmapCacheEvent, MaximumRangeType, type); + }; + + QVector<qint64> numbers; // image starting to load - expected.detailType = QQmlProfilerDefinitions::PixmapLoadingStarted; - VERIFY(MessageListPixmap, 0, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 0, createType(PixmapLoadingStarted), + CheckMessageType | CheckDetailType, numbers); // image size - expected.detailType = QQmlProfilerDefinitions::PixmapSizeKnown; - expected.line = expected.column = 2; // width and height, in fact - VERIFY(MessageListPixmap, 1, expected, - CheckMessageType | CheckDetailType | CheckLine | CheckColumn); + numbers = QVector<qint64>({2, 2, 1}); + VERIFY(MessageListPixmap, 1, createType(PixmapSizeKnown), + CheckMessageType | CheckDetailType | CheckNumbers, numbers); // image loaded - expected.detailType = QQmlProfilerDefinitions::PixmapLoadingFinished; - VERIFY(MessageListPixmap, 2, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 2, createType(PixmapLoadingFinished), + CheckMessageType | CheckDetailType, numbers); // cache size - expected.detailType = QQmlProfilerDefinitions::PixmapCacheCountChanged; - VERIFY(MessageListPixmap, 3, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 3, createType(PixmapCacheCountChanged), + CheckMessageType | CheckDetailType, numbers); } void tst_QQmlProfilerService::scenegraphData() { - connect(true, "scenegraphTest.qml"); - - m_client->sendRecordingStatus(true); + QCOMPARE(connect(true, "scenegraphTest.qml"), ConnectSuccess); while (!m_process->output().contains(QLatin1String("tick"))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); @@ -619,34 +613,39 @@ void tst_QQmlProfilerService::scenegraphData() qint64 contextFrameTime = -1; qint64 renderFrameTime = -1; #if QT_CONFIG(opengl) //Software renderer doesn't have context frames - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) { - contextFrameTime = msg.time; - break; + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.message() == SceneGraphFrame) { + if (type.detailType() == SceneGraphContextFrame) { + contextFrameTime = msg.timestamp(); + break; + } } } - } - QVERIFY(contextFrameTime != -1); + QVERIFY(contextFrameTime != -1); + } #endif - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRendererFrame) { - QVERIFY(msg.time >= contextFrameTime); - renderFrameTime = msg.time; + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.detailType() == SceneGraphRendererFrame) { + QVERIFY(msg.timestamp() >= contextFrameTime); + renderFrameTime = msg.timestamp(); break; } } QVERIFY(renderFrameTime != -1); - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) { - if (msg.time >= contextFrameTime) { + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.detailType() == SceneGraphRenderLoopFrame) { + if (msg.timestamp() >= contextFrameTime) { // Make sure SceneGraphRenderLoopFrame is not between SceneGraphContextFrame and // SceneGraphRendererFrame. A SceneGraphRenderLoopFrame before everything else is // OK as the scene graph might decide to do an initial rendering. - QVERIFY(msg.time >= renderFrameTime); + QVERIFY(msg.timestamp() >= renderFrameTime); break; } } @@ -655,9 +654,8 @@ void tst_QQmlProfilerService::scenegraphData() void tst_QQmlProfilerService::profileOnExit() { - connect(true, "exit.qml"); - - m_client->sendRecordingStatus(true); + QCOMPARE(connect(true, "exit.qml"), ConnectSuccess); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); @@ -665,78 +663,182 @@ void tst_QQmlProfilerService::profileOnExit() void tst_QQmlProfilerService::controlFromJS() { - connect(true, "controlFromJS.qml"); + QCOMPARE(connect(true, "controlFromJS.qml", false), ConnectSuccess); - m_client->sendRecordingStatus(false); + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } void tst_QQmlProfilerService::signalSourceLocation() { - connect(true, "signalSourceLocation.qml"); + QCOMPARE(connect(true, "signalSourceLocation.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); while (!(m_process->output().contains(QLatin1String("500")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeLocation, - QQmlProfilerDefinitions::HandlingSignal, - QLatin1String("signalSourceLocation.qml")); - expected.line = 8; - expected.column = 28; - VERIFY(MessageListQML, 9, expected, CheckAll); + auto createType = [](int line, int column) { + return QQmlProfilerEventType( + MaximumMessage, HandlingSignal, -1, + QQmlProfilerEventLocation(QLatin1String("signalSourceLocation.qml"), line, + column)); + }; - expected.line = 7; - expected.column = 21; - VERIFY(MessageListQML, 11, expected, CheckAll); + VERIFY(MessageListQML, 4, createType(8, 28), CheckType | CheckNumbers, m_rangeStart); + VERIFY(MessageListQML, 6, createType(7, 21), CheckType | CheckNumbers, m_rangeEnd); } void tst_QQmlProfilerService::javascript() { - connect(true, "javascript.qml"); + QCOMPARE(connect(true, "javascript.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); while (!(m_process->output().contains(QLatin1String("done")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeStart, - QQmlProfilerDefinitions::Javascript); - VERIFY(MessageListJavaScript, 6, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListJavaScript, 2, QQmlProfilerEventType(MaximumMessage, Javascript), + CheckMessageType | CheckDetailType | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeLocation; - expected.detailData = QLatin1String("javascript.qml"); - expected.line = 4; - expected.column = 5; - VERIFY(MessageListJavaScript, 7, expected, CheckAll); + VERIFY(MessageListJavaScript, 3, + QQmlProfilerEventType( + MaximumMessage, Javascript, -1, + QQmlProfilerEventLocation(QLatin1String("javascript.qml"), 4, 5)), + CheckType | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeData; - expected.detailData = QLatin1String("something"); - VERIFY(MessageListJavaScript, 8, expected, - CheckMessageType | CheckDetailType | CheckDataEndsWith); + VERIFY(MessageListJavaScript, 4, QQmlProfilerEventType( + MaximumMessage, Javascript, -1, + QQmlProfilerEventLocation(), QLatin1String("something")), + CheckMessageType | CheckDetailType | CheckDataEndsWith | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeEnd; - VERIFY(MessageListJavaScript, 21, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListJavaScript, 10, QQmlProfilerEventType(MaximumMessage, Javascript), + CheckMessageType | CheckDetailType | CheckNumbers, m_rangeEnd); } void tst_QQmlProfilerService::flushInterval() { - connect(true, "timer.qml"); - - m_client->sendRecordingStatus(true, -1, 1); + QCOMPARE(connect(true, "timer.qml", true, 1), ConnectSuccess); // Make sure we get multiple messages QTRY_VERIFY(m_client->qmlMessages.length() > 0); QVERIFY(m_client->qmlMessages.length() < 100); QTRY_VERIFY(m_client->qmlMessages.length() > 100); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); + checkTraceReceived(); + checkJsHeap(); +} + +void tst_QQmlProfilerService::translationBinding() +{ + QCOMPARE(connect(true, "qstr.qml"), ConnectSuccess); + checkProcessTerminated(); + + checkTraceReceived(); + checkJsHeap(); + + const QQmlProfilerEventType type(MaximumMessage, Binding); + + VERIFY(MessageListQML, 4, type, CheckDetailType | CheckMessageType | CheckNumbers, + m_rangeStart); + VERIFY(MessageListQML, 5, type, CheckDetailType | CheckMessageType | CheckNumbers, + m_rangeEnd); +} + +void tst_QQmlProfilerService::memory() +{ + QCOMPARE(connect(true, "memory.qml"), ConnectSuccess); + checkProcessTerminated(); + + checkTraceReceived(); + checkJsHeap(); + + QVERIFY(m_client); + int smallItems = 0; + for (const auto& message : m_client->jsHeapMessages) { + const QQmlProfilerEventType &type = m_client->types[message.typeIndex()]; + if (type.detailType() == SmallItem) + ++smallItems; + } + + QVERIFY(smallItems > 5); +} + +static bool hasCompileEvents(const QVector<QQmlProfilerEventType> &types) +{ + for (const QQmlProfilerEventType &type : types) { + if (type.message() == MaximumMessage && type.rangeType() == Compiling) + return true; + } + return false; +} + +void tst_QQmlProfilerService::compile() +{ + // Flush interval so that we actually get the events before we stop recording. + connect(true, "test.qml", true, 100); + + QVERIFY(m_client); + + // We need to check specifically for compile events as we can otherwise stop recording after the + // StartTrace has arrived, but before it compiles anything. + QTRY_VERIFY(hasCompileEvents(m_client->types)); + m_client->client->setRecording(false); + + checkTraceReceived(); + checkJsHeap(); + + Message rangeStage = MaximumMessage; + for (const auto& message : m_client->qmlMessages) { + const QQmlProfilerEventType &type = m_client->types[message.typeIndex()]; + if (type.rangeType() == Compiling) { + switch (rangeStage) { + case MaximumMessage: + QCOMPARE(message.rangeStage(), RangeStart); + break; + case RangeStart: + QCOMPARE(message.rangeStage(), RangeEnd); + break; + default: + QFAIL("Wrong range stage"); + } + rangeStage = message.rangeStage(); + QCOMPARE(type.message(), MaximumMessage); + QCOMPARE(type.location().filename(), testFileUrl("test.qml").toString()); + QCOMPARE(type.location().line(), 0); + QCOMPARE(type.location().column(), 0); + } + } + + QCOMPARE(rangeStage, RangeEnd); +} + +void tst_QQmlProfilerService::multiEngine() +{ + QCOMPARE(connect(true, "quit.qml", true, 0, false, debugJsServerPath("qqmlprofilerservice")), + ConnectSuccess); + + QSignalSpy spy(m_client->client, SIGNAL(complete(qint64))); + + checkTraceReceived(); + checkJsHeap(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); + + QCOMPARE(spy.count(), 1); +} + +void tst_QQmlProfilerService::batchOverflow() +{ + // The trace client checks that the events are received in order. + QCOMPARE(connect(true, "batchOverflow.qml"), ConnectSuccess); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); } diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 441f8c113f..b75fb6b895 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -38,7 +38,6 @@ #include <private/qv4debugging_p.h> #include <private/qv8engine_p.h> #include <private/qv4objectiterator_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qv4string_p.h> #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmldebugservice_p.h> @@ -46,7 +45,7 @@ using namespace QV4; using namespace QV4::Debugging; -typedef void (*InjectedFunction)(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +typedef QV4::ReturnedValue (*InjectedFunction)(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int); Q_DECLARE_METATYPE(InjectedFunction) static bool waitForSignal(QObject* obj, const char* signal, int timeout = 10000) @@ -79,7 +78,7 @@ public: emit evaluateFinished(); } - QV4::ExecutionEngine *v4Engine() { return QV8Engine::getV4(this); } + QV4::ExecutionEngine *v4Engine() { return handle(); } Q_INVOKABLE void injectFunction(const QString &functionName, InjectedFunction injectedFunction) { @@ -87,8 +86,7 @@ public: QV4::Scope scope(v4); QV4::ScopedString name(scope, v4->newString(functionName)); - QV4::ScopedContext ctx(scope, v4->rootContext()); - QV4::ScopedValue function(scope, BuiltinFunction::create(ctx, name, injectedFunction)); + QV4::ScopedValue function(scope, FunctionObject::createBuiltinFunction(v4, name, injectedFunction, 0)); v4->globalObject->put(name, function); } @@ -107,7 +105,7 @@ public: void run() { QV4::Scope scope(collector->engine()); QV4::ScopedValue v(scope, *collector->engine()->exceptionValue); - exception = collector->collect(v); + exception = collector->addValueRef(v); } QV4DataCollector::Ref exceptionValue() const { return exception; } @@ -167,7 +165,7 @@ public: , m_thrownValue(-1) , collector(engine) , m_resumeSpeed(QV4Debugger::FullThrottle) - , m_debugger(0) + , m_debugger(nullptr) { } @@ -221,16 +219,26 @@ public: { for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) { m_capturedScope.append(NamedRefs()); - ScopeJob job(&collector, i, 0); + FrameJob frameJob(&collector, i); + debugger->runInEngine(&frameJob); + QJsonObject frameObj = frameJob.returnValue(); + QJsonArray scopes = frameObj.value(QLatin1String("scopes")).toArray(); + int nscopes = scopes.size(); + int s = 0; + for (s = 0; s < nscopes; ++s) { + QJsonObject o = scopes.at(s).toObject(); + if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext + break; + } + if (s == nscopes) + return; + + ScopeJob job(&collector, i, s); debugger->runInEngine(&job); NamedRefs &refs = m_capturedScope.last(); QJsonObject object = job.returnValue(); object = object.value(QLatin1String("object")).toObject(); - if (object.contains("ref") && !object.contains("properties")) { - QVERIFY(collector.redundantRefs()); - object = collector.lookupRef(object.value("ref").toInt(), true); - QVERIFY(object.contains("properties")); - } + QVERIFY(!object.contains("ref") || object.contains("properties")); foreach (const QJsonValue &value, object.value(QLatin1String("properties")).toArray()) { QJsonObject property = value.toObject(); QString name = property.value(QLatin1String("name")).toString(); @@ -296,13 +304,10 @@ private slots: void conditionalBreakPointInQml(); // context access: - void readArguments_data() { redundancy_data(); } void readArguments(); - void readLocals_data() { redundancy_data(); } + void readComplicatedArguments(); void readLocals(); - void readObject_data() { redundancy_data(); } void readObject(); - void readContextInAllFrames_data() { redundancy_data(); } void readContextInAllFrames(); // exceptions: @@ -310,13 +315,13 @@ private slots: void breakInCatch(); void breakInWith(); - void evaluateExpression_data() { redundancy_data(); } void evaluateExpression(); - void stepToEndOfScript_data() { redundancy_data(); } void stepToEndOfScript(); void lastLineOfConditional_data(); void lastLineOfConditional(); + + void readThis(); private: QV4Debugger *debugger() const { @@ -330,8 +335,6 @@ private: waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0); } - void redundancy_data(); - TestEngine *m_engine; QV4::ExecutionEngine *m_v4; TestAgent *m_debuggerAgent; @@ -343,7 +346,6 @@ void tst_qv4debugger::init() m_javaScriptThread = new QThread; m_engine = new TestEngine; m_v4 = m_engine->v4Engine(); - m_v4->iselFactory.reset(new QV4::Moth::ISelFactory); m_v4->setDebugger(new QV4Debugger(m_v4)); m_engine->moveToThread(m_javaScriptThread); m_javaScriptThread->start(); @@ -357,10 +359,10 @@ void tst_qv4debugger::cleanup() m_javaScriptThread->wait(); delete m_engine; delete m_javaScriptThread; - m_engine = 0; - m_v4 = 0; + m_engine = nullptr; + m_v4 = nullptr; delete m_debuggerAgent; - m_debuggerAgent = 0; + m_debuggerAgent = nullptr; } void tst_qv4debugger::breakAnywhere() @@ -438,9 +440,9 @@ void tst_qv4debugger::addBreakPointWhilePaused() QCOMPARE(state.lineNumber, 2); } -static void someCall(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *) +static QV4::ReturnedValue someCall(const FunctionObject *function, const QV4::Value *, const QV4::Value *, int) { - static_cast<QV4Debugger *>(scope.engine->debugger()) + static_cast<QV4Debugger *>(function->engine()->debugger()) ->removeBreakPoint("removeBreakPointForNextInstruction", 2); RETURN_UNDEFINED(); } @@ -464,7 +466,7 @@ void tst_qv4debugger::conditionalBreakPoint() { m_debuggerAgent->m_captureContextInfo = true; QString script = - "function test() {\n" + "var test = function() {\n" " for (var i = 0; i < 15; ++i) {\n" " var x = i;\n" " }\n" @@ -481,7 +483,7 @@ void tst_qv4debugger::conditionalBreakPoint() QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 2); + QCOMPARE(frame0.size(), 3); QVERIFY(frame0.contains("i")); QCOMPARE(frame0.value("i").toInt(), 11); } @@ -489,9 +491,8 @@ void tst_qv4debugger::conditionalBreakPoint() void tst_qv4debugger::conditionalBreakPointInQml() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + QV4::ExecutionEngine *v4 = engine.handle(); QV4Debugger *v4Debugger = new QV4Debugger(v4); - v4->iselFactory.reset(new QV4::Moth::ISelFactory); v4->setDebugger(v4Debugger); QScopedPointer<QThread> debugThread(new QThread); @@ -526,12 +527,9 @@ void tst_qv4debugger::conditionalBreakPointInQml() void tst_qv4debugger::readArguments() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - m_debuggerAgent->m_captureContextInfo = true; QString script = - "function f(a, b, c, d) {\n" + "var f = function(a, b, c, d) {\n" " return a === b\n" "}\n" "var four;\n" @@ -541,7 +539,7 @@ void tst_qv4debugger::readArguments() QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 4); + QCOMPARE(frame0.size(), 5); QVERIFY(frame0.contains(QStringLiteral("a"))); QCOMPARE(frame0.type(QStringLiteral("a")), QStringLiteral("number")); QCOMPARE(frame0.value(QStringLiteral("a")).toDouble(), 1.0); @@ -550,14 +548,31 @@ void tst_qv4debugger::readArguments() QCOMPARE(frame0.value(QStringLiteral("b")).toString(), QStringLiteral("two")); } -void tst_qv4debugger::readLocals() +void tst_qv4debugger::readComplicatedArguments() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; + QString script = + "var f = function(a) {\n" + " a = 12;\n" + " return a;\n" + "}\n" + "f(1, 2);\n"; + debugger()->addBreakPoint("readArguments", 3); + evaluateJavaScript(script, "readArguments"); + QVERIFY(m_debuggerAgent->m_wasPaused); + QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); + const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); + QCOMPARE(frame0.size(), 2); + QVERIFY(frame0.contains(QStringLiteral("a"))); + QCOMPARE(frame0.type(QStringLiteral("a")), QStringLiteral("number")); + QCOMPARE(frame0.value(QStringLiteral("a")).toInt(), 12); +} +void tst_qv4debugger::readLocals() +{ m_debuggerAgent->m_captureContextInfo = true; QString script = - "function f(a, b) {\n" + "var f = function(a, b) {\n" " var c = a + b\n" " var d = a - b\n" // breakpoint, c should be set, d should be undefined " return c === d\n" @@ -568,7 +583,7 @@ void tst_qv4debugger::readLocals() QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 4); // locals and parameters + QCOMPARE(frame0.size(), 5); // locals and parameters QVERIFY(frame0.contains("c")); QCOMPARE(frame0.type("c"), QStringLiteral("number")); QCOMPARE(frame0.value("c").toDouble(), 3.0); @@ -578,12 +593,9 @@ void tst_qv4debugger::readLocals() void tst_qv4debugger::readObject() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - m_debuggerAgent->m_captureContextInfo = true; QString script = - "function f(a) {\n" + "var f = function(a) {\n" " var b = a\n" " return b\n" "}\n" @@ -593,7 +605,7 @@ void tst_qv4debugger::readObject() QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 2); + QCOMPARE(frame0.size(), 3); QVERIFY(frame0.contains("b")); QCOMPARE(frame0.type("b"), QStringLiteral("object")); QJsonObject b = frame0.rawValue("b"); @@ -602,7 +614,7 @@ void tst_qv4debugger::readObject() QVERIFY(!b.contains(QStringLiteral("properties"))); QVERIFY(b.value("value").isDouble()); QCOMPARE(b.value("value").toInt(), 2); - b = m_debuggerAgent->collector.lookupRef(b.value("ref").toInt(), true); + b = m_debuggerAgent->collector.lookupRef(b.value("ref").toInt()); QVERIFY(b.contains(QStringLiteral("properties"))); QVERIFY(b.value("properties").isArray()); QJsonArray b_props = b.value("properties").toArray(); @@ -618,8 +630,7 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail.value("name").toString(), QStringLiteral("tail")); QVERIFY(b_tail.contains("ref")); - QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt(), - true); + QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt()); QCOMPARE(b_tail_value.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_value.contains("properties")); QJsonArray b_tail_props = b_tail_value.value("properties").toArray(); @@ -630,18 +641,15 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail_head.value("value").toString(), QStringLiteral("asdf")); QJsonObject b_tail_tail = b_tail_props.at(1).toObject(); QCOMPARE(b_tail_tail.value("name").toString(), QStringLiteral("tail")); - QCOMPARE(b_tail_tail.value("type").toString(), QStringLiteral("null")); + QCOMPARE(b_tail_tail.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_tail.value("value").isNull()); } void tst_qv4debugger::readContextInAllFrames() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - m_debuggerAgent->m_captureContextInfo = true; QString script = - "function fact(n) {\n" + "var fact = function(n) {\n" " if (n > 1) {\n" " var n_1 = n - 1;\n" " n_1 = fact(n_1);\n" @@ -658,7 +666,7 @@ void tst_qv4debugger::readContextInAllFrames() for (int i = 0; i < 12; ++i) { const TestAgent::NamedRefs &scope = m_debuggerAgent->m_capturedScope.at(i); - QCOMPARE(scope.size(), 2); + QCOMPARE(scope.size(), 3); QVERIFY(scope.contains("n")); QCOMPARE(scope.type("n"), QStringLiteral("number")); QCOMPARE(scope.value("n").toDouble(), i + 1.0); @@ -686,8 +694,7 @@ void tst_qv4debugger::pauseOnThrow() QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Throwing); QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 2); QVERIFY(m_debuggerAgent->m_thrownValue >= qint64(0)); - QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue, - true); + QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue); // DUMP_JSON(exception); QCOMPARE(exception.value("type").toString(), QStringLiteral("string")); QCOMPARE(exception.value("value").toString(), QStringLiteral("hard")); @@ -731,9 +738,6 @@ void tst_qv4debugger::breakInWith() void tst_qv4debugger::evaluateExpression() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - QString script = "function testFunction() {\n" " var x = 10\n" @@ -775,9 +779,6 @@ void tst_qv4debugger::evaluateExpression() void tst_qv4debugger::stepToEndOfScript() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - QString script = "var ret = 0;\n" "ret += 4;\n" @@ -814,13 +815,13 @@ void tst_qv4debugger::lastLineOfConditional_data() QTest::newRow("do..while {block}") << "do {\n" << "} while (ret < 10);" << 4 << 7; QTest::newRow("if true {block}") << "if (true) {\n" << "}" - << 4 << 7; + << 4 << 8; QTest::newRow("if false {block}") << "if (false) {\n" << "}" << 2 << 8; QTest::newRow("if true else {block}") << "if (true) {\n" << "} else {\n ret += 8;\n}" - << 4 << 7; + << 4 << 10; QTest::newRow("if false else {block}") << "if (false) {\n" << "} else {\n ret += 8;\n}" - << 8 << 9; + << 8 << 10; QTest::newRow("for statement") << "for (var i = 0; i < 10; ++i)\n" << "" << 4 << 2; QTest::newRow("for..in statement") << "for (var i in [0, 1, 2, 3, 4])\n" << "" << 4 << 2; @@ -829,11 +830,11 @@ void tst_qv4debugger::lastLineOfConditional_data() // For two nested if statements without blocks, we need to map the jump from the inner to the // outer one on the outer "if". There is just no better place. - QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 2; + QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 8; QTest::newRow("if false statement") << "if (false)\n" << "" << 2 << 8; // Also two nested ifs without blocks. - QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 2; + QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 9; QTest::newRow("if false else statement") << "if (false)\n" << "else\n ret += 8;" << 8 << 9; } @@ -868,11 +869,34 @@ void tst_qv4debugger::lastLineOfConditional() QCOMPARE(secondState.lineNumber, lastLine); } -void tst_qv4debugger::redundancy_data() +void tst_qv4debugger::readThis() { - QTest::addColumn<bool>("redundantRefs"); - QTest::addRow("redundant") << true; - QTest::addRow("sparse") << false; + m_debuggerAgent->m_captureContextInfo = true; + QString script = + "var x = function() {\n" + " return this.a;\n" + "}.apply({a : 5}, []);\n"; + + TestAgent::ExpressionRequest request; + request.expression = "this"; + request.frameNr = 0; + request.context = -1; // no extra context + m_debuggerAgent->m_expressionRequests << request; + + debugger()->addBreakPoint("applyThis", 2); + evaluateJavaScript(script, "applyThis"); + QVERIFY(m_debuggerAgent->m_wasPaused); + + QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 1); + QJsonObject result0 = m_debuggerAgent->m_expressionResults[0]; + QCOMPARE(result0.value("type").toString(), QStringLiteral("object")); + QCOMPARE(result0.value("value").toInt(), 1); + QJsonArray properties = result0.value("properties").toArray(); + QCOMPARE(properties.size(), 1); + QJsonObject a = properties.first().toObject(); + QCOMPARE(a.value("name").toString(), QStringLiteral("a")); + QCOMPARE(a.value("type").toString(), QStringLiteral("number")); + QCOMPARE(a.value("value").toInt(), 5); } QTEST_MAIN(tst_qv4debugger) diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp index 7b9e935678..68446b53a4 100644 --- a/tests/auto/qml/debugger/shared/debugutil.cpp +++ b/tests/auto/qml/debugger/shared/debugutil.cpp @@ -27,13 +27,12 @@ ****************************************************************************/ #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include <private/qqmldebugconnection_p.h> #include <QtCore/qeventloop.h> #include <QtCore/qtimer.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qdir.h> bool QQmlDebugTest::waitForSignal(QObject *receiver, const char *member, int timeout) { QEventLoop loop; @@ -51,15 +50,23 @@ bool QQmlDebugTest::waitForSignal(QObject *receiver, const char *member, int tim QList<QQmlDebugClient *> QQmlDebugTest::createOtherClients(QQmlDebugConnection *connection) { QList<QQmlDebugClient *> ret; - foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) { + + static const auto debuggerServices + = QStringList({"V8Debugger", "QmlDebugger", "DebugMessages"}); + static const auto inspectorServices + = QStringList({"QmlInspector"}); + static const auto profilerServices + = QStringList({"CanvasFrameRate", "EngineControl", "DebugMessages"}); + + for (const QString &service : debuggerServices) { if (!connection->client(service)) ret << new QQmlDebugClient(service, connection); } - foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) { + for (const QString &service : inspectorServices) { if (!connection->client(service)) ret << new QQmlDebugClient(service, connection); } - foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) { + for (const QString &service : profilerServices) { if (!connection->client(service)) ret << new QQmlDebugClient(service, connection); } @@ -91,6 +98,9 @@ QString QQmlDebugTest::connectionStateString(const QQmlDebugConnection *connecti QQmlDebugTestClient::QQmlDebugTestClient(const QString &s, QQmlDebugConnection *c) : QQmlDebugClient(s, c) { + connect(this, &QQmlDebugClient::stateChanged, this, [this](QQmlDebugClient::State newState) { + QCOMPARE(newState, state()); + }); } QByteArray QQmlDebugTestClient::waitForResponse() @@ -104,201 +114,150 @@ QByteArray QQmlDebugTestClient::waitForResponse() return lastMsg; } -void QQmlDebugTestClient::stateChanged(State stat) -{ - QCOMPARE(stat, state()); - emit stateHasChanged(); -} - void QQmlDebugTestClient::messageReceived(const QByteArray &ba) { lastMsg = ba; emit serverMessage(ba); } -QQmlDebugProcess::QQmlDebugProcess(const QString &executable, QObject *parent) - : QObject(parent) - , m_executable(executable) - , m_started(false) - , m_port(0) - , m_maximumBindErrors(0) - , m_receivedBindErrors(0) +QQmlDebugTest::ConnectResult QQmlDebugTest::connect( + const QString &executable, const QString &services, const QString &extraArgs, + bool block) { - m_process.setProcessChannelMode(QProcess::MergedChannels); - m_timer.setSingleShot(true); - m_timer.setInterval(5000); - connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processAppOutput())); - connect(&m_process, SIGNAL(errorOccurred(QProcess::ProcessError)), - this, SLOT(processError(QProcess::ProcessError))); - connect(&m_timer, SIGNAL(timeout()), SLOT(timeout())); -} + QStringList arguments; + arguments << QString::fromLatin1("-qmljsdebugger=port:13773,13783%3%4") + .arg(block ? QStringLiteral(",block") : QString()) + .arg(services.isEmpty() ? services : (QStringLiteral(",services:") + services)) + << extraArgs; -QQmlDebugProcess::~QQmlDebugProcess() -{ - stop(); -} + m_process = createProcess(executable); + if (!m_process) + return ProcessFailed; -QString QQmlDebugProcess::state() -{ - QString stateStr; - switch (m_process.state()) { - case QProcess::NotRunning: { - stateStr = "not running"; - if (m_process.exitStatus() == QProcess::CrashExit) - stateStr += " (crashed!)"; - else - stateStr += ", return value " + QString::number(m_process.exitCode()); - break; - } - case QProcess::Starting: stateStr = "starting"; break; - case QProcess::Running: stateStr = "running"; break; - } - return stateStr; -} + m_process->start(QStringList() << arguments); + if (!m_process->waitForSessionStart()) + return SessionFailed; -void QQmlDebugProcess::start(const QStringList &arguments) -{ -#ifdef Q_OS_MAC - // make sure m_executable points to the actual binary even if it's inside an app bundle - QFileInfo binFile(m_executable); - if (!binFile.isExecutable()) { - QDir bundleDir(m_executable + ".app"); - if (bundleDir.exists()) { - m_executable = bundleDir.absoluteFilePath("Contents/MacOS/" + binFile.baseName()); - //qDebug() << Q_FUNC_INFO << "found bundled binary" << m_executable; - } - } -#endif - m_mutex.lock(); - m_port = 0; - m_process.setEnvironment(QProcess::systemEnvironment() + m_environment); - m_process.start(m_executable, arguments); - if (!m_process.waitForStarted()) { - qWarning() << "QML Debug Client: Could not launch app " << m_executable - << ": " << m_process.errorString(); - m_eventLoop.quit(); - } else { - m_timer.start(); - } - m_mutex.unlock(); + m_connection = createConnection(); + if (!m_connection) + return ConnectionFailed; + + m_clients = createClients(); + if (m_clients.contains(nullptr)) + return ClientsFailed; + + ClientStateHandler stateHandler(m_clients, createOtherClients(m_connection), services.isEmpty() + ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); + + + const int port = m_process->debugPort(); + m_connection->connectToHost(QLatin1String("127.0.0.1"), port); + + QEventLoop loop; + QTimer timer; + QObject::connect(&stateHandler, &ClientStateHandler::allOk, &loop, &QEventLoop::quit); + QObject::connect(m_connection, &QQmlDebugConnection::disconnected, &loop, &QEventLoop::quit); + QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + + timer.start(5000); + loop.exec(); + + if (!stateHandler.allEnabled()) + return EnableFailed; + + if (!stateHandler.othersAsExpected()) + return RestrictFailed; + + return ConnectSuccess; } -void QQmlDebugProcess::stop() +QList<QQmlDebugClient *> QQmlDebugTest::createClients() { - if (m_process.state() != QProcess::NotRunning) { - m_process.kill(); - m_process.waitForFinished(5000); - } + return QList<QQmlDebugClient *>(); } -void QQmlDebugProcess::setMaximumBindErrors(int ignore) +QQmlDebugProcess *QQmlDebugTest::createProcess(const QString &executable) { - m_maximumBindErrors = ignore; + return new QQmlDebugProcess(executable, this); } -void QQmlDebugProcess::timeout() +QQmlDebugConnection *QQmlDebugTest::createConnection() { - qWarning() << "Timeout while waiting for QML debugging messages " - "in application output. Process is in state" << m_process.state() << ", Output:" << m_output << "."; - m_eventLoop.quit(); + return new QQmlDebugConnection(this); } -bool QQmlDebugProcess::waitForSessionStart() +void QQmlDebugTest::cleanup() { - if (m_process.state() != QProcess::Running) { - qWarning() << "Could not start up " << m_executable; - return false; + if (QTest::currentTestFailed()) { + const QString null = QStringLiteral("null"); + + qDebug() << "Process State:" << (m_process ? m_process->stateString() : null); + qDebug() << "Application Output:" << (m_process ? m_process->output() : null); + qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(m_connection); + for (QQmlDebugClient *client : m_clients) { + if (client) + qDebug() << client->name() << "State:" << QQmlDebugTest::clientStateString(client); + else + qDebug() << "Failed Client:" << null; + } } - m_eventLoop.exec(); - return m_started; -} + qDeleteAll(m_clients); + m_clients.clear(); -int QQmlDebugProcess::debugPort() const -{ - return m_port; -} + delete m_connection; + m_connection = nullptr; -bool QQmlDebugProcess::waitForFinished() -{ - return m_process.waitForFinished(); + if (m_process) { + m_process->stop(); + delete m_process; + m_process = nullptr; + } } -QProcess::ExitStatus QQmlDebugProcess::exitStatus() const +ClientStateHandler::ClientStateHandler(const QList<QQmlDebugClient *> &clients, + const QList<QQmlDebugClient *> &others, + QQmlDebugClient::State expectedOthers) : + m_clients(clients), m_others(others), m_expectedOthers(expectedOthers) { - return m_process.exitStatus(); + for (QQmlDebugClient *client : m_clients) { + QObject::connect(client, &QQmlDebugClient::stateChanged, + this, &ClientStateHandler::checkStates); + } + for (QQmlDebugClient *client : m_others) { + QObject::connect(client, &QQmlDebugClient::stateChanged, + this, &ClientStateHandler::checkStates); + } } -void QQmlDebugProcess::addEnvironment(const QString &environment) +ClientStateHandler::~ClientStateHandler() { - m_environment.append(environment); + qDeleteAll(m_others); } -QString QQmlDebugProcess::output() const +void ClientStateHandler::checkStates() { - return m_output; -} + for (QQmlDebugClient *client : m_clients) { + if (client->state() != QQmlDebugClient::Enabled) + return; + } -void QQmlDebugProcess::processAppOutput() -{ - m_mutex.lock(); - - bool outputFromAppItself = false; - - QString newOutput = m_process.readAll(); - m_output.append(newOutput); - m_outputBuffer.append(newOutput); - - while (true) { - const int nlIndex = m_outputBuffer.indexOf(QLatin1Char('\n')); - if (nlIndex < 0) // no further complete lines - break; - const QString line = m_outputBuffer.left(nlIndex); - m_outputBuffer = m_outputBuffer.right(m_outputBuffer.size() - nlIndex - 1); - - if (line.contains("QML Debugger:")) { - const QRegExp portRx("Waiting for connection on port (\\d+)"); - if (portRx.indexIn(line) != -1) { - m_port = portRx.cap(1).toInt(); - m_timer.stop(); - m_started = true; - m_eventLoop.quit(); - continue; - } - if (line.contains("Unable to listen")) { - if (++m_receivedBindErrors >= m_maximumBindErrors) { - if (m_maximumBindErrors == 0) - qWarning() << "App was unable to bind to port!"; - m_timer.stop(); - m_eventLoop.quit(); - } - continue; - } - } else { - // set to true if there is output not coming from the debugger - outputFromAppItself = true; - } + m_allEnabled = true; + + for (QQmlDebugClient *other : m_others) { + if (other->state() != m_expectedOthers) + return; } - m_mutex.unlock(); - if (outputFromAppItself) - emit readyReadStandardOutput(); + m_othersAsExpected = true; + emit allOk(); } -void QQmlDebugProcess::processError(QProcess::ProcessError error) +QString debugJsServerPath(const QString &selfPath) { - if (!m_eventLoop.isRunning()) - return; - - qDebug() << "An error occurred while waiting for debug process to become available:"; - switch (error) { - case QProcess::FailedToStart: qDebug() << "Process failed to start."; break; - case QProcess::Crashed: qDebug() << "Process crashed."; break; - case QProcess::Timedout: qDebug() << "Process timed out."; break; - case QProcess::WriteError: qDebug() << "Error while writing to process."; break; - case QProcess::ReadError: qDebug() << "Error while reading from process."; break; - case QProcess::UnknownError: qDebug() << "Unknown process error."; break; - } - - m_eventLoop.exit(); + static const char *debugserver = "qqmldebugjsserver"; + QString appPath = QCoreApplication::applicationDirPath(); + const int position = appPath.lastIndexOf(selfPath); + return (position == -1 ? appPath : appPath.replace(position, selfPath.length(), debugserver)) + + "/" + debugserver; } diff --git a/tests/auto/qml/debugger/shared/debugutil.pri b/tests/auto/qml/debugger/shared/debugutil.pri index 1983f3583e..13dcdb91d8 100644 --- a/tests/auto/qml/debugger/shared/debugutil.pri +++ b/tests/auto/qml/debugger/shared/debugutil.pri @@ -1,4 +1,11 @@ QT += qmldebug-private -HEADERS += $$PWD/debugutil_p.h -SOURCES += $$PWD/debugutil.cpp +INCLUDEPATH += $$PWD +include($$PWD/../../../shared/util.pri) + +HEADERS += \ + $$PWD/debugutil_p.h \ + $$PWD/qqmldebugprocess_p.h +SOURCES += \ + $$PWD/debugutil.cpp \ + $$PWD/qqmldebugprocess.cpp diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h index 1ec0a6513d..1c32590305 100644 --- a/tests/auto/qml/debugger/shared/debugutil_p.h +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. @@ -41,23 +40,43 @@ // We mean it. // +#include <../../../shared/util.h> #include <private/qqmldebugclient_p.h> -#include <QtCore/qeventloop.h> -#include <QtCore/qtimer.h> -#include <QtCore/qthread.h> -#include <QtCore/qprocess.h> -#include <QtCore/qmutex.h> -#include <QtTest/qtest.h> -#include <QtQml/qqmlengine.h> - -class QQmlDebugTest +class QQmlDebugProcess; +class QQmlDebugTest : public QQmlDataTest { + Q_OBJECT public: static bool waitForSignal(QObject *receiver, const char *member, int timeout = 5000); static QList<QQmlDebugClient *> createOtherClients(QQmlDebugConnection *connection); static QString clientStateString(const QQmlDebugClient *client); static QString connectionStateString(const QQmlDebugConnection *connection); + +protected: + enum ConnectResult { + ConnectSuccess, + ProcessFailed, + SessionFailed, + ConnectionFailed, + ClientsFailed, + EnableFailed, + RestrictFailed + }; + + ConnectResult connect(const QString &executable, const QString &services, + const QString &extraArgs, bool block); + + virtual QQmlDebugProcess *createProcess(const QString &executable); + virtual QQmlDebugConnection *createConnection(); + virtual QList<QQmlDebugClient *> createClients(); + + QQmlDebugProcess *m_process = nullptr; + QQmlDebugConnection *m_connection = nullptr; + QList<QQmlDebugClient *> m_clients; + +protected slots: + virtual void cleanup(); }; class QQmlDebugTestClient : public QQmlDebugClient @@ -69,62 +88,15 @@ public: QByteArray waitForResponse(); signals: - void stateHasChanged(); void serverMessage(const QByteArray &); protected: - virtual void stateChanged(State state); virtual void messageReceived(const QByteArray &ba); private: QByteArray lastMsg; }; -class QQmlDebugProcess : public QObject -{ - Q_OBJECT -public: - QQmlDebugProcess(const QString &executable, QObject *parent = 0); - ~QQmlDebugProcess(); - - QString state(); - - void addEnvironment(const QString &environment); - - void start(const QStringList &arguments); - bool waitForSessionStart(); - int debugPort() const; - - bool waitForFinished(); - QProcess::ExitStatus exitStatus() const; - - QString output() const; - void stop(); - void setMaximumBindErrors(int numErrors); - -signals: - void readyReadStandardOutput(); - -private slots: - void timeout(); - void processAppOutput(); - void processError(QProcess::ProcessError error); - -private: - QString m_executable; - QProcess m_process; - QString m_outputBuffer; - QString m_output; - QTimer m_timer; - QEventLoop m_eventLoop; - QMutex m_mutex; - bool m_started; - QStringList m_environment; - int m_port; - int m_maximumBindErrors; - int m_receivedBindErrors; -}; - class QQmlInspectorResultRecipient : public QObject { Q_OBJECT @@ -142,4 +114,33 @@ public: } }; +class ClientStateHandler : public QObject +{ + Q_OBJECT +public: + ClientStateHandler(const QList<QQmlDebugClient *> &clients, + const QList<QQmlDebugClient *> &others, + QQmlDebugClient::State expectedOthers); + + ~ClientStateHandler(); + + bool allEnabled() const { return m_allEnabled; } + bool othersAsExpected() const { return m_othersAsExpected; } + +signals: + void allOk(); + +private: + void checkStates(); + + const QList<QQmlDebugClient *> m_clients; + const QList<QQmlDebugClient *> m_others; + const QQmlDebugClient::State m_expectedOthers; + + bool m_allEnabled = false; + bool m_othersAsExpected = false; +}; + +QString debugJsServerPath(const QString &selfPath); + #endif // DEBUGUTIL_P_H diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp new file mode 100644 index 0000000000..956e97e7ba --- /dev/null +++ b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qqmldebugprocess_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> + +QQmlDebugProcess::QQmlDebugProcess(const QString &executable, QObject *parent) + : QObject(parent) + , m_executable(executable) + , m_state(SessionUnknown) + , m_port(0) + , m_maximumBindErrors(0) + , m_receivedBindErrors(0) +{ + m_process.setProcessChannelMode(QProcess::MergedChannels); + m_timer.setInterval(15000); + connect(&m_process, &QProcess::readyReadStandardOutput, + this, &QQmlDebugProcess::processAppOutput); + connect(&m_process, &QProcess::errorOccurred, + this, &QQmlDebugProcess::processError); + connect(&m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), + this, [this]() { + m_timer.stop(); + m_eventLoop.quit(); + emit finished(); + }); + connect(&m_timer, &QTimer::timeout, + this, &QQmlDebugProcess::timeout); +} + +QQmlDebugProcess::~QQmlDebugProcess() +{ + stop(); +} + +QString QQmlDebugProcess::stateString() const +{ + QString stateStr; + switch (m_process.state()) { + case QProcess::NotRunning: { + stateStr = "not running"; + if (m_process.exitStatus() == QProcess::CrashExit) + stateStr += " (crashed!)"; + else + stateStr += ", return value " + QString::number(m_process.exitCode()); + break; + } + case QProcess::Starting: + stateStr = "starting"; + break; + case QProcess::Running: + stateStr = "running"; + break; + } + return stateStr; +} + +void QQmlDebugProcess::start(const QStringList &arguments) +{ +#ifdef Q_OS_MAC + // make sure m_executable points to the actual binary even if it's inside an app bundle + QFileInfo binFile(m_executable); + if (!binFile.isExecutable()) { + QDir bundleDir(m_executable + ".app"); + if (bundleDir.exists()) { + m_executable = bundleDir.absoluteFilePath("Contents/MacOS/" + binFile.baseName()); + //qDebug() << Q_FUNC_INFO << "found bundled binary" << m_executable; + } + } +#endif + m_mutex.lock(); + m_port = 0; + m_process.setEnvironment(QProcess::systemEnvironment() + m_environment); + m_process.start(m_executable, arguments); + if (!m_process.waitForStarted()) { + qWarning() << "QML Debug Client: Could not launch app " << m_executable + << ": " << m_process.errorString(); + m_eventLoop.quit(); + } + m_mutex.unlock(); +} + +void QQmlDebugProcess::stop() +{ + if (m_process.state() != QProcess::NotRunning) { + disconnect(&m_process, &QProcess::errorOccurred, this, &QQmlDebugProcess::processError); + m_process.kill(); + m_process.waitForFinished(5000); + } +} + +void QQmlDebugProcess::setMaximumBindErrors(int ignore) +{ + m_maximumBindErrors = ignore; +} + +void QQmlDebugProcess::timeout() +{ + qWarning() << "Timeout while waiting for QML debugging messages " + "in application output. Process is in state" << m_process.state() + << ", Output:" << m_output << "."; +} + +bool QQmlDebugProcess::waitForSessionStart() +{ + if (m_process.state() != QProcess::Running) { + qWarning() << "Could not start up " << m_executable; + return false; + } else if (m_state == SessionStarted) { + return true; + } else if (m_state == SessionFailed) { + return false; + } + + m_timer.start(); + m_eventLoop.exec(); + + return m_state == SessionStarted; +} + +int QQmlDebugProcess::debugPort() const +{ + return m_port; +} + +bool QQmlDebugProcess::waitForFinished() +{ + return m_process.waitForFinished(); +} + +QProcess::ProcessState QQmlDebugProcess::state() const +{ + return m_process.state(); +} + +QProcess::ExitStatus QQmlDebugProcess::exitStatus() const +{ + return m_process.exitStatus(); +} + +void QQmlDebugProcess::addEnvironment(const QString &environment) +{ + m_environment.append(environment); +} + +QString QQmlDebugProcess::output() const +{ + return m_output; +} + +void QQmlDebugProcess::processAppOutput() +{ + m_mutex.lock(); + + bool outputFromAppItself = false; + + QString newOutput = m_process.readAll(); + m_output.append(newOutput); + m_outputBuffer.append(newOutput); + + while (true) { + const int nlIndex = m_outputBuffer.indexOf(QLatin1Char('\n')); + if (nlIndex < 0) // no further complete lines + break; + const QString line = m_outputBuffer.left(nlIndex); + m_outputBuffer = m_outputBuffer.right(m_outputBuffer.size() - nlIndex - 1); + + if (line.contains("QML Debugger:")) { + const QRegExp portRx("Waiting for connection on port (\\d+)"); + if (portRx.indexIn(line) != -1) { + m_port = portRx.cap(1).toInt(); + m_timer.stop(); + m_state = SessionStarted; + m_eventLoop.quit(); + continue; + } + if (line.contains("Unable to listen")) { + if (++m_receivedBindErrors >= m_maximumBindErrors) { + if (m_maximumBindErrors == 0) + qWarning() << "App was unable to bind to port!"; + m_timer.stop(); + m_state = SessionFailed; + m_eventLoop.quit(); + } + continue; + } + } + + // set to true if there is output not coming from the debugger or we don't understand it + outputFromAppItself = true; + } + m_mutex.unlock(); + + if (outputFromAppItself) + emit readyReadStandardOutput(); +} + +void QQmlDebugProcess::processError(QProcess::ProcessError error) +{ + qDebug() << "An error occurred while waiting for debug process to become available:"; + switch (error) { + case QProcess::FailedToStart: + qDebug() << "Process failed to start."; + break; + case QProcess::Crashed: + qDebug() << "Process crashed."; + break; + case QProcess::Timedout: + qDebug() << "Process timed out."; + break; + case QProcess::WriteError: + qDebug() << "Error while writing to process."; + break; + case QProcess::ReadError: + qDebug() << "Error while reading from process."; + break; + case QProcess::UnknownError: + qDebug() << "Unknown process error."; + break; + } +} diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h new file mode 100644 index 0000000000..945cc58c85 --- /dev/null +++ b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QQMLDEBUGPROCESS_P_H +#define QQMLDEBUGPROCESS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qprocess.h> +#include <QtCore/qtimer.h> +#include <QtCore/qeventloop.h> +#include <QtCore/qmutex.h> + +class QQmlDebugProcess : public QObject +{ + Q_OBJECT +public: + QQmlDebugProcess(const QString &executable, QObject *parent = 0); + ~QQmlDebugProcess(); + + QString stateString() const; + + void addEnvironment(const QString &environment); + + void start(const QStringList &arguments); + bool waitForSessionStart(); + int debugPort() const; + + bool waitForFinished(); + QProcess::ProcessState state() const; + QProcess::ExitStatus exitStatus() const; + + QString output() const; + void stop(); + void setMaximumBindErrors(int numErrors); + +signals: + void readyReadStandardOutput(); + void finished(); + +private slots: + void timeout(); + void processAppOutput(); + void processError(QProcess::ProcessError error); + +private: + enum SessionState { + SessionUnknown, + SessionStarted, + SessionFailed + }; + + QString m_executable; + QProcess m_process; + QString m_outputBuffer; + QString m_output; + QTimer m_timer; + QEventLoop m_eventLoop; + QMutex m_mutex; + SessionState m_state; + QStringList m_environment; + int m_port; + int m_maximumBindErrors; + int m_receivedBindErrors; +}; + +#endif // QQMLDEBUGPROCESS_P_H diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp index 4dce07d824..896ed608fd 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp +++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp @@ -48,5 +48,4 @@ void QQmlDebugTestService::stateAboutToBeChanged(QQmlDebugService::State) void QQmlDebugTestService::stateChanged(State) { Q_ASSERT(QThread::currentThread() != thread()); - emit stateHasChanged(); } diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h index 37b4a9f98c..9c39c0893d 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h +++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h @@ -38,9 +38,6 @@ class QQmlDebugTestService : public QQmlDebugService public: QQmlDebugTestService(const QString &s, float version = 1, QObject *parent = 0); -signals: - void stateHasChanged(); - protected: virtual void messageReceived(const QByteArray &ba); virtual void stateAboutToBeChanged(State state); diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp deleted file mode 100644 index c0252a0290..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module 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 "qqmlenginedebugclient.h" -#include <private/qqmldebugconnection_p.h> - -struct QmlObjectData { - QUrl url; - int lineNumber; - int columnNumber; - QString idString; - QString objectName; - QString objectType; - int objectId; - int contextId; - int parentId; -}; - -QPacket &operator>>(QPacket &ds, QmlObjectData &data) -{ - ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString - >> data.objectName >> data.objectType >> data.objectId >> data.contextId - >> data.parentId; - return ds; -} - -struct QmlObjectProperty { - enum Type { Unknown, Basic, Object, List, SignalProperty }; - Type type; - QString name; - QVariant value; - QString valueTypeName; - QString binding; - bool hasNotifySignal; -}; - -QPacket &operator>>(QPacket &ds, QmlObjectProperty &data) -{ - int type; - ds >> type >> data.name >> data.value >> data.valueTypeName - >> data.binding >> data.hasNotifySignal; - data.type = (QmlObjectProperty::Type)type; - return ds; -} - -QQmlEngineDebugClient::QQmlEngineDebugClient( - QQmlDebugConnection *connection) - : QQmlDebugClient(QLatin1String("QmlDebugger"), connection), - m_nextId(0), - m_valid(false) -{ -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugPropertyReference &property, bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("WATCH_PROPERTY") << id << property.objectDebugId - << property.name.toUtf8(); - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugContextReference &, const QString &, bool *success) -{ - *success = false; - qWarning("QQmlEngineDebugClient::addWatch(): Not implemented"); - return 0; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugObjectReference &object, const QString &expr, - bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("WATCH_EXPR_OBJECT") << id << object.debugId << expr; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugObjectReference &object, bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("WATCH_OBJECT") << id << object.debugId; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugFileReference &, bool *success) -{ - *success = false; - qWarning("QQmlEngineDebugClient::addWatch(): Not implemented"); - return 0; -} - -void QQmlEngineDebugClient::removeWatch(quint32 id, bool *success) -{ - *success = false; - if (state() == QQmlDebugClient::Enabled) { - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("NO_WATCH") << id; - sendMessage(ds.data()); - *success = true; - } -} - -quint32 QQmlEngineDebugClient::queryAvailableEngines(bool *success) -{ - m_engines.clear(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("LIST_ENGINES") << id; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryRootContexts( - const QmlDebugEngineReference &engine, bool *success) -{ - m_rootContext = QmlDebugContextReference(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && engine.debugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("LIST_OBJECTS") << id << engine.debugId; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObject( - const QmlDebugObjectReference &object, bool *success) -{ - m_object = QmlDebugObjectReference(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && object.debugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECT") << id << object.debugId << false << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObjectsForLocation( - const QString &file, int lineNumber, int columnNumber, bool *success) -{ - m_objects.clear(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << file << lineNumber - << columnNumber << false << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObjectRecursive( - const QmlDebugObjectReference &object, bool *success) -{ - m_object = QmlDebugObjectReference(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && object.debugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECT") << id << object.debugId << true << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObjectsForLocationRecursive(const QString &file, - int lineNumber, int columnNumber, bool *success) -{ - m_objects.clear(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << file << lineNumber - << columnNumber << true << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryExpressionResult( - int objectDebugId, const QString &expr, bool *success) -{ - m_exprResult = QVariant(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr - << engines()[0].debugId; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryExpressionResultBC( - int objectDebugId, const QString &expr, bool *success) -{ - m_exprResult = QVariant(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::setBindingForObject( - int objectDebugId, - const QString &propertyName, - const QVariant &bindingExpression, - bool isLiteralValue, - QString source, int line, - bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("SET_BINDING") << id << objectDebugId << propertyName - << bindingExpression << isLiteralValue << source << line; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::resetBindingForObject( - int objectDebugId, - const QString &propertyName, - bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("RESET_BINDING") << id << objectDebugId << propertyName; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::setMethodBody( - int objectDebugId, const QString &methodName, - const QString &methodBody, bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("SET_METHOD_BODY") << id << objectDebugId - << methodName << methodBody; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -void QQmlEngineDebugClient::decode(QPacket &ds, - QmlDebugObjectReference &o, - bool simple) -{ - QmlObjectData data; - ds >> data; - o.debugId = data.objectId; - o.className = data.objectType; - o.idString = data.idString; - o.name = data.objectName; - o.source.url = data.url; - o.source.lineNumber = data.lineNumber; - o.source.columnNumber = data.columnNumber; - o.contextDebugId = data.contextId; - - if (simple) - return; - - int childCount; - bool recur; - ds >> childCount >> recur; - - for (int ii = 0; ii < childCount; ++ii) { - o.children.append(QmlDebugObjectReference()); - decode(ds, o.children.last(), !recur); - } - - int propCount; - ds >> propCount; - - for (int ii = 0; ii < propCount; ++ii) { - QmlObjectProperty data; - ds >> data; - QmlDebugPropertyReference prop; - prop.objectDebugId = o.debugId; - prop.name = data.name; - prop.binding = data.binding; - prop.hasNotifySignal = data.hasNotifySignal; - prop.valueTypeName = data.valueTypeName; - switch (data.type) { - case QmlObjectProperty::Basic: - case QmlObjectProperty::List: - case QmlObjectProperty::SignalProperty: - { - prop.value = data.value; - break; - } - case QmlObjectProperty::Object: - { - QmlDebugObjectReference obj; - obj.debugId = prop.value.toInt(); - obj.className = prop.valueTypeName; - prop.value = qVariantFromValue(obj); - break; - } - case QmlObjectProperty::Unknown: - break; - } - o.properties << prop; - } -} - -void QQmlEngineDebugClient::decode(QPacket &ds, - QList<QmlDebugObjectReference> &o, - bool simple) -{ - int count; - ds >> count; - for (int i = 0; i < count; i++) { - QmlDebugObjectReference obj; - decode(ds, obj, simple); - o << obj; - } -} - -void QQmlEngineDebugClient::decode(QPacket &ds, - QmlDebugContextReference &c) -{ - ds >> c.name >> c.debugId; - - int contextCount; - ds >> contextCount; - - for (int ii = 0; ii < contextCount; ++ii) { - c.contexts.append(QmlDebugContextReference()); - decode(ds, c.contexts.last()); - } - - int objectCount; - ds >> objectCount; - - for (int ii = 0; ii < objectCount; ++ii) { - QmlDebugObjectReference obj; - decode(ds, obj, true); - - obj.contextDebugId = c.debugId; - c.objects << obj; - } -} - -void QQmlEngineDebugClient::messageReceived(const QByteArray &data) -{ - m_valid = false; - QPacket ds(connection()->currentDataStreamVersion(), data); - - int queryId; - QByteArray type; - ds >> type >> queryId; - - //qDebug() << "QQmlEngineDebugPrivate::message()" << type; - - if (type == "LIST_ENGINES_R") { - int count; - ds >> count; - - m_engines.clear(); - for (int ii = 0; ii < count; ++ii) { - QmlDebugEngineReference eng; - ds >> eng.name; - ds >> eng.debugId; - m_engines << eng; - } - } else if (type == "LIST_OBJECTS_R") { - if (!ds.atEnd()) - decode(ds, m_rootContext); - - } else if (type == "FETCH_OBJECT_R") { - if (!ds.atEnd()) - decode(ds, m_object, false); - - } else if (type == "FETCH_OBJECTS_FOR_LOCATION_R") { - if (!ds.atEnd()) - decode(ds, m_objects, false); - - } else if (type == "EVAL_EXPRESSION_R") {; - ds >> m_exprResult; - - } else if (type == "WATCH_PROPERTY_R") { - ds >> m_valid; - - } else if (type == "WATCH_OBJECT_R") { - ds >> m_valid; - - } else if (type == "WATCH_EXPR_OBJECT_R") { - ds >> m_valid; - - } else if (type == "UPDATE_WATCH") { - int debugId; - QByteArray name; - QVariant value; - ds >> debugId >> name >> value; - emit valueChanged(name, value); - return; - - } else if (type == "OBJECT_CREATED") { - int engineId, objectId, parentId; - ds >> engineId >> objectId >> parentId; - emit newObject(objectId); - return; - } else if (type == "SET_BINDING_R") { - ds >> m_valid; - } else if (type == "RESET_BINDING_R") { - ds >> m_valid; - } else if (type == "SET_METHOD_BODY_R") { - ds >> m_valid; - } else if (type == "NO_WATCH_R") { - ds >> m_valid; - } - emit result(); -} - diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h deleted file mode 100644 index 5d74f2d43c..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h +++ /dev/null @@ -1,233 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module 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 QQMLENGINEDEBUGCLIENT_H -#define QQMLENGINEDEBUGCLIENT_H - -#include <private/qqmldebugclient_p.h> -#include <private/qpacket_p.h> - -#include <QtCore/qurl.h> -#include <QtCore/qvariant.h> - -struct QmlDebugPropertyReference -{ - QmlDebugPropertyReference() - : objectDebugId(-1), hasNotifySignal(false) - { - } - - QmlDebugPropertyReference &operator=( - const QmlDebugPropertyReference &o) - { - objectDebugId = o.objectDebugId; name = o.name; value = o.value; - valueTypeName = o.valueTypeName; binding = o.binding; - hasNotifySignal = o.hasNotifySignal; - return *this; - } - - int objectDebugId; - QString name; - QVariant value; - QString valueTypeName; - QString binding; - bool hasNotifySignal; -}; - -struct QmlDebugFileReference -{ - QmlDebugFileReference() - : lineNumber(-1), columnNumber(-1) - { - } - - QmlDebugFileReference &operator=( - const QmlDebugFileReference &o) - { - url = o.url; lineNumber = o.lineNumber; columnNumber = o.columnNumber; - return *this; - } - - QUrl url; - int lineNumber; - int columnNumber; -}; - -struct QmlDebugObjectReference -{ - QmlDebugObjectReference() - : debugId(-1), contextDebugId(-1) - { - } - - QmlDebugObjectReference(int id) - : debugId(id), contextDebugId(-1) - { - } - - QmlDebugObjectReference &operator=( - const QmlDebugObjectReference &o) - { - debugId = o.debugId; className = o.className; idString = o.idString; - name = o.name; source = o.source; contextDebugId = o.contextDebugId; - properties = o.properties; children = o.children; - return *this; - } - int debugId; - QString className; - QString idString; - QString name; - QmlDebugFileReference source; - int contextDebugId; - QList<QmlDebugPropertyReference> properties; - QList<QmlDebugObjectReference> children; -}; - -Q_DECLARE_METATYPE(QmlDebugObjectReference) - -struct QmlDebugContextReference -{ - QmlDebugContextReference() - : debugId(-1) - { - } - - QmlDebugContextReference &operator=( - const QmlDebugContextReference &o) - { - debugId = o.debugId; name = o.name; objects = o.objects; - contexts = o.contexts; - return *this; - } - - int debugId; - QString name; - QList<QmlDebugObjectReference> objects; - QList<QmlDebugContextReference> contexts; -}; - -struct QmlDebugEngineReference -{ - QmlDebugEngineReference() - : debugId(-1) - { - } - - QmlDebugEngineReference(int id) - : debugId(id) - { - } - - QmlDebugEngineReference &operator=( - const QmlDebugEngineReference &o) - { - debugId = o.debugId; name = o.name; - return *this; - } - - int debugId; - QString name; -}; - -class QQmlEngineDebugClient : public QQmlDebugClient -{ - Q_OBJECT -public: - explicit QQmlEngineDebugClient(QQmlDebugConnection *conn); - - quint32 addWatch(const QmlDebugPropertyReference &, - bool *success); - quint32 addWatch(const QmlDebugContextReference &, const QString &, - bool *success); - quint32 addWatch(const QmlDebugObjectReference &, const QString &, - bool *success); - quint32 addWatch(const QmlDebugObjectReference &, - bool *success); - quint32 addWatch(const QmlDebugFileReference &, - bool *success); - - void removeWatch(quint32 watch, bool *success); - - quint32 queryAvailableEngines(bool *success); - quint32 queryRootContexts(const QmlDebugEngineReference &, - bool *success); - quint32 queryObject(const QmlDebugObjectReference &, - bool *success); - quint32 queryObjectsForLocation(const QString &file, - int lineNumber, int columnNumber, bool *success); - quint32 queryObjectRecursive(const QmlDebugObjectReference &, - bool *success); - quint32 queryObjectsForLocationRecursive(const QString &file, - int lineNumber, int columnNumber, bool *success); - quint32 queryExpressionResult(int objectDebugId, - const QString &expr, - bool *success); - quint32 queryExpressionResultBC(int objectDebugId, - const QString &expr, - bool *success); - quint32 setBindingForObject(int objectDebugId, const QString &propertyName, - const QVariant &bindingExpression, - bool isLiteralValue, - QString source, int line, bool *success); - quint32 resetBindingForObject(int objectDebugId, - const QString &propertyName, bool *success); - quint32 setMethodBody(int objectDebugId, const QString &methodName, - const QString &methodBody, bool *success); - - quint32 getId() { return m_nextId++; } - - void decode(QPacket &ds, QmlDebugContextReference &); - void decode(QPacket &ds, QmlDebugObjectReference &, bool simple); - void decode(QPacket &ds, QList<QmlDebugObjectReference> &o, bool simple); - - QList<QmlDebugEngineReference> engines() { return m_engines; } - QmlDebugContextReference rootContext() { return m_rootContext; } - QmlDebugObjectReference object() { return m_object; } - QList<QmlDebugObjectReference> objects() { return m_objects; } - QVariant resultExpr() { return m_exprResult; } - bool valid() { return m_valid; } - -signals: - void newObject(int objectId); - void valueChanged(QByteArray,QVariant); - void result(); - -protected: - void messageReceived(const QByteArray &); - -private: - quint32 m_nextId; - bool m_valid; - QList<QmlDebugEngineReference> m_engines; - QmlDebugContextReference m_rootContext; - QmlDebugObjectReference m_object; - QList<QmlDebugObjectReference> m_objects; - QVariant m_exprResult; -}; - -#endif // QQMLENGINEDEBUGCLIENT_H diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.pri b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.pri deleted file mode 100644 index a969b4f153..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.pri +++ /dev/null @@ -1,3 +0,0 @@ -HEADERS += $$PWD/qqmlenginedebugclient.h - -SOURCES += $$PWD/qqmlenginedebugclient.cpp diff --git a/tests/auto/qml/debugger/shared/qqmlinspectorclient.cpp b/tests/auto/qml/debugger/shared/qqmlinspectorclient.cpp deleted file mode 100644 index 20faef177e..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlinspectorclient.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlinspectorclient.h" - -#include <private/qpacket_p.h> -#include <private/qqmldebugconnection_p.h> -#include <QtCore/qdebug.h> - -QQmlInspectorClient::QQmlInspectorClient(QQmlDebugConnection *connection) : - QQmlDebugClient(QLatin1String("QmlInspector"), connection), - m_lastRequestId(-1) -{ -} - -int QQmlInspectorClient::setInspectToolEnabled(bool enabled) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray(enabled ? "enable" : "disable"); - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::setShowAppOnTop(bool showOnTop) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("showAppOnTop") << showOnTop; - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::setAnimationSpeed(qreal speed) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("setAnimationSpeed") << speed; - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::select(const QList<int> &objectIds) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("select") << objectIds; - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::createObject(const QString &qml, int parentId, const QStringList &imports, - const QString &filename) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("createObject") << qml << parentId << imports << filename; - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::moveObject(int childId, int newParentId) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("moveObject") << childId << newParentId; - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::destroyObject(int objectId) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("destroyObject") << objectId; - sendMessage(ds.data()); - return m_lastRequestId; -} - -void QQmlInspectorClient::messageReceived(const QByteArray &message) -{ - QPacket ds(connection()->currentDataStreamVersion(), message); - QByteArray type; - ds >> type; - - if (type != QByteArray("response")) { - qDebug() << "Unhandled message of type" << type; - return; - } - - int responseId; - bool result; - ds >> responseId >> result; - emit responseReceived(responseId, result); -} diff --git a/tests/auto/qml/debugger/shared/qqmlinspectorclient.pri b/tests/auto/qml/debugger/shared/qqmlinspectorclient.pri deleted file mode 100644 index c136e1313a..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlinspectorclient.pri +++ /dev/null @@ -1,3 +0,0 @@ -HEADERS += $$PWD/qqmlinspectorclient.h - -SOURCES += $$PWD/qqmlinspectorclient.cpp diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 27498de473..0e0d70845b 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -1,178 +1,720 @@ -# wrong tests -# uses octal number -15.2.3.6-2-17-1 failing +# ----- These are tests we will not fix -# these fail after the update to Unicode 6.3. -# the reason is that u+180e changed type from whitespace to control -S9.3.1_A2 -S9.3.1_A3_T1 -S9.3.1_A3_T2 -S15.1.2.2_A2_T10 -S15.1.2.3_A2_T10 -15.5.4.20-3-2 -15.5.4.20-3-3 -15.5.4.20-3-4 -15.5.4.20-3-5 -15.5.4.20-3-6 +# The tests below rely on the ES6 spec quirk that allows 'let' as an identifier. We've +# always treated 'let' as a reserved keyword (without ever getting a bug report about it), +# so we'll keep it that way. It also removes a huge headache in the parser. +language/statements/for-in/let-block-with-newline.js sloppyFails +language/statements/for-of/let-block-with-newline.js sloppyFails +language/statements/for/let-block-with-newline.js sloppyFails +language/statements/if/let-block-with-newline.js sloppyFails +language/statements/labeled/let-block-with-newline.js sloppyFails +language/statements/while/let-block-with-newline.js sloppyFails +language/statements/with/let-block-with-newline.js sloppyFails +language/statements/for-in/let-identifier-with-newline.js sloppyFails +language/statements/for-of/let-identifier-with-newline.js sloppyFails +language/statements/for/let-identifier-with-newline.js sloppyFails +language/statements/if/let-identifier-with-newline.js sloppyFails +language/statements/labeled/let-identifier-with-newline.js sloppyFails +language/statements/while/let-identifier-with-newline.js sloppyFails +language/statements/with/let-identifier-with-newline.js sloppyFails -10.4.3-1-106 failing -11.2.3-3_3 failing -S13_A15_T4 failing -S15.4.4.3_A1_T1 failing -S15.4.4.3_A3_T1 failing -S15.5.4.11_A5_T1 failing -S15.2.4.4_A14 failing +# The ES6/7 spec says that [[DefineOwnProperty]] on the module namespace exotic object +# always returns false. This was changed in https://github.com/tc39/ecma262/pull/858 +# but it's not in the published spec yet. +language/module-code/namespace/internals/define-own-property.js strictFails +# This test includes the above and therefore also fails. +language/module-code/namespace/internals/set.js strictFails -# Function declarations in conditionals. We allow them, because the real -# world requires them. -Sbp_12.5_A9_T3 failing -Sbp_12.6.1_A13_T3 failing -Sbp_12.6.2_A13_T3 failing -Sbp_12.6.4_A13_T3 failing - -# es6: function length attributes are configurable, wasn't in es5 -S15.1.2.2_A9.2 failing -S15.1.3.1_A5.2 failing -S15.1.3.2_A5.2 failing -S15.1.3.3_A5.2 failing -S15.1.2.3_A7.2 failing -S15.1.2.4_A2.2 failing -S15.1.2.5_A2.2 failing -S15.1.3.4_A5.2 failing -15.2.3.3-4-186 failing -S15.2.4.2_A9 failing -S15.2.4.3_A9 failing -S15.2.4.4_A9 failing -S15.2.4.5_A9 failing -S15.2.4.6_A9 failing -S15.2.4.7_A9 failing -15.3.3.2-1 failing -15.4.4.2_A4.2 -S15.3.4.2_A9 failing -S15.3.4.3_A9 failing -S15.3.4.4_A9 failing -15.3.4.5-15-2 failing -S15.4.4.2_A4.2 failing -S15.4.4.3_A4.2 failing -S15.4.4.4_A4.2 failing -S15.4.4.5_A6.2 failing -S15.4.4.6_A5.2 failing -S15.4.4.7_A6.2 failing -S15.4.4.8_A5.2 failing -S15.4.4.9_A5.2 failing -S15.4.4.10_A5.2 failing -S15.4.4.11_A7.2 failing -S15.4.4.12_A5.2 failing -S15.4.4.13_A5.2 failing -S15.5.4.10_A9 failing -S15.5.4.11_A9 failing -S15.5.4.12_A9 failing -S15.5.4.13_A9 failing -S15.5.4.14_A9 failing -S15.5.4.15_A9 failing -S15.5.4.16_A9 failing -S15.5.4.17_A9 failing -S15.5.4.18_A9 failing -S15.5.4.19_A9 failing -S15.5.4.4_A9 failing -S15.5.4.5_A9 failing -S15.5.4.6_A9 failing -S15.5.4.7_A9 failing -S15.5.4.8_A9 failing -S15.5.4.9_A9 failing -S15.9.4.2_A3_T2 failing -S15.9.4.3_A3_T2 failing -S15.9.5.2_A3_T2 failing -S15.9.5.3_A3_T2 failing -S15.9.5.4_A3_T2 failing -S15.9.5.5_A3_T2 failing -S15.9.5.1_A3_T2 failing -S15.9.5.10_A3_T2 failing -S15.9.5.11_A3_T2 failing -S15.9.5.12_A3_T2 failing -S15.9.5.13_A3_T2 failing -S15.9.5.14_A3_T2 failing -S15.9.5.15_A3_T2 failing -S15.9.5.16_A3_T2 failing -S15.9.5.17_A3_T2 failing -S15.9.5.18_A3_T2 failing -S15.9.5.19_A3_T2 failing -S15.9.5.20_A3_T2 failing -S15.9.5.21_A3_T2 failing -S15.9.5.22_A3_T2 failing -S15.9.5.23_A3_T2 failing -S15.9.5.24_A3_T2 failing -S15.9.5.25_A3_T2 failing -S15.9.5.26_A3_T2 failing -S15.9.5.27_A3_T2 failing -S15.9.5.28_A3_T2 failing -S15.9.5.29_A3_T2 failing -S15.9.5.30_A3_T2 failing -S15.9.5.31_A3_T2 failing -S15.9.5.32_A3_T2 failing -S15.9.5.33_A3_T2 failing -S15.9.5.34_A3_T2 failing -S15.9.5.35_A3_T2 failing -S15.9.5.36_A3_T2 failing -S15.9.5.37_A3_T2 failing -S15.9.5.38_A3_T2 failing -S15.9.5.39_A3_T2 failing -S15.9.5.40_A3_T2 failing -S15.9.5.41_A3_T2 failing -S15.9.5.42_A3_T2 failing -S15.9.5.6_A3_T2 failing -S15.9.5.7_A3_T2 failing -S15.9.5.8_A3_T2 failing -S15.9.5.9_A3_T2 failing -S15.10.6.2_A9 failing -S15.10.6.3_A9 failing -S15.10.6.4_A9 failing - -# es6: Object.freeze(v) on a non-object returns v, no longer TypeError -15.2.3.9-1 failing -15.2.3.9-1-1 failing -15.2.3.9-1-2 failing -15.2.3.9-1-3 failing -15.2.3.9-1-4 failing -# es6: Object.preventExtensions(O) on a non-object, no longer TypeError -15.2.3.10-1 failing -15.2.3.10-1-3 failing -15.2.3.10-1-4 failing -# es6: Object.isSealed(O) on a non-object, no longer TypeError -15.2.3.11-1 -# es6: Object.isFrozen(O) on a non-object, no longer TypeError -15.2.3.12-1 -15.2.3.12-1-3 -15.2.3.12-1-4 -# es6: Object.isExtensible(O) on a non-object, no longer TypeError -15.2.3.13-1 -15.2.3.13-1-3 -15.2.3.13-1-4 -# es6: Object.keys(O) on a non-object, no longer TypeError -15.2.3.14-1-1 -15.2.3.14-1-2 -15.2.3.14-1-3 -15.2.3.14-1 -15.2.3.14-2 -15.2.3.14-3 -# es6: Object.getOwnPropertyDescriptor(O) on a non-object, no longer TypeError -15.2.3.3-1 -15.2.3.3-1-3 -15.2.3.3-1-4 -# es6: Object.getPrototypeOf(O) on a non-object, no longer TypeError -15.2.3.2-1 -15.2.3.2-1-3 -15.2.3.2-1-4 -# es6: Object.getOwnPropertyNames(O) on a non-object, no longer TypeError -15.2.3.4-1 -15.2.3.4-1-4 -15.2.3.4-1-5 -# es6: Object.seal(O) on a non-object, no longer TypeError -15.2.3.8-1 -15.2.3.8-1-1 -15.2.3.8-1-2 -15.2.3.8-1-3 -15.2.3.8-1-4 - -# es6: Date.prototype is no longer a DateObject -15.9.5.40_1 failing +# ----- test failures that should be fixed +built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js fails +built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments-with-dupes.js sloppyFails +built-ins/Array/prototype/concat/Array.prototype.concat_sloppy-arguments.js fails +built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js fails +built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-function.js fails +built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-reg-exp.js fails +built-ins/Array/prototype/concat/Array.prototype.concat_spreadable-sparse-object.js fails +built-ins/Array/prototype/concat/Array.prototype.concat_strict-arguments.js fails +built-ins/Array/prototype/concat/S15.4.4.4_A3_T1.js fails +built-ins/Array/prototype/concat/S15.4.4.4_A3_T2.js fails +built-ins/Array/prototype/concat/S15.4.4.4_A3_T3.js fails +built-ins/Array/prototype/concat/create-ctor-non-object.js fails +built-ins/Array/prototype/concat/create-ctor-poisoned.js fails +built-ins/Array/prototype/concat/create-proxy.js fails +built-ins/Array/prototype/concat/create-species-abrupt.js fails +built-ins/Array/prototype/concat/create-species-non-ctor.js fails +built-ins/Array/prototype/concat/create-species-poisoned.js fails +built-ins/Array/prototype/concat/create-species.js fails +built-ins/Array/prototype/concat/is-concat-spreadable-val-falsey.js fails +built-ins/Array/prototype/copyWithin/return-abrupt-from-has-start.js fails +built-ins/Array/prototype/every/15.4.4.16-3-29.js fails +built-ins/Array/prototype/filter/create-ctor-non-object.js fails +built-ins/Array/prototype/filter/create-ctor-poisoned.js fails +built-ins/Array/prototype/filter/create-proxy.js fails +built-ins/Array/prototype/filter/create-species-abrupt.js fails +built-ins/Array/prototype/filter/create-species-non-ctor.js fails +built-ins/Array/prototype/filter/create-species-poisoned.js fails +built-ins/Array/prototype/filter/create-species.js fails +built-ins/Array/prototype/includes/length-boundaries.js fails +built-ins/Array/prototype/indexOf/15.4.4.14-3-28.js fails +built-ins/Array/prototype/indexOf/15.4.4.14-3-29.js fails +built-ins/Array/prototype/join/S15.4.4.5_A4_T3.js fails +built-ins/Array/prototype/lastIndexOf/15.4.4.15-3-28.js fails +built-ins/Array/prototype/map/create-ctor-non-object.js fails +built-ins/Array/prototype/map/create-ctor-poisoned.js fails +built-ins/Array/prototype/map/create-proxy.js fails +built-ins/Array/prototype/map/create-species-abrupt.js fails +built-ins/Array/prototype/map/create-species-non-ctor.js fails +built-ins/Array/prototype/map/create-species-poisoned.js fails +built-ins/Array/prototype/map/create-species.js fails +built-ins/Array/prototype/pop/S15.4.4.6_A2_T2.js fails +built-ins/Array/prototype/pop/S15.4.4.6_A3_T1.js fails +built-ins/Array/prototype/pop/S15.4.4.6_A3_T2.js fails +built-ins/Array/prototype/pop/clamps-to-integer-limit.js fails +built-ins/Array/prototype/pop/length-near-integer-limit.js fails +built-ins/Array/prototype/push/S15.4.4.7_A2_T2.js fails +built-ins/Array/prototype/push/S15.4.4.7_A3.js fails +built-ins/Array/prototype/push/throws-if-integer-limit-exceeded.js fails +built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-object.js fails +built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js fails +built-ins/Array/prototype/slice/S15.4.4.10_A3_T1.js fails +built-ins/Array/prototype/slice/S15.4.4.10_A3_T2.js fails +built-ins/Array/prototype/slice/create-ctor-non-object.js fails +built-ins/Array/prototype/slice/create-ctor-poisoned.js fails +built-ins/Array/prototype/slice/create-non-array-invalid-len.js fails +built-ins/Array/prototype/slice/create-proxied-array-invalid-len.js fails +built-ins/Array/prototype/slice/create-proxy.js fails +built-ins/Array/prototype/slice/create-species-abrupt.js fails +built-ins/Array/prototype/slice/create-species-neg-zero.js fails +built-ins/Array/prototype/slice/create-species-non-ctor.js fails +built-ins/Array/prototype/slice/create-species-poisoned.js fails +built-ins/Array/prototype/slice/create-species.js fails +built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js fails +built-ins/Array/prototype/slice/length-exceeding-integer-limit.js fails +built-ins/Array/prototype/some/15.4.4.17-3-28.js fails +built-ins/Array/prototype/some/15.4.4.17-3-29.js fails +built-ins/Array/prototype/sort/comparefn-nonfunction-call-throws.js fails +built-ins/Array/prototype/splice/S15.4.4.12_A3_T1.js fails +built-ins/Array/prototype/splice/clamps-length-to-integer-limit.js fails +built-ins/Array/prototype/splice/create-ctor-non-object.js fails +built-ins/Array/prototype/splice/create-ctor-poisoned.js fails +built-ins/Array/prototype/splice/create-proxy.js fails +built-ins/Array/prototype/splice/create-species-abrupt.js fails +built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit.js fails +built-ins/Array/prototype/splice/create-species-neg-zero.js fails +built-ins/Array/prototype/splice/create-species-non-ctor.js fails +built-ins/Array/prototype/splice/create-species-poisoned.js fails +built-ins/Array/prototype/splice/create-species.js fails +built-ins/Array/prototype/splice/length-and-deleteCount-exceeding-integer-limit.js fails +built-ins/Array/prototype/splice/length-exceeding-integer-limit-shrink-array.js fails +built-ins/Array/prototype/splice/length-near-integer-limit-grow-array.js fails +built-ins/Array/prototype/toLocaleString/primitive_this_value.js strictFails +built-ins/Array/prototype/toLocaleString/primitive_this_value_getter.js strictFails +built-ins/Array/prototype/unshift/clamps-to-integer-limit.js fails +built-ins/Array/prototype/unshift/length-near-integer-limit.js fails +built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded.js fails +built-ins/ArrayBuffer/data-allocation-after-object-creation.js fails +built-ins/ArrayIteratorPrototype/next/detach-typedarray-in-progress.js fails +built-ins/AsyncFunction/AsyncFunction-construct.js fails +built-ins/AsyncFunction/AsyncFunction-is-extensible.js fails +built-ins/AsyncFunction/AsyncFunction-is-subclass.js fails +built-ins/AsyncFunction/AsyncFunction-length.js fails +built-ins/AsyncFunction/AsyncFunction-name.js fails +built-ins/AsyncFunction/AsyncFunction-prototype.js fails +built-ins/AsyncFunction/AsyncFunction.js fails +built-ins/AsyncFunction/AsyncFunctionPrototype-is-extensible.js fails +built-ins/AsyncFunction/AsyncFunctionPrototype-prototype.js fails +built-ins/AsyncFunction/AsyncFunctionPrototype-to-string.js fails +built-ins/AsyncFunction/instance-construct-throws.js fails +built-ins/AsyncFunction/instance-has-name.js fails +built-ins/AsyncFunction/instance-length.js fails +built-ins/AsyncFunction/instance-prototype-property.js fails +built-ins/AsyncGeneratorPrototype/next/name.js fails +built-ins/AsyncGeneratorPrototype/return/name.js fails +built-ins/AsyncGeneratorPrototype/throw/name.js fails +built-ins/Atomics/wait/did-timeout.js fails +built-ins/Atomics/wait/good-views.js fails +built-ins/Atomics/wait/nan-timeout.js fails +built-ins/Atomics/wait/negative-timeout.js fails +built-ins/Atomics/wait/no-spurious-wakeup.js fails +built-ins/Atomics/wait/was-woken.js fails +built-ins/Atomics/wake/counts.js fails +built-ins/Atomics/wake/wake-all-on-loc.js fails +built-ins/Atomics/wake/wake-all.js fails +built-ins/Atomics/wake/wake-in-order.js fails +built-ins/Atomics/wake/wake-nan.js fails +built-ins/Atomics/wake/wake-negative.js fails +built-ins/Atomics/wake/wake-one.js fails +built-ins/Atomics/wake/wake-two.js fails +built-ins/Atomics/wake/wake-zero.js fails +built-ins/DataView/custom-proto-access-throws.js fails +built-ins/DataView/custom-proto-if-object-is-used.js fails +built-ins/Date/prototype/toDateString/format.js fails +built-ins/Date/prototype/toDateString/invalid-date.js fails +built-ins/Date/prototype/toString/format.js fails +built-ins/Date/prototype/toTimeString/format.js fails +built-ins/Date/prototype/toTimeString/invalid-date.js fails +built-ins/Date/prototype/toUTCString/day-names.js fails +built-ins/Date/prototype/toUTCString/format.js fails +built-ins/Date/prototype/toUTCString/month-names.js fails +built-ins/Function/internals/Construct/base-ctor-revoked-proxy.js fails +built-ins/Function/prototype/Symbol.hasInstance/value-get-prototype-of-err.js fails +built-ins/Function/prototype/bind/BoundFunction_restricted-properties.js fails +built-ins/Function/prototype/bind/instance-name-chained.js fails +built-ins/Function/prototype/bind/instance-name-error.js fails +built-ins/Function/prototype/bind/instance-name-non-string.js fails +built-ins/Function/prototype/bind/instance-name.js fails +built-ins/Function/prototype/toString/AsyncFunction.js fails +built-ins/Function/prototype/toString/async-arrow-function.js fails +built-ins/Function/prototype/toString/async-function-declaration.js fails +built-ins/Function/prototype/toString/async-function-expression.js fails +built-ins/Function/prototype/toString/async-method-class-expression-static.js fails +built-ins/Function/prototype/toString/async-method-class-expression.js fails +built-ins/Function/prototype/toString/async-method-class-statement-static.js fails +built-ins/Function/prototype/toString/async-method-class-statement.js fails +built-ins/Function/prototype/toString/async-method-object.js fails +built-ins/Function/prototype/toString/method-computed-property-name.js fails +built-ins/JSON/parse/revived-proxy-revoked.js fails +built-ins/JSON/parse/revived-proxy.js fails +built-ins/JSON/parse/reviver-array-define-prop-err.js fails +built-ins/JSON/parse/reviver-array-delete-err.js fails +built-ins/JSON/parse/reviver-array-length-coerce-err.js fails +built-ins/JSON/parse/reviver-array-length-get-err.js fails +built-ins/JSON/parse/reviver-call-err.js fails +built-ins/JSON/parse/reviver-get-name-err.js fails +built-ins/JSON/parse/reviver-object-define-prop-err.js fails +built-ins/JSON/parse/reviver-object-delete-err.js fails +built-ins/JSON/parse/reviver-object-own-keys-err.js fails +built-ins/JSON/stringify/replacer-proxy-revoked.js fails +built-ins/JSON/stringify/replacer-proxy.js fails +built-ins/JSON/stringify/value-proxy.js fails +built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js fails +built-ins/Math/round/S15.8.2.15_A7.js fails +built-ins/Number/prototype/toPrecision/return-values.js fails +built-ins/Object/create/15.2.3.5-4-14.js strictFails +built-ins/Object/create/15.2.3.5-4-37.js strictFails +built-ins/Object/entries/getter-making-future-key-nonenumerable.js fails +built-ins/Object/entries/getter-removing-future-key.js fails +built-ins/Object/entries/observable-operations.js fails +built-ins/Object/getOwnPropertyDescriptors/proxy-undefined-descriptor.js fails +built-ins/Object/keys/proxy-keys.js fails +built-ins/Object/proto-from-ctor.js fails +built-ins/Object/prototype/toLocaleString/primitive_this_value_getter.js strictFails +built-ins/Object/prototype/toString/proxy-array.js fails +built-ins/Object/prototype/toString/proxy-function.js fails +built-ins/Object/prototype/valueOf/S15.2.4.4_A14.js fails +built-ins/Object/values/getter-adding-key.js fails +built-ins/Object/values/observable-operations.js fails +built-ins/Promise/prototype/catch/this-value-obj-coercible.js fails +built-ins/Promise/prototype/then/capability-executor-not-callable.js fails +built-ins/Promise/prototype/then/ctor-custom.js fails +built-ins/Promise/prototype/then/ctor-throws.js fails +built-ins/Promise/race/ctx-ctor.js fails +built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails +built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails +built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails +built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails +built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails +built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails +built-ins/RegExp/prototype/exec/success-lastindex-access.js fails +built-ins/RegExp/prototype/source/value-line-terminator.js fails +built-ins/RegExp/prototype/test/S15.10.6.3_A1_T22.js fails +built-ins/RegExp/unicode_restricted_brackets.js fails +built-ins/RegExp/unicode_restricted_character_class_escape.js fails +built-ins/RegExp/unicode_restricted_identity_escape.js fails +built-ins/RegExp/unicode_restricted_identity_escape_alpha.js fails +built-ins/RegExp/unicode_restricted_identity_escape_c.js fails +built-ins/RegExp/unicode_restricted_incomple_quantifier.js fails +built-ins/RegExp/unicode_restricted_octal_escape.js fails +built-ins/RegExp/unicode_restricted_quantifiable_assertion.js fails +built-ins/Set/prototype/forEach/iterates-values-revisits-after-delete-re-add.js fails +built-ins/SharedArrayBuffer/data-allocation-after-object-creation.js fails +built-ins/SharedArrayBuffer/prototype-from-newtarget.js fails +built-ins/String/prototype/endsWith/return-abrupt-from-searchstring-regexp-test.js fails +built-ins/String/prototype/includes/return-abrupt-from-searchstring-regexp-test.js fails +built-ins/String/prototype/indexOf/position-tointeger-toprimitive.js fails +built-ins/String/prototype/indexOf/position-tointeger.js fails +built-ins/String/prototype/indexOf/searchstring-tostring-toprimitive.js fails +built-ins/String/prototype/replace/cstm-replace-get-err.js fails +built-ins/String/prototype/replace/cstm-replace-invocation.js fails +built-ins/String/prototype/replace/this-value-not-obj-coercible.js fails +built-ins/String/prototype/search/cstm-search-get-err.js fails +built-ins/String/prototype/search/cstm-search-invocation.js fails +built-ins/String/prototype/search/invoke-builtin-search-searcher-undef.js fails +built-ins/String/prototype/search/invoke-builtin-search.js fails +built-ins/String/prototype/slice/this-value-not-obj-coercible.js fails +built-ins/String/prototype/split/cstm-split-get-err.js fails +built-ins/String/prototype/split/cstm-split-invocation.js fails +built-ins/String/prototype/startsWith/return-abrupt-from-searchstring-regexp-test.js fails +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 +built-ins/TypedArray/prototype/sort/arraylength-internal.js fails +built-ins/TypedArray/prototype/sort/comparefn-call-throws.js fails +built-ins/TypedArray/prototype/sort/comparefn-calls.js fails +built-ins/TypedArray/prototype/sort/detached-buffer-comparefn.js fails +built-ins/TypedArray/prototype/sort/invoked-as-func.js fails +built-ins/TypedArray/prototype/sort/invoked-as-method.js fails +built-ins/TypedArray/prototype/sort/length.js fails +built-ins/TypedArray/prototype/sort/name.js fails +built-ins/TypedArray/prototype/sort/prop-desc.js fails +built-ins/TypedArray/prototype/sort/return-same-instance.js fails +built-ins/TypedArray/prototype/sort/sortcompare-with-no-tostring.js fails +built-ins/TypedArray/prototype/sort/sorted-values-nan.js fails +built-ins/TypedArray/prototype/sort/sorted-values.js fails +built-ins/TypedArrays/ctors/buffer-arg/defined-negative-length.js fails +built-ins/TypedArrays/ctors/object-arg/as-generator-iterable-returns.js fails +built-ins/TypedArrays/ctors/object-arg/iterating-throws.js fails +built-ins/TypedArrays/ctors/object-arg/iterator-not-callable-throws.js fails +built-ins/TypedArrays/ctors/object-arg/iterator-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/detached-when-species-retrieved-different-type.js fails +built-ins/TypedArrays/ctors/typedarray-arg/detached-when-species-retrieved-same-type.js fails +built-ins/TypedArrays/ctors/typedarray-arg/other-ctor-buffer-ctor-access-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/other-ctor-buffer-ctor-custom-species.js fails +built-ins/TypedArrays/ctors/typedarray-arg/other-ctor-buffer-ctor-not-object-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/other-ctor-buffer-ctor-species-access-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/other-ctor-buffer-ctor-species-not-ctor-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/other-ctor-buffer-ctor-species-prototype-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-access-throws.js fails +built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-species-custom.js fails +built-ins/TypedArrays/ctors/typedarray-arg/same-ctor-buffer-ctor-species-not-ctor.js fails +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 +built-ins/TypedArrays/internals/Set/detached-buffer.js fails +built-ins/TypedArrays/internals/Set/tonumber-value-throws.js fails +built-ins/global/global-object.js fails +built-ins/global/property-descriptor.js fails +built-ins/isFinite/toprimitive-not-callable-throws.js fails +built-ins/isNaN/toprimitive-not-callable-throws.js fails +language/computed-property-names/class/static/method-number.js fails +language/computed-property-names/class/static/method-string.js fails +language/computed-property-names/class/static/method-symbol.js fails +language/eval-code/direct/new.target-arrow.js fails +language/eval-code/direct/new.target.js fails +language/eval-code/direct/non-definable-function-with-function.js sloppyFails +language/eval-code/direct/non-definable-function-with-variable.js sloppyFails +language/eval-code/direct/non-definable-global-function.js sloppyFails +language/eval-code/direct/non-definable-global-generator.js sloppyFails +language/eval-code/direct/super-call-arrow.js fails +language/eval-code/direct/super-call-fn.js fails +language/eval-code/direct/super-call-method.js fails +language/eval-code/direct/super-call.js fails +language/eval-code/direct/super-prop-arrow.js fails +language/eval-code/direct/super-prop-dot-no-home.js fails +language/eval-code/direct/super-prop-expr-no-home-no-eval.js fails +language/eval-code/direct/super-prop-expr-no-home.js fails +language/eval-code/direct/super-prop.js fails +language/eval-code/direct/this-value-func-strict-source.js sloppyFails +language/eval-code/direct/var-env-func-init-global-update-configurable.js sloppyFails +language/eval-code/direct/var-env-global-lex-non-strict.js sloppyFails +language/eval-code/direct/var-env-lower-lex-catch-non-strict.js sloppyFails +language/eval-code/direct/var-env-lower-lex-non-strict.js sloppyFails +language/eval-code/indirect/always-non-strict.js strictFails +language/eval-code/indirect/new.target.js fails +language/eval-code/indirect/non-definable-function-with-function.js sloppyFails +language/eval-code/indirect/non-definable-function-with-variable.js sloppyFails +language/eval-code/indirect/non-definable-global-function.js fails +language/eval-code/indirect/non-definable-global-generator.js fails +language/eval-code/indirect/non-definable-global-var.js strictFails +language/eval-code/indirect/super-call.js fails +language/eval-code/indirect/super-prop.js fails +language/eval-code/indirect/this-value-func.js strictFails +language/eval-code/indirect/var-env-func-init-global-new.js strictFails +language/eval-code/indirect/var-env-func-init-global-update-configurable.js fails +language/eval-code/indirect/var-env-func-init-multi.js strictFails +language/eval-code/indirect/var-env-func-non-strict.js strictFails +language/eval-code/indirect/var-env-global-lex-non-strict.js fails +language/eval-code/indirect/var-env-var-init-global-exstng.js strictFails +language/eval-code/indirect/var-env-var-init-global-new.js strictFails +language/eval-code/indirect/var-env-var-non-strict.js strictFails +language/expressions/arrow-function/dflt-params-ref-later.js fails +language/expressions/arrow-function/dflt-params-ref-self.js fails +language/expressions/arrow-function/lexical-super-call-from-within-constructor.js fails +language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow.js fails +language/expressions/arrow-function/scope-body-lex-distinct.js sloppyFails +language/expressions/arrow-function/scope-param-elem-var-close.js sloppyFails +language/expressions/arrow-function/scope-param-elem-var-open.js sloppyFails +language/expressions/arrow-function/scope-param-rest-elem-var-close.js sloppyFails +language/expressions/arrow-function/scope-param-rest-elem-var-open.js sloppyFails +language/expressions/arrow-function/scope-paramsbody-var-open.js fails +language/expressions/assignment/S11.13.1_A5_T1.js sloppyFails +language/expressions/assignment/S11.13.1_A5_T2.js sloppyFails +language/expressions/assignment/S11.13.1_A5_T3.js sloppyFails +language/expressions/assignment/S11.13.1_A5_T4.js sloppyFails +language/expressions/assignment/S11.13.1_A5_T5.js fails +language/expressions/assignment/S11.13.1_A6_T1.js sloppyFails +language/expressions/assignment/S11.13.1_A6_T2.js sloppyFails +language/expressions/assignment/S11.13.1_A6_T3.js sloppyFails +language/expressions/assignment/S11.13.1_A7_T1.js fails +language/expressions/assignment/S11.13.1_A7_T2.js fails +language/expressions/assignment/S11.13.1_A7_T3.js fails +language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js fails +language/expressions/assignment/destructuring/keyed-destructuring-property-reference-target-evaluation-order.js fails +language/expressions/assignment/dstr-array-elem-iter-rtrn-close-err.js fails +language/expressions/assignment/dstr-array-elem-iter-thrw-close-err.js fails +language/expressions/assignment/dstr-array-elem-iter-thrw-close.js fails +language/expressions/assignment/dstr-array-elem-put-let.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-list-thrw-close-err.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-list-thrw-close.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-rest-rtrn-close-err.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-rest-rtrn-close-null.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-rest-rtrn-close.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-rest-thrw-close-err.js fails +language/expressions/assignment/dstr-array-elem-trlg-iter-rest-thrw-close.js fails +language/expressions/assignment/dstr-array-rest-iter-rtrn-close-err.js fails +language/expressions/assignment/dstr-array-rest-iter-rtrn-close-null.js fails +language/expressions/assignment/dstr-array-rest-iter-rtrn-close.js fails +language/expressions/assignment/dstr-array-rest-iter-thrw-close-err.js fails +language/expressions/assignment/dstr-array-rest-iter-thrw-close.js fails +language/expressions/assignment/dstr-array-rest-lref-err.js fails +language/expressions/assignment/dstr-array-rest-put-let.js fails +language/expressions/assignment/dstr-obj-id-put-let.js fails +language/expressions/assignment/dstr-obj-prop-put-let.js fails +language/expressions/assignment/fn-name-lhs-cover.js fails +language/expressions/assignment/fn-name-lhs-member.js fails +language/expressions/async-function/expression-returns-promise.js fails +language/expressions/async-function/syntax-expression-is-PrimaryExpression.js fails +language/expressions/await/await-BindingIdentifier-in-global.js fails +language/expressions/await/await-in-nested-function.js fails +language/expressions/await/await-in-nested-generator.js fails +language/expressions/await/await-throws-rejections.js fails +language/expressions/call/11.2.3-3_3.js fails +language/expressions/call/eval-spread-empty-leading.js fails +language/expressions/call/eval-spread-empty-trailing.js fails +language/expressions/call/eval-spread.js fails +language/expressions/call/scope-lex-open.js fails +language/expressions/class/gen-meth-dflt-params-ref-later.js fails +language/expressions/class/gen-meth-dflt-params-ref-self.js fails +language/expressions/class/gen-meth-static-dflt-params-ref-later.js fails +language/expressions/class/gen-meth-static-dflt-params-ref-self.js fails +language/expressions/class/meth-dflt-params-ref-later.js fails +language/expressions/class/meth-dflt-params-ref-self.js fails +language/expressions/class/meth-static-dflt-params-ref-later.js fails +language/expressions/class/meth-static-dflt-params-ref-self.js fails +language/expressions/class/name.js fails +language/expressions/class/scope-gen-meth-paramsbody-var-open.js fails +language/expressions/class/scope-meth-paramsbody-var-open.js fails +language/expressions/class/scope-setter-paramsbody-var-open.js fails +language/expressions/class/scope-static-gen-meth-paramsbody-var-open.js fails +language/expressions/class/scope-static-meth-paramsbody-var-open.js fails +language/expressions/class/scope-static-setter-paramsbody-var-open.js fails +language/expressions/compound-assignment/S11.13.2_A5.10_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.10_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.10_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.10_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.10_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.11_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.11_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.11_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.11_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.11_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.1_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.1_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.1_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.1_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.1_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.2_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.2_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.2_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.2_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.2_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.3_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.3_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.3_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.3_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.3_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.4_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.4_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.4_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.4_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.4_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.5_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.5_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.5_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.5_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.5_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.6_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.6_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.6_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.6_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.6_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.7_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.7_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.7_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.7_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.7_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.8_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.8_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.8_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.8_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.8_T5.js fails +language/expressions/compound-assignment/S11.13.2_A5.9_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.9_T2.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.9_T3.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.9_T4.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A5.9_T5.js fails +language/expressions/compound-assignment/S11.13.2_A6.10_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.11_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.1_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.2_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.3_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.4_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.5_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.6_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.7_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.8_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A6.9_T1.js sloppyFails +language/expressions/compound-assignment/S11.13.2_A7.10_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.11_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.1_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.2_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.3_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.4_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.5_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.6_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.7_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.8_T4.js fails +language/expressions/compound-assignment/S11.13.2_A7.9_T4.js fails +language/expressions/delete/super-property.js fails +language/expressions/function/arguments-with-arguments-fn.js sloppyFails +language/expressions/function/arguments-with-arguments-lex.js sloppyFails +language/expressions/function/dflt-params-ref-later.js fails +language/expressions/function/dflt-params-ref-self.js fails +language/expressions/function/name.js fails +language/expressions/function/param-dflt-yield-non-strict.js sloppyFails +language/expressions/function/scope-body-lex-distinct.js sloppyFails +language/expressions/function/scope-name-var-open-non-strict.js sloppyFails +language/expressions/function/scope-name-var-open-strict.js strictFails +language/expressions/function/scope-param-elem-var-close.js sloppyFails +language/expressions/function/scope-param-elem-var-open.js sloppyFails +language/expressions/function/scope-param-rest-elem-var-close.js sloppyFails +language/expressions/function/scope-param-rest-elem-var-open.js sloppyFails +language/expressions/function/scope-paramsbody-var-open.js fails +language/expressions/generators/arguments-with-arguments-fn.js sloppyFails +language/expressions/generators/arguments-with-arguments-lex.js sloppyFails +language/expressions/generators/default-proto.js fails +language/expressions/generators/dflt-params-ref-later.js fails +language/expressions/generators/dflt-params-ref-self.js fails +language/expressions/generators/name.js fails +language/expressions/generators/named-yield-identifier-non-strict.js sloppyFails +language/expressions/generators/scope-body-lex-distinct.js sloppyFails +language/expressions/generators/scope-name-var-open-non-strict.js sloppyFails +language/expressions/generators/scope-name-var-open-strict.js strictFails +language/expressions/generators/scope-param-elem-var-close.js sloppyFails +language/expressions/generators/scope-param-elem-var-open.js sloppyFails +language/expressions/generators/scope-param-rest-elem-var-close.js sloppyFails +language/expressions/generators/scope-param-rest-elem-var-open.js sloppyFails +language/expressions/generators/scope-paramsbody-var-open.js fails +language/expressions/generators/yield-as-function-expression-binding-identifier.js sloppyFails +language/expressions/generators/yield-as-identifier-in-nested-function.js sloppyFails +language/expressions/generators/yield-as-literal-property-name.js fails +language/expressions/generators/yield-as-property-name.js fails +language/expressions/generators/yield-identifier-non-strict.js sloppyFails +language/expressions/object/let-non-strict-access.js sloppyFails +language/expressions/object/let-non-strict-syntax.js sloppyFails +language/expressions/object/method-definition/gen-meth-dflt-params-ref-later.js fails +language/expressions/object/method-definition/gen-meth-dflt-params-ref-self.js fails +language/expressions/object/method-definition/gen-yield-identifier-non-strict.js sloppyFails +language/expressions/object/method-definition/meth-dflt-params-ref-later.js fails +language/expressions/object/method-definition/meth-dflt-params-ref-self.js fails +language/expressions/object/method-definition/object-method-returns-promise.js fails +language/expressions/object/method-definition/yield-as-function-expression-binding-identifier.js sloppyFails +language/expressions/object/method-definition/yield-as-identifier-in-nested-function.js sloppyFails +language/expressions/object/method-definition/yield-as-literal-property-name.js fails +language/expressions/object/method-definition/yield-as-property-name.js fails +language/expressions/object/properties-names-eval-arguments.js strictFails +language/expressions/object/scope-gen-meth-body-lex-distinct.js sloppyFails +language/expressions/object/scope-gen-meth-param-elem-var-close.js sloppyFails +language/expressions/object/scope-gen-meth-param-elem-var-open.js sloppyFails +language/expressions/object/scope-gen-meth-param-rest-elem-var-close.js sloppyFails +language/expressions/object/scope-gen-meth-param-rest-elem-var-open.js sloppyFails +language/expressions/object/scope-gen-meth-paramsbody-var-open.js fails +language/expressions/object/scope-getter-body-lex-distinc.js sloppyFails +language/expressions/object/scope-meth-body-lex-distinct.js sloppyFails +language/expressions/object/scope-meth-param-elem-var-close.js sloppyFails +language/expressions/object/scope-meth-param-elem-var-open.js sloppyFails +language/expressions/object/scope-meth-param-rest-elem-var-close.js sloppyFails +language/expressions/object/scope-meth-param-rest-elem-var-open.js sloppyFails +language/expressions/object/scope-meth-paramsbody-var-open.js fails +language/expressions/object/scope-setter-body-lex-distinc.js sloppyFails +language/expressions/object/scope-setter-paramsbody-var-open.js fails +language/expressions/postfix-decrement/S11.3.2_A5_T1.js sloppyFails +language/expressions/postfix-decrement/S11.3.2_A5_T2.js sloppyFails +language/expressions/postfix-decrement/S11.3.2_A5_T3.js sloppyFails +language/expressions/postfix-decrement/S11.3.2_A5_T4.js sloppyFails +language/expressions/postfix-decrement/S11.3.2_A5_T5.js fails +language/expressions/postfix-decrement/S11.3.2_A6_T3.js fails +language/expressions/postfix-increment/S11.3.1_A5_T1.js sloppyFails +language/expressions/postfix-increment/S11.3.1_A5_T2.js sloppyFails +language/expressions/postfix-increment/S11.3.1_A5_T3.js sloppyFails +language/expressions/postfix-increment/S11.3.1_A5_T4.js sloppyFails +language/expressions/postfix-increment/S11.3.1_A5_T5.js fails +language/expressions/postfix-increment/S11.3.1_A6_T3.js fails +language/expressions/prefix-decrement/S11.4.5_A5_T1.js sloppyFails +language/expressions/prefix-decrement/S11.4.5_A5_T2.js sloppyFails +language/expressions/prefix-decrement/S11.4.5_A5_T3.js sloppyFails +language/expressions/prefix-decrement/S11.4.5_A5_T4.js sloppyFails +language/expressions/prefix-decrement/S11.4.5_A5_T5.js fails +language/expressions/prefix-decrement/S11.4.5_A6_T3.js fails +language/expressions/prefix-increment/S11.4.4_A5_T1.js sloppyFails +language/expressions/prefix-increment/S11.4.4_A5_T2.js sloppyFails +language/expressions/prefix-increment/S11.4.4_A5_T3.js sloppyFails +language/expressions/prefix-increment/S11.4.4_A5_T4.js sloppyFails +language/expressions/prefix-increment/S11.4.4_A5_T5.js fails +language/expressions/prefix-increment/S11.4.4_A6_T3.js fails +language/expressions/tagged-template/invalid-escape-sequences.js fails +language/expressions/tagged-template/tco-member.js strictFails +language/function-code/each-param-has-own-non-shared-eval-scope.js sloppyFails +language/function-code/each-param-has-own-scope.js sloppyFails +language/function-code/eval-param-env-with-computed-key.js sloppyFails +language/function-code/eval-param-env-with-prop-initializer.js sloppyFails +language/global-code/decl-lex-restricted-global.js fails +language/global-code/script-decl-func-dups.js fails +language/global-code/script-decl-func.js fails +language/global-code/script-decl-lex-deletion.js sloppyFails +language/global-code/script-decl-lex-lex.js fails +language/global-code/script-decl-lex-restricted-global.js fails +language/global-code/script-decl-lex-var.js fails +language/global-code/script-decl-lex.js fails +language/global-code/script-decl-var-collision.js fails +language/global-code/script-decl-var.js fails +language/identifiers/other_id_continue.js fails +language/identifiers/other_id_start-escaped.js fails +language/identifiers/other_id_start.js fails +language/statements/async-function/cptn-decl.js fails +language/statements/async-function/declaration-returns-promise.js fails +language/statements/async-function/evaluation-body.js fails +language/statements/async-function/syntax-declaration-line-terminators-allowed.js fails +language/statements/class/constructor-inferred-observable-iteration.js fails +language/statements/class/cptn-decl.js fails +language/statements/class/definition/class-method-returns-promise.js fails +language/statements/class/definition/getters-restricted-ids.js fails +language/statements/class/definition/methods-gen-yield-as-literal-property-name.js fails +language/statements/class/definition/methods-gen-yield-as-property-name.js fails +language/statements/class/definition/methods-named-eval-arguments.js fails +language/statements/class/definition/prototype-property.js fails +language/statements/class/definition/setters-prop-desc.js fails +language/statements/class/definition/setters-restricted-ids.js fails +language/statements/class/definition/this-access-restriction-2.js fails +language/statements/class/definition/this-access-restriction.js fails +language/statements/class/definition/this-check-ordering.js fails +language/statements/class/gen-meth-dflt-params-ref-later.js fails +language/statements/class/gen-meth-dflt-params-ref-self.js fails +language/statements/class/gen-meth-static-dflt-params-ref-later.js fails +language/statements/class/gen-meth-static-dflt-params-ref-self.js fails +language/statements/class/meth-dflt-params-ref-later.js fails +language/statements/class/meth-dflt-params-ref-self.js fails +language/statements/class/meth-static-dflt-params-ref-later.js fails +language/statements/class/meth-static-dflt-params-ref-self.js fails +language/statements/class/scope-gen-meth-paramsbody-var-open.js fails +language/statements/class/scope-meth-paramsbody-var-open.js fails +language/statements/class/scope-setter-paramsbody-var-open.js fails +language/statements/class/scope-static-gen-meth-paramsbody-var-open.js fails +language/statements/class/scope-static-meth-paramsbody-var-open.js fails +language/statements/class/scope-static-setter-paramsbody-var-open.js fails +language/statements/class/subclass/bound-function.js fails +language/statements/class/subclass/default-constructor-spread-override.js fails +language/statements/for-in/head-lhs-let.js sloppyFails +language/statements/for-in/head-var-bound-names-let.js sloppyFails +language/statements/for-in/identifier-let-allowed-as-lefthandside-expression-not-strict.js sloppyFails +language/statements/for-of/dstr-array-elem-iter-rtrn-close-err.js fails +language/statements/for-of/dstr-array-elem-iter-thrw-close-err.js fails +language/statements/for-of/dstr-array-elem-iter-thrw-close.js fails +language/statements/for-of/dstr-array-elem-put-let.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-list-thrw-close-err.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-list-thrw-close.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-rest-rtrn-close-err.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-rest-rtrn-close-null.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-rest-rtrn-close.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-rest-thrw-close-err.js fails +language/statements/for-of/dstr-array-elem-trlg-iter-rest-thrw-close.js fails +language/statements/for-of/dstr-array-rest-iter-rtrn-close-err.js fails +language/statements/for-of/dstr-array-rest-iter-rtrn-close-null.js fails +language/statements/for-of/dstr-array-rest-iter-rtrn-close.js fails +language/statements/for-of/dstr-array-rest-iter-thrw-close-err.js fails +language/statements/for-of/dstr-array-rest-iter-thrw-close.js fails +language/statements/for-of/dstr-array-rest-lref-err.js fails +language/statements/for-of/dstr-array-rest-put-let.js fails +language/statements/for-of/dstr-obj-id-put-let.js fails +language/statements/for-of/dstr-obj-prop-put-let.js fails +language/statements/for-of/head-var-bound-names-let.js sloppyFails +language/statements/for-of/iterator-next-reference.js fails +language/statements/for/head-lhs-let.js sloppyFails +language/statements/for/scope-body-lex-open.js fails +language/statements/function/13.2-30-s.js fails +language/statements/function/S13_A15_T4.js sloppyFails +language/statements/function/arguments-with-arguments-fn.js sloppyFails +language/statements/function/arguments-with-arguments-lex.js sloppyFails +language/statements/function/dflt-params-ref-later.js fails +language/statements/function/dflt-params-ref-self.js fails +language/statements/function/param-dflt-yield-non-strict.js sloppyFails +language/statements/function/scope-body-lex-distinct.js sloppyFails +language/statements/function/scope-param-elem-var-close.js sloppyFails +language/statements/function/scope-param-elem-var-open.js sloppyFails +language/statements/function/scope-param-rest-elem-var-close.js sloppyFails +language/statements/function/scope-param-rest-elem-var-open.js sloppyFails +language/statements/function/scope-paramsbody-var-open.js fails +language/statements/generators/arguments-with-arguments-fn.js sloppyFails +language/statements/generators/arguments-with-arguments-lex.js sloppyFails +language/statements/generators/default-proto.js fails +language/statements/generators/dflt-params-ref-later.js fails +language/statements/generators/dflt-params-ref-self.js fails +language/statements/generators/scope-body-lex-distinct.js sloppyFails +language/statements/generators/scope-param-elem-var-close.js sloppyFails +language/statements/generators/scope-param-elem-var-open.js sloppyFails +language/statements/generators/scope-param-rest-elem-var-close.js sloppyFails +language/statements/generators/scope-param-rest-elem-var-open.js sloppyFails +language/statements/generators/scope-paramsbody-var-open.js fails +language/statements/generators/yield-as-function-expression-binding-identifier.js sloppyFails +language/statements/generators/yield-as-identifier-in-nested-function.js sloppyFails +language/statements/generators/yield-as-literal-property-name.js fails +language/statements/generators/yield-as-property-name.js fails +language/statements/generators/yield-identifier-non-strict.js sloppyFails +language/statements/let/block-local-closure-set-before-initialization.js fails +language/statements/let/function-local-closure-set-before-initialization.js fails +language/statements/let/global-closure-set-before-initialization.js fails +language/statements/throw/S12.13_A2_T6.js strictFails +language/statements/try/S12.14_A18_T6.js strictFails +language/statements/try/scope-catch-block-lex-open.js fails +language/statements/variable/binding-resolution.js sloppyFails +language/statements/with/unscopables-inc-dec.js sloppyFails +language/types/reference/put-value-prop-base-primitive.js strictFails diff --git a/tests/auto/qml/ecmascripttests/ecmascripttests.pro b/tests/auto/qml/ecmascripttests/ecmascripttests.pro index 6d3ee12307..9c09ee701e 100644 --- a/tests/auto/qml/ecmascripttests/ecmascripttests.pro +++ b/tests/auto/qml/ecmascripttests/ecmascripttests.pro @@ -1,20 +1,13 @@ -CONFIG += testcase -TARGET = tst_ecmascripttests -QT += testlib -macos:CONFIG -= app_bundle -SOURCES += tst_ecmascripttests.cpp -DEFINES += SRCDIR=\\\"$$PWD\\\" - -TESTSCRIPT=$$PWD/test262.py -isEmpty(V4CMD): V4CMD = qmljs +TEMPLATE = subdirs +SUBDIRS = testcase.pro qjstest checkjittarget.target = check-jit -checkjittarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations +checkjittarget.commands = qjstest --jit --parallel --with-test-expectations --update-expectations checkjittarget.depends = all QMAKE_EXTRA_TARGETS += checkjittarget checkmothtarget.target = check-interpreter -checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations +checkmothtarget.commands = qjstest --interpret --parallel --with-test-expectations checkmothtarget.depends = all QMAKE_EXTRA_TARGETS += checkmothtarget diff --git a/tests/auto/qml/ecmascripttests/qjstest/main.cpp b/tests/auto/qml/ecmascripttests/qjstest/main.cpp new file mode 100644 index 0000000000..4a3541d892 --- /dev/null +++ b/tests/auto/qml/ecmascripttests/qjstest/main.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the V4VM module 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 <QJSEngine> +#include <QCoreApplication> +#include <QCommandLineParser> +#include <qdebug.h> +#include <stdlib.h> + +#include "test262runner.h" + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + + QCommandLineParser parser; + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption verbose("verbose", "Verbose output"); + parser.addOption(verbose); + QCommandLineOption commandOption("command", "Javascript command line interpreter", "command"); + parser.addOption(commandOption); + QCommandLineOption testDir("tests", "path to the tests", "tests", "test262"); + parser.addOption(testDir); + QCommandLineOption cat("cat", "Print packaged test code that would be run"); + parser.addOption(cat); + QCommandLineOption parallel("parallel", "Run tests in parallel"); + parser.addOption(parallel); + QCommandLineOption jit("jit", "JIT all code"); + parser.addOption(jit); + QCommandLineOption bytecode("interpret", "Run using the bytecode interpreter"); + parser.addOption(bytecode); + QCommandLineOption withExpectations("with-test-expectations", "Parse TestExpectations to deal with known failures"); + parser.addOption(withExpectations); + QCommandLineOption updateExpectations("update-expectations", "Update TestExpectations to remove unexepected passes"); + parser.addOption(updateExpectations); + QCommandLineOption writeExpectations("write-expectations", "Generate a new TestExpectations file based on the results of the run"); + parser.addOption(writeExpectations); + parser.addPositionalArgument("[filter]", "Only run tests that contain filter in their name"); + + parser.process(app); + + Test262Runner testRunner(parser.value(commandOption), parser.value(testDir)); + + QStringList otherArgs = parser.positionalArguments(); + if (otherArgs.size() > 1) { + qWarning() << "too many arguments"; + return 1; + } else if (otherArgs.size()) { + testRunner.setFilter(otherArgs.at(0)); + } + + if (parser.isSet(cat)) { + testRunner.cat(); + return 0; + } + + if (parser.isSet(updateExpectations) && parser.isSet(writeExpectations)) { + qWarning() << "Can only specify one of --update-expectations and --write-expectations."; + exit(1); + } + + if (parser.isSet(jit) && parser.isSet(bytecode)) { + qWarning() << "Can only specify one of --jit and --interpret."; + exit(1); + } + + int flags = 0; + if (parser.isSet(verbose)) + flags |= Test262Runner::Verbose; + if (parser.isSet(parallel)) + flags |= Test262Runner::Parallel; + if (parser.isSet(jit)) + flags |= Test262Runner::ForceJIT; + if (parser.isSet(bytecode)) + flags |= Test262Runner::ForceBytecode; + if (parser.isSet(withExpectations)) + flags |= Test262Runner::WithTestExpectations; + if (parser.isSet(updateExpectations)) + flags |= Test262Runner::UpdateTestExpectations; + if (parser.isSet(writeExpectations)) + flags |= Test262Runner::WriteTestExpectations; + testRunner.setFlags(flags); + + if (testRunner.run()) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/tests/auto/qml/ecmascripttests/qjstest/qjstest.pro b/tests/auto/qml/ecmascripttests/qjstest/qjstest.pro new file mode 100644 index 0000000000..6dec5f8f23 --- /dev/null +++ b/tests/auto/qml/ecmascripttests/qjstest/qjstest.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = qjstest +QT += qml-private +INCLUDEPATH += . + +DEFINES += QT_DEPRECATED_WARNINGS + +HEADERS += test262runner.h +SOURCES += main.cpp test262runner.cpp + +QMAKE_TARGET_DESCRIPTION = Javascript test runner + +load(qt_tool) diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp new file mode 100644 index 0000000000..cbe3be2e70 --- /dev/null +++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp @@ -0,0 +1,852 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the V4VM module 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 "test262runner.h" + +#include <qfile.h> +#include <qdir.h> +#include <qdiriterator.h> +#include <qdebug.h> +#include <qprocess.h> +#include <qtemporaryfile.h> + +#include <private/qv4script_p.h> +#include "private/qv4globalobject_p.h" +#include "private/qqmlbuiltinfunctions_p.h" +#include "private/qv4arraybuffer_p.h" + +#include "qrunnable.h" + +static const char *excludedFeatures[] = { + "BigInt", + "class-fields-public", + "class-fields-private", + "Promise.prototype.finally", + "async-iteration", + "Symbol.asyncIterator", + "object-rest", + "object-spread", + "optional-catch-binding", + "regexp-dotall", + "regexp-lookbehind", + "regexp-named-groups", + "regexp-unicode-property-escapes", + "Atomics", + "SharedArrayBuffer", + "Array.prototype.flatten", + "Array.prototype.flatMap", + "string-trimming", + "String.prototype.trimEnd", + "String.prototype.trimStart", + "numeric-separator-literal", + + // optional features, not supported by us + "caller", + nullptr +}; + +static const char *excludedFilePatterns[] = { + "realm", + nullptr +}; + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static ReturnedValue method_detachArrayBuffer(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc) + return scope.engine->throwTypeError(); + Scoped<ArrayBuffer> a(scope, argv[0]); + if (!a) + return scope.engine->throwTypeError(); + + if (a->isShared()) + return scope.engine->throwTypeError(); + + a->d()->detachArrayBuffer(); + + return Encode::null(); +} + +static void initD262(ExecutionEngine *e) +{ + Scope scope(e); + ScopedObject d262(scope, e->newObject()); + + d262->defineDefaultProperty(QStringLiteral("detachArrayBuffer"), method_detachArrayBuffer, 1); + + ScopedString s(scope, e->newString(QStringLiteral("$262"))); + e->globalObject->put(s, d262); +} + +} + +QT_END_NAMESPACE + +Test262Runner::Test262Runner(const QString &command, const QString &dir) + : command(command), testDir(dir) +{ + if (testDir.endsWith(QLatin1Char('/'))) + testDir = testDir.chopped(1); +} + +Test262Runner::~Test262Runner() +{ + delete threadPool; +} + +void Test262Runner::cat() +{ + if (!loadTests()) + return; + + if (testCases.size() != 1) + qWarning() << "test262 --cat: Ambiguous test case, using" << testCases.begin().key(); + TestData data = getTestData(testCases.begin().value()); + printf("%s", data.content.constData()); +} + +bool Test262Runner::run() +{ + if (!loadTests()) + return false; + + if (flags & Parallel) { + threadPool = new QThreadPool; + threadPool->setStackSize(16*1024*1024); + if (flags & Verbose) + qDebug() << "Running in parallel with" << QThread::idealThreadCount() << "threads."; + } + + if (flags & ForceJIT) + qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0")); + else if (flags & ForceBytecode) + qputenv("QV4_FORCE_INTERPRETER", QByteArray("1")); + + if (flags & WithTestExpectations) + loadTestExpectations(); + + for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) { + auto c = it.value(); + if (!c.skipTestCase) { + int result = runSingleTest(c); + if (result == -2) + return false; + } + } + + if (threadPool) + threadPool->waitForDone(); + + const bool testsOk = report(); + + if (flags & WriteTestExpectations) + writeTestExpectations(); + else if (flags & UpdateTestExpectations) + updateTestExpectations(); + + return testsOk; +} + +bool Test262Runner::report() +{ + qDebug() << "Test execution summary:"; + qDebug() << " Executed" << testCases.size() << "test cases."; + QStringList crashes; + QStringList unexpectedFailures; + QStringList unexpectedPasses; + for (auto it = testCases.constBegin(); it != testCases.constEnd(); ++it) { + const auto c = it.value(); + if (c.strictResult == c.strictExpectation && c.sloppyResult == c.sloppyExpectation) + continue; + auto report = [&] (TestCase::Result expected, TestCase::Result result, const char *s) { + if (result == TestCase::Crashes) + crashes << (it.key() + " crashed in " + s + " mode"); + if (result == TestCase::Fails && expected == TestCase::Passes) + unexpectedFailures << (it.key() + " failed in " + s + " mode"); + if (result == TestCase::Passes && expected == TestCase::Fails) + unexpectedPasses << (it.key() + " unexpectedly passed in " + s + " mode"); + }; + report(c.strictExpectation, c.strictResult, "strict"); + report(c.sloppyExpectation, c.sloppyResult, "sloppy"); + } + if (!crashes.isEmpty()) { + qDebug() << " Encountered" << crashes.size() << "crashes in the following files:"; + for (const QString &f : qAsConst(crashes)) + qDebug() << " " << f; + } + if (!unexpectedFailures.isEmpty()) { + qDebug() << " Encountered" << unexpectedFailures.size() << "unexpected failures in the following files:"; + for (const QString &f : qAsConst(unexpectedFailures)) + qDebug() << " " << f; + } + if (!unexpectedPasses.isEmpty()) { + qDebug() << " Encountered" << unexpectedPasses.size() << "unexpected passes in the following files:"; + for (const QString &f : qAsConst(unexpectedPasses)) + qDebug() << " " << f; + } + return crashes.isEmpty() && unexpectedFailures.isEmpty() && unexpectedPasses.isEmpty(); +} + +bool Test262Runner::loadTests() +{ + QDir dir(testDir + "/test"); + if (!dir.exists()) { + qWarning() << "Could not load tests," << dir.path() << "does not exist."; + return false; + } + + QString annexB = "annexB"; + QString harness = "harness"; + QString intl402 = "intl402"; + + int pathlen = dir.path().length() + 1; + QDirIterator it(dir, QDirIterator::Subdirectories); + while (it.hasNext()) { + QString file = it.next().mid(pathlen); + if (!file.endsWith(".js")) + continue; + if (file.endsWith("_FIXTURE.js")) + continue; + if (!filter.isEmpty() && !file.contains(filter)) + continue; + if (file.startsWith(annexB) || file.startsWith(harness) || file.startsWith(intl402)) + continue; + const char **excluded = excludedFilePatterns; + bool skip = false; + while (*excluded) { + if (file.contains(QLatin1String(*excluded))) + skip = true; + ++excluded; + } + if (skip) + continue; + + testCases.insert(file, TestCase{ file }); + } + if (testCases.isEmpty()) { + qWarning() << "No tests to run."; + return false; + } + + return true; +} + + +struct TestExpectationLine { + TestExpectationLine(const QByteArray &line); + enum State { + Fails, + SloppyFails, + StrictFails, + Skip, + Passes + } state; + QString testCase; + + QByteArray toLine() const; + void update(const TestCase &testCase); + + static TestExpectationLine fromTestCase(const TestCase &testCase); +private: + TestExpectationLine() = default; + static State stateFromTestCase(const TestCase &testCase); +}; + +TestExpectationLine::TestExpectationLine(const QByteArray &line) +{ + int space = line.indexOf(' '); + + testCase = QString::fromUtf8(space > 0 ? line.left(space) : line); + if (!testCase.endsWith(".js")) + testCase += ".js"; + + state = Fails; + if (space < 0) + return; + QByteArray qualifier = line.mid(space + 1); + if (qualifier == "skip") + state = Skip; + else if (qualifier == "strictFails") + state = StrictFails; + else if (qualifier == "sloppyFails") + state = SloppyFails; + else if (qualifier == "fails") + state = Fails; + else + qWarning() << "illegal format in TestExpectations, line" << line; +} + +QByteArray TestExpectationLine::toLine() const { + const char *res = nullptr; + switch (state) { + case Fails: + res = " fails\n"; + break; + case SloppyFails: + res = " sloppyFails\n"; + break; + case StrictFails: + res = " strictFails\n"; + break; + case Skip: + res = " skip\n"; + break; + case Passes: + // no need for an entry + return QByteArray(); + } + QByteArray result = testCase.toUtf8() + res; + return result; +} + +void TestExpectationLine::update(const TestCase &testCase) +{ + Q_ASSERT(testCase.test == this->testCase); + + State resultState = stateFromTestCase(testCase); + switch (resultState) { + case Fails: + // no improvement, don't update + break; + case SloppyFails: + if (state == Fails) + state = SloppyFails; + else if (state == StrictFails) + // we have a regression in sloppy mode, but strict now passes + state = Passes; + break; + case StrictFails: + if (state == Fails) + state = StrictFails; + else if (state == SloppyFails) + // we have a regression in strict mode, but sloppy now passes + state = Passes; + break; + case Skip: + Q_ASSERT(state == Skip); + // nothing to do + break; + case Passes: + state = Passes; + } +} + +TestExpectationLine TestExpectationLine::fromTestCase(const TestCase &testCase) +{ + TestExpectationLine l; + l.testCase = testCase.test; + l.state = stateFromTestCase(testCase); + return l; +} + +TestExpectationLine::State TestExpectationLine::stateFromTestCase(const TestCase &testCase) +{ + // keep skipped tests + if (testCase.skipTestCase) + return Skip; + + bool strictFails = (testCase.strictResult == TestCase::Crashes || testCase.strictResult == TestCase::Fails); + bool sloppyFails = (testCase.sloppyResult == TestCase::Crashes || testCase.sloppyResult == TestCase::Fails); + if (strictFails && sloppyFails) + return Fails; + if (strictFails) + return StrictFails; + if (sloppyFails) + return SloppyFails; + return Passes; +} + + +void Test262Runner::loadTestExpectations() +{ + QFile file("TestExpectations"); + if (!file.open(QFile::ReadOnly)) { + qWarning() << "Could not open TestExpectations file."; + return; + } + + int line = 0; + while (!file.atEnd()) { + ++line; + QByteArray line = file.readLine().trimmed(); + if (line.startsWith('#') || line.isEmpty()) + continue; + TestExpectationLine expectation(line); + if (!filter.isEmpty() && !expectation.testCase.contains(filter)) + continue; + + if (!testCases.contains(expectation.testCase)) + qWarning() << "Unknown test case" << expectation.testCase << "in TestExpectations file."; + //qDebug() << "TestExpectations:" << expectation.testCase << expectation.state; + TestCase &s = testCases[expectation.testCase]; + switch (expectation.state) { + case TestExpectationLine::Fails: + s.strictExpectation = TestCase::Fails; + s.sloppyExpectation = TestCase::Fails; + break; + case TestExpectationLine::SloppyFails: + s.strictExpectation = TestCase::Passes; + s.sloppyExpectation = TestCase::Fails; + break; + case TestExpectationLine::StrictFails: + s.strictExpectation = TestCase::Fails; + s.sloppyExpectation = TestCase::Passes; + break; + case TestExpectationLine::Skip: + s.skipTestCase = true; + break; + case TestExpectationLine::Passes: + Q_UNREACHABLE(); + } + } +} + +void Test262Runner::updateTestExpectations() +{ + QFile file("TestExpectations"); + if (!file.open(QFile::ReadOnly)) { + qWarning() << "Could not open TestExpectations file."; + return; + } + + QTemporaryFile updatedExpectations; + updatedExpectations.open(); + + int line = 0; + while (!file.atEnd()) { + ++line; + QByteArray originalLine = file.readLine(); + QByteArray line = originalLine.trimmed(); + if (line.startsWith('#') || line.isEmpty()) { + updatedExpectations.write(originalLine); + continue; + } + + TestExpectationLine expectation(line); +// qDebug() << "checking: " << expectation.testCase; + if (!testCases.contains(expectation.testCase)) { + updatedExpectations.write(originalLine); + continue; + } + const TestCase &testcase = testCases.value(expectation.testCase); + expectation.update(testcase); + + line = expectation.toLine(); +// qDebug() << "updated line:" << line; + updatedExpectations.write(line); + } + file.close(); + updatedExpectations.close(); + file.remove(); + qDebug() << updatedExpectations.fileName() << file.fileName(); + updatedExpectations.copy(file.fileName()); + qDebug() << "Updated TestExpectations file written!"; +} + +void Test262Runner::writeTestExpectations() +{ + QFile file("TestExpectations"); + + QTemporaryFile expectations; + expectations.open(); + + for (auto c : qAsConst(testCases)) { + TestExpectationLine line = TestExpectationLine::fromTestCase(c); + expectations.write(line.toLine()); + } + + expectations.close(); + if (file.exists()) + file.remove(); + qDebug() << expectations.fileName() << file.fileName(); + expectations.copy(file.fileName()); + qDebug() << "new TestExpectations file written!"; + +} + +static bool executeTest(const QByteArray &data, bool runAsModule = false, const QString &testCasePath = QString(), const QByteArray &harnessForModules = QByteArray()) +{ + QString testData = QString::fromUtf8(data.constData(), data.size()); + + QV4::ExecutionEngine vm; + + QV4::Scope scope(&vm); + + QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension); + QV4::initD262(&vm); + + if (runAsModule) { + const QUrl rootModuleUrl = QUrl::fromLocalFile(testCasePath); + // inject all modules with the harness + QVector<QUrl> modulesToLoad = { rootModuleUrl }; + while (!modulesToLoad.isEmpty()) { + QUrl url = modulesToLoad.takeFirst(); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> module; + + QFile f(url.toLocalFile()); + if (f.open(QIODevice::ReadOnly)) { + QByteArray content = harnessForModules + f.readAll(); + module = vm.compileModule(url.toString(), QString::fromUtf8(content.constData(), content.length()), QFileInfo(f).lastModified()); + if (vm.hasException) + break; + vm.injectModule(module); + } else { + vm.throwError(QStringLiteral("Could not load module")); + break; + } + + for (const QString &request: module->moduleRequests()) { + const QUrl absoluteRequest = module->finalUrl().resolved(QUrl(request)); + if (!vm.modules.contains(absoluteRequest)) + modulesToLoad << absoluteRequest; + } + } + + if (!vm.hasException) { + if (auto rootModuleUnit = vm.loadModule(rootModuleUrl)) { + if (rootModuleUnit->instantiate(&vm)) + rootModuleUnit->evaluate(); + } + } + } else { + QV4::ScopedContext ctx(scope, vm.rootContext()); + + QV4::Script script(ctx, QV4::Compiler::ContextType::Global, testData); + script.parse(); + + if (!vm.hasException) + script.run(); + } + return !vm.hasException; +} + +class SingleTest : public QRunnable +{ +public: + SingleTest(Test262Runner *runner, const TestData &data) + : runner(runner), data(data) + { + command = runner->command; + } + void run(); + + void runExternalTest(); + + QString command; + Test262Runner *runner; + TestData data; +}; + +void SingleTest::run() +{ + if (!command.isEmpty()) { + runExternalTest(); + return; + } + + if (data.runInSloppyMode) { + bool ok = ::executeTest(data.content); + if (data.negative) + ok = !ok; + + data.sloppyResult = ok ? TestCase::Passes : TestCase::Fails; + } else { + data.sloppyResult = TestCase::Skipped; + } + if (data.runInStrictMode) { + const QString testCasePath = QFileInfo(runner->testDir + "/test/" + data.test).absoluteFilePath(); + QByteArray c = "'use strict';\n" + data.content; + bool ok = ::executeTest(c, data.runAsModuleCode, testCasePath, data.harness); + if (data.negative) + ok = !ok; + + data.strictResult = ok ? TestCase::Passes : TestCase::Fails; + } else { + data.strictResult = TestCase::Skipped; + } + runner->addResult(data); +} + +void SingleTest::runExternalTest() +{ + auto runTest = [=] (const char *header, TestCase::Result *result) { + QTemporaryFile tempFile; + tempFile.open(); + tempFile.write(header); + tempFile.write(data.content); + tempFile.close(); + + QProcess process; +// if (flags & Verbose) +// process.setReadChannelMode(QProcess::ForwardedChannels); + + process.start(command, QStringList(tempFile.fileName())); + if (!process.waitForFinished(-1) || process.error() == QProcess::FailedToStart) { + qWarning() << "Could not execute" << command; + *result = TestCase::Crashes; + } + if (process.exitStatus() != QProcess::NormalExit) { + *result = TestCase::Crashes; + } + bool ok = (process.exitCode() == EXIT_SUCCESS); + if (data.negative) + ok = !ok; + *result = ok ? TestCase::Passes : TestCase::Fails; + }; + + if (data.runInSloppyMode) + runTest("", &data.sloppyResult); + if (data.runInStrictMode) + runTest("'use strict';\n", &data.strictResult); + + runner->addResult(data); +} + +int Test262Runner::runSingleTest(TestCase testCase) +{ + TestData data = getTestData(testCase); +// qDebug() << "starting test" << data.test; + + if (data.isExcluded || data.async) + return 0; + + if (threadPool) { + SingleTest *test = new SingleTest(this, data); + threadPool->start(test); + return 0; + } + SingleTest test(this, data); + test.run(); + return 0; +} + +void Test262Runner::addResult(TestCase result) +{ + { + QMutexLocker locker(&mutex); + Q_ASSERT(result.strictExpectation == testCases[result.test].strictExpectation); + Q_ASSERT(result.sloppyExpectation == testCases[result.test].sloppyExpectation); + testCases[result.test] = result; + } + + if (!(flags & Verbose)) + return; + + QString test = result.test; + if (result.strictResult == TestCase::Skipped) { + ; + } else if (result.strictResult == TestCase::Crashes) { + qDebug() << "FAIL:" << test << "crashed in strict mode!"; + } else if ((result.strictResult == TestCase::Fails) && (result.strictExpectation == TestCase::Fails)) { + qDebug() << "PASS:" << test << "failed in strict mode as expected"; + } else if ((result.strictResult == TestCase::Passes) == (result.strictExpectation == TestCase::Passes)) { + qDebug() << "PASS:" << test << "passed in strict mode"; + } else if (!(result.strictExpectation == TestCase::Fails)) { + qDebug() << "FAIL:" << test << "failed in strict mode"; + } else { + qDebug() << "XPASS:" << test << "unexpectedly passed in strict mode"; + } + + if (result.sloppyResult == TestCase::Skipped) { + ; + } else if (result.sloppyResult == TestCase::Crashes) { + qDebug() << "FAIL:" << test << "crashed in sloppy mode!"; + } else if ((result.sloppyResult == TestCase::Fails) && (result.sloppyExpectation == TestCase::Fails)) { + qDebug() << "PASS:" << test << "failed in sloppy mode as expected"; + } else if ((result.sloppyResult == TestCase::Passes) == (result.sloppyExpectation == TestCase::Passes)) { + qDebug() << "PASS:" << test << "passed in sloppy mode"; + } else if (!(result.sloppyExpectation == TestCase::Fails)) { + qDebug() << "FAIL:" << test << "failed in sloppy mode"; + } else { + qDebug() << "XPASS:" << test << "unexpectedly passed in sloppy mode"; + } +} + +TestData Test262Runner::getTestData(const TestCase &testCase) +{ + QFile testFile(testDir + "/test/" + testCase.test); + if (!testFile.open(QFile::ReadOnly)) { + qWarning() << "wrong test file" << testCase.test; + exit(1); + } + QByteArray content = testFile.readAll(); + content.replace(QByteArrayLiteral("\r\n"), "\n"); + +// qDebug() << "parsing test file" << test; + + TestData data(testCase); + parseYaml(content, &data); + + data.harness += harness("assert.js"); + data.harness += harness("sta.js"); + + for (QByteArray inc : qAsConst(data.includes)) { + inc = inc.trimmed(); + data.harness += harness(inc); + } + + if (data.async) + data.harness += harness("doneprintHandle.js"); + + data.content = data.harness + content; + + return data; +} + +struct YamlSection { + YamlSection(const QByteArray &yaml, const char *sectionName); + + bool contains(const char *keyword) const; + QList<QByteArray> keywords() const; + + QByteArray yaml; + int start = -1; + int length = 0; + bool shortSection = false; +}; + +YamlSection::YamlSection(const QByteArray &yaml, const char *sectionName) + : yaml(yaml) +{ + start = yaml.indexOf(sectionName); + if (start < 0) + return; + start += static_cast<int>(strlen(sectionName)); + int end = yaml.indexOf('\n', start + 1); + if (end < 0) + end = yaml.length(); + + int s = yaml.indexOf('[', start); + if (s > 0 && s < end) { + shortSection = true; + start = s + 1; + end = yaml.indexOf(']', s); + } else { + while (end < yaml.size() - 1 && yaml.at(end + 1) == ' ') + end = yaml.indexOf('\n', end + 1); + } + length = end - start; +} + +bool YamlSection::contains(const char *keyword) const +{ + if (start < 0) + return false; + int idx = yaml.indexOf(keyword, start); + if (idx >= start && idx < start + length) + return true; + return false; +} + +QList<QByteArray> YamlSection::keywords() const +{ + if (start < 0) + return QList<QByteArray>(); + + QByteArray content = yaml.mid(start, length); + QList<QByteArray> keywords; + if (shortSection) { + keywords = content.split(','); + } else { + const QList<QByteArray> list = content.split('\n'); + for (const QByteArray &l : list) { + int i = 0; + while (i < l.size() && (l.at(i) == ' ' || l.at(i) == '-')) + ++i; + QByteArray entry = l.mid(i); + if (!entry.isEmpty()) + keywords.append(entry); + } + } +// qDebug() << "keywords:" << keywords; + return keywords; +} + + +void Test262Runner::parseYaml(const QByteArray &content, TestData *data) +{ + int start = content.indexOf("/*---"); + if (start < 0) + return; + start += sizeof("/*---"); + + int end = content.indexOf("---*/"); + if (end < 0) + return; + + QByteArray yaml = content.mid(start, end - start); + + if (yaml.contains("negative:")) + data->negative = true; + + YamlSection flags(yaml, "flags:"); + data->runInSloppyMode = !flags.contains("onlyStrict"); + data->runInStrictMode = !flags.contains("noStrict") && !flags.contains("raw"); + data->runAsModuleCode = flags.contains("module"); + data->async = flags.contains("async"); + + if (data->runAsModuleCode) { + data->runInStrictMode = true; + data->runInSloppyMode = false; + } + + YamlSection includes(yaml, "includes:"); + data->includes = includes.keywords(); + + YamlSection features = YamlSection(yaml, "features:"); + + const char **f = excludedFeatures; + while (*f) { + if (features.contains(*f)) { + data->isExcluded = true; + break; + } + ++f; + } + +// qDebug() << "Yaml:\n" << yaml; +} + +QByteArray Test262Runner::harness(const QByteArray &name) +{ + if (harnessFiles.contains(name)) + return harnessFiles.value(name); + + QFile h(testDir + QLatin1String("/harness/") + name); + if (!h.open(QFile::ReadOnly)) { + qWarning() << "Illegal test harness file" << name; + exit(1); + } + + QByteArray content = h.readAll(); + harnessFiles.insert(name, content); + return content; +} diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.h b/tests/auto/qml/ecmascripttests/qjstest/test262runner.h new file mode 100644 index 0000000000..e20d4a7bdd --- /dev/null +++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the V4VM module 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 TEST262RUNNER_H +#define TEST262RUNNER_H +#include <qstring.h> +#include <qstringlist.h> +#include <qset.h> +#include <qmap.h> +#include <qmutex.h> +#include <qthreadpool.h> + +struct TestCase { + TestCase() = default; + TestCase(const QString &test) + : test(test) {} + enum Result { + Skipped, + Passes, + Fails, + Crashes + }; + bool skipTestCase = false; + Result strictExpectation = Passes; + Result sloppyExpectation = Passes; + Result strictResult = Skipped; + Result sloppyResult = Skipped; + + QString test; +}; + +struct TestData : TestCase { + TestData(const TestCase &testCase) + : TestCase(testCase) {} + // flags + bool negative = false; + bool runInStrictMode = true; + bool runInSloppyMode = true; + bool runAsModuleCode = false; + bool async = false; + + bool isExcluded = false; + + QList<QByteArray> includes; + + QByteArray harness; + QByteArray content; +}; + +class Test262Runner +{ +public: + Test262Runner(const QString &command, const QString &testDir); + ~Test262Runner(); + + enum Mode { + Sloppy = 0, + Strict = 1 + }; + + enum Flags { + Verbose = 0x1, + Parallel = 0x2, + ForceBytecode = 0x4, + ForceJIT = 0x8, + WithTestExpectations = 0x10, + UpdateTestExpectations = 0x20, + WriteTestExpectations = 0x40, + }; + void setFlags(int f) { flags = f; } + + void setFilter(const QString &f) { filter = f; } + + void cat(); + bool run(); + + bool report(); + +private: + friend class SingleTest; + bool loadTests(); + void loadTestExpectations(); + void updateTestExpectations(); + void writeTestExpectations(); + int runSingleTest(TestCase testCase); + + TestData getTestData(const TestCase &testCase); + void parseYaml(const QByteArray &content, TestData *data); + + QByteArray harness(const QByteArray &name); + + void addResult(TestCase result); + + QString command; + QString testDir; + int flags = 0; + + QMutex mutex; + QString filter; + + QMap<QString, TestCase> testCases; + QHash<QByteArray, QByteArray> harnessFiles; + + QThreadPool *threadPool = nullptr; +}; + + +#endif diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262 -Subproject d60c4ed97e69639bc5bc1db43a98828debf80c8 +Subproject 6b0c42c63c2492bd0a7a96d3179d122b5f71793 diff --git a/tests/auto/qml/ecmascripttests/test262.py b/tests/auto/qml/ecmascripttests/test262.py index 9f0a7c1dee..19551e3ba2 100755 --- a/tests/auto/qml/ecmascripttests/test262.py +++ b/tests/auto/qml/ecmascripttests/test262.py @@ -1,39 +1,27 @@ #!/usr/bin/env python ############################################################################# ## -## Copyright (C) 2015 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ +## Copyright (C) 2017 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ ## ## This file is part of the test suite module of the Qt Toolkit. ## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: +## $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. ## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## 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$ ## @@ -73,6 +61,35 @@ from parseTestRecord import parseTestRecord, stripHeader from packagerConfig import * +# excluded features that are still experimental and not part of any official standard +# see also the features.txt file in test262/ +excludedFeatures = [ + "BigInt", + "class-fields-public", + "class-fields-private", + "Promise.prototype.finally", + "async-iteration", + "Symbol.asyncIterator", + "object-rest", + "object-spread", + "optional-catch-binding", + "regexp-dotall", + "regexp-lookbehind", + "regexp-named-groups", + "regexp-unicode-property-escapes", + "Atomics", + "SharedArrayBuffer", + "Array.prototype.flatten", + "Array.prototype.flatMap", + "string-trimming", + "String.prototype.trimEnd", + "String.prototype.trimStart", + "numeric-separator-literal", + + # optional features, not supported by us + "caller" +] + # ############# Helpers needed for parallel multi-process test execution ############ def runTest(case, args): @@ -107,19 +124,19 @@ class TestExpectations: continue record = line.split() if len(record) == 1: - self.testsToSkip.append(record[0]) + self.failingTests.append(record[0]) else: test = record[0] expectation = record[1] - if expectation == "failing": - self.failingTests.append(test) + if expectation == "skip": + self.testsToSkip.append(test) f.close() def update(self, progress): - unexpectedPasses = [c.case.name[-1] for c in progress.failed_tests if c.case.IsNegative()] + unexpectedPasses = [c.case.name for c in progress.failed_tests if c.case.IsNegative()] # If a test fails that we expected to fail, then it actually passed unexpectedly. - failures = [c.case.name[-1] for c in progress.failed_tests if not c.case.IsNegative()] + failures = [c.case.name for c in progress.failed_tests if not c.case.IsNegative()] for failure in failures: if failure in self.failingTests: unexpectedPasses.append(failure) @@ -128,7 +145,7 @@ class TestExpectations: lines = f.read().splitlines() oldLen = len(lines) for result in unexpectedPasses: - expectationLine = result + " failing" + expectationLine = result try: lines.remove(expectationLine) except ValueError: @@ -289,14 +306,17 @@ class TestCase(object): f.close() testRecord = parseTestRecord(self.contents, name) self.test = testRecord["test"] + if 'features' in testRecord: + self.features = testRecord["features"]; + else: + self.features = [] del testRecord["test"] del testRecord["header"] - del testRecord["commentary"] self.testRecord = testRecord; def GetName(self): - return path.join(*self.name) + return self.name def GetMode(self): if self.strict_mode: @@ -322,14 +342,20 @@ class TestCase(object): def IsNoStrict(self): return 'noStrict' in self.testRecord + def IsExperimental(self): + for f in self.features: + if excludedFeatures.count(f) >= 1: + return True; + return False + def GetSource(self): # "var testDescrip = " + str(self.testRecord) + ';\n\n' + \ - source = self.suite.GetInclude("cth.js") + \ + source = self.suite.GetInclude("assert.js") + \ self.suite.GetInclude("sta.js") + \ - self.suite.GetInclude("ed.js") + \ - self.suite.GetInclude("testBuiltInObject.js") + \ - self.suite.GetInclude("testIntl.js") + \ self.test + '\n' + if 'includes' in self.testRecord: + for inc in self.testRecord['includes']: + source += self.suite.GetInclude(inc); if self.strict_mode: source = '"use strict";\nvar strict_mode = true;\n' + source @@ -415,14 +441,23 @@ class TestSuite(object): def __init__(self, root, strict_only, non_strict_only, unmarked_default, load_expectations): # TODO: derive from packagerConfig.py - self.test_root = path.join(root, 'test', 'suite') - self.lib_root = path.join(root, 'test', 'harness') + self.test_root = path.join(root, 'test') + self.lib_root = path.join(root, 'harness') self.strict_only = strict_only self.non_strict_only = non_strict_only self.unmarked_default = unmarked_default self.include_cache = { } self.expectations = TestExpectations(load_expectations) + def IsExcludedTest(self, path): + if path.startswith('annexB'): + return True; + if path.startswith('harness'): + return True; + if path.startswith('intl402'): + return True; + return False; + def Validate(self): if not path.exists(self.test_root): ReportError("No test repository found") @@ -471,25 +506,25 @@ class TestSuite(object): else: logging.warning("Unexpected path %s", full_path) rel_path = full_path - if self.ShouldRun(rel_path, tests) and not rel_path.startswith("intl402" + os.sep): + if self.ShouldRun(rel_path, tests) and not self.IsExcludedTest(rel_path): basename = path.basename(full_path)[:-3] - name = rel_path.split(path.sep)[:-1] + [basename] - if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(basename) >= 1: - print 'Excluded: ' + basename + name = rel_path.replace('.js', '') + if EXCLUDE_LIST.count(basename) >= 1 or self.expectations.testsToSkip.count(name) >= 1: + print 'Excluded: ' + rel_path else: if not self.non_strict_only: strict_case = TestCase(self, name, full_path, True) - if self.expectations.failingTests.count(basename) >= 1: + if self.expectations.failingTests.count(name) >= 1: strict_case.NegateResult() - if not strict_case.IsNoStrict(): + if not strict_case.IsNoStrict() and not strict_case.IsExperimental(): if strict_case.IsOnlyStrict() or \ self.unmarked_default in ['both', 'strict']: cases.append(strict_case) if not self.strict_only: non_strict_case = TestCase(self, name, full_path, False) - if self.expectations.failingTests.count(basename) >= 1: + if self.expectations.failingTests.count(name) >= 1: non_strict_case.NegateResult() - if not non_strict_case.IsOnlyStrict(): + if not non_strict_case.IsOnlyStrict() and not non_strict_case.IsExperimental(): if non_strict_case.IsNoStrict() or \ self.unmarked_default in ['both', 'non_strict']: cases.append(non_strict_case) @@ -564,10 +599,10 @@ class TestSuite(object): def Main(): - # Some date tests rely on being run in pacific time. # Uncomment the next line for more logging info. #logging.basicConfig(level=logging.DEBUG) - os.environ["TZ"] = "PST8PDT" + # Some date tests rely on being run in pacific time and the USA's locale: + os.environ["TZ"] = "America/Los_Angeles" # it *matters* that this is (7m8s) *East* of PST's nominal meridian ! os.environ["LANG"] = "en_US.UTF-8" os.environ["LC_TIME"] = "en_US.UTF-8" parser = BuildOptions() diff --git a/tests/auto/qml/ecmascripttests/testcase.pro b/tests/auto/qml/ecmascripttests/testcase.pro new file mode 100644 index 0000000000..5bf7ecd696 --- /dev/null +++ b/tests/auto/qml/ecmascripttests/testcase.pro @@ -0,0 +1,15 @@ +CONFIG += testcase +TARGET = tst_ecmascripttests +QT += testlib qml-private +macos:CONFIG -= app_bundle +SOURCES += tst_ecmascripttests.cpp qjstest/test262runner.cpp +HEADERS += qjstest/test262runner.h +DEFINES += SRCDIR=\\\"$$PWD\\\" + +# The ES test suite takes approximately 5 mins to run, on a fairly +# vanilla developer machine, so the default watchdog timer kills the +# test some of the time. Fix by raising time-out to 400s when +# invoking tst_ecmascripttests: +checkenv.name = QTEST_FUNCTION_TIMEOUT +checkenv.value = 500000 +QT_TOOL_ENV += checkenv diff --git a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp index 5d7009e7c8..27d2822762 100644 --- a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp +++ b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp @@ -30,44 +30,36 @@ #include <QtTest/QtTest> #include <QProcess> #include <QLibraryInfo> +#include <qjstest/test262runner.h> class tst_EcmaScriptTests : public QObject { Q_OBJECT + private slots: - void runTests_data(); - void runTests(); + void runInterpreted(); + void runJitted(); }; -void tst_EcmaScriptTests::runTests_data() +void tst_EcmaScriptTests::runInterpreted() { - QTest::addColumn<QString>("qmljsParameter"); - - QTest::newRow("jit") << QStringLiteral("--jit"); - // Not passing yet: QTest::newRow("interpreter") << QStringLiteral("--interpret"); +#if defined(Q_PROCESSOR_X86_64) + QDir::setCurrent(QLatin1String(SRCDIR)); + Test262Runner runner(QString(), "test262"); + runner.setFlags(Test262Runner::ForceBytecode|Test262Runner::WithTestExpectations|Test262Runner::Parallel|Test262Runner::Verbose); + bool result = runner.run(); + QVERIFY(result); +#endif } -void tst_EcmaScriptTests::runTests() +void tst_EcmaScriptTests::runJitted() { -#if defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64) - QFETCH(QString, qmljsParameter); - - QProcess process; - process.setProcessChannelMode(QProcess::ForwardedChannels); - process.setWorkingDirectory(QLatin1String(SRCDIR)); - process.setProgram("python"); - process.setArguments(QStringList() << "test262.py" << "--command=" + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs " + qmljsParameter << "--parallel" << "--with-test-expectations"); - - qDebug() << "Going to run" << process.program() << process.arguments() << "in" << process.workingDirectory(); - - process.start(); - QVERIFY(process.waitForStarted()); - const int timeoutInMSecs = 20 * 60 * 1000; - QVERIFY2(process.waitForFinished(timeoutInMSecs), "Tests did not terminate in time -- see output above for details"); - QVERIFY2(process.exitStatus() == QProcess::NormalExit, "Running the test harness failed -- see output above for details"); - QVERIFY2(process.exitCode() == 0, "Tests failed -- see output above for details"); -#else - QSKIP("Currently the ecmascript tests are only run on Linux/x86-64"); +#if defined(Q_PROCESSOR_X86_64) + QDir::setCurrent(QLatin1String(SRCDIR)); + Test262Runner runner(QString(), "test262"); + runner.setFlags(Test262Runner::ForceJIT|Test262Runner::WithTestExpectations|Test262Runner::Parallel|Test262Runner::Verbose); + bool result = runner.run(); + QVERIFY(result); #endif } diff --git a/tests/auto/qml/qjsengine/exporterror1.mjs b/tests/auto/qml/qjsengine/exporterror1.mjs new file mode 100644 index 0000000000..9ce9aa3b83 --- /dev/null +++ b/tests/auto/qml/qjsengine/exporterror1.mjs @@ -0,0 +1,2 @@ +let dummy = 0; +export { nothere } from "./testmodule.mjs"; diff --git a/tests/auto/qml/qjsengine/importerror1.mjs b/tests/auto/qml/qjsengine/importerror1.mjs new file mode 100644 index 0000000000..be33753c56 --- /dev/null +++ b/tests/auto/qml/qjsengine/importerror1.mjs @@ -0,0 +1,2 @@ +let dummy = 0; +import { nothere } from "./testmodule.mjs"; diff --git a/tests/auto/qml/qjsengine/modulewithlexicals.mjs b/tests/auto/qml/qjsengine/modulewithlexicals.mjs new file mode 100644 index 0000000000..c31cb5aef3 --- /dev/null +++ b/tests/auto/qml/qjsengine/modulewithlexicals.mjs @@ -0,0 +1,9 @@ +class Point { + constructor() {} +} + + +export function main() { + (new Point()); + return 10; +} diff --git a/tests/auto/qml/qjsengine/qjsengine.pro b/tests/auto/qml/qjsengine/qjsengine.pro index c9d78e22a0..b412e37727 100644 --- a/tests/auto/qml/qjsengine/qjsengine.pro +++ b/tests/auto/qml/qjsengine/qjsengine.pro @@ -4,5 +4,6 @@ QT += qml qml-private widgets testlib gui-private macx:CONFIG -= app_bundle SOURCES += tst_qjsengine.cpp RESOURCES += qjsengine.qrc +RESOURCES += testmodule.mjs modulewithlexicals.mjs importerror1.mjs exporterror1.mjs TESTDATA = script/* diff --git a/tests/auto/qml/qjsengine/testmodule.mjs b/tests/auto/qml/qjsengine/testmodule.mjs new file mode 100644 index 0000000000..df561c06a1 --- /dev/null +++ b/tests/auto/qml/qjsengine/testmodule.mjs @@ -0,0 +1,6 @@ + +export var value = 42; + +export function sideEffect() { + value = value + 1 +} diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index a3a2efd565..01b9465f58 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -39,6 +39,8 @@ #include <qqmlcomponent.h> #include <stdlib.h> #include <private/qv4alloca_p.h> +#include <private/qjsvalue_p.h> +#include <QScopeGuard> #ifdef Q_CC_MSVC #define NO_INLINE __declspec(noinline) @@ -64,6 +66,10 @@ private slots: void newArray(); void newArray_HooliganTask218092(); void newArray_HooliganTask233836(); + void toScriptValue_data(); + void toScriptValue(); + void toScriptValuenotroundtripped_data(); + void toScriptValuenotroundtripped(); void newVariant(); void newVariant_valueOfToString(); void newVariant_valueOfEnum(); @@ -92,6 +98,7 @@ private slots: void valueConversion_basic2(); void valueConversion_dateTime(); void valueConversion_regExp(); + void valueConversion_RegularExpression(); void castWithMultipleInheritance(); void collectGarbage(); void gcWithNestedDataStructure(); @@ -128,9 +135,13 @@ private slots: void JSONparse(); void arraySort(); void lookupOnDisappearingProperty(); + void arrayConcat(); + void recursiveBoundFunctions(); void qRegExpInport_data(); void qRegExpInport(); + void qRegularExpressionImport_data(); + void qRegularExpressionImport(); void dateRoundtripJSQtJS(); void dateRoundtripQtJSQt(); void dateConversionJSQt(); @@ -145,6 +156,7 @@ private slots: void array_join_QTBUG_53672(); void regexpLastMatch(); + void regexpLastIndex(); void indexedAccesses(); void prototypeChainGc(); @@ -172,6 +184,8 @@ private slots: void translateScriptUnicodeIdBased_data(); void translateScriptUnicodeIdBased(); void translateFromBuiltinCallback(); + void translationFilePath_data(); + void translationFilePath(); void installConsoleFunctions(); void logging(); @@ -198,8 +212,41 @@ private slots: void basicBlockMergeAfterLoopPeeling(); + void modulusCrash(); void malformedExpression(); + void scriptScopes(); + + void binaryNumbers(); + void octalNumbers(); + + void incrementAfterNewline(); + + void deleteInsideForIn(); + + void functionToString_data(); + void functionToString(); + + void stringReplace(); + + void protoChanges_QTBUG68369(); + void multilineStrings(); + + void throwError(); + void throwErrorObject(); + void returnError(); + void mathMinMax(); + + void importModule(); + void importModuleRelative(); + void importModuleWithLexicallyScopedVars(); + void importExportErrors(); + +public: + Q_INVOKABLE QJSValue throwingCppMethod1(); + Q_INVOKABLE void throwingCppMethod2(); + Q_INVOKABLE QJSValue throwingCppMethod3(); + signals: void testSignal(); }; @@ -438,17 +485,97 @@ void tst_QJSEngine::newArray_HooliganTask233836() } } +void tst_QJSEngine::toScriptValue_data() +{ + QTest::addColumn<QVariant>("input"); + + QTest::newRow("UnknownType") << QVariant(int(QMetaType::UnknownType), nullptr); + QTest::newRow("Nullptr") << QVariant(int(QMetaType::Nullptr), nullptr); + QTest::newRow("true") << QVariant(true); + QTest::newRow("false") << QVariant(false); + QTest::newRow("int") << QVariant(int(42)); + QTest::newRow("uint") << QVariant(uint(42)); + QTest::newRow("longlong") << QVariant(qlonglong(4242)); + QTest::newRow("ulonglong") << QVariant(qulonglong(4242)); + QTest::newRow("double") << QVariant(double(42.42)); + QTest::newRow("float") << QVariant(float(42.42)); + QTest::newRow("qstring") << QVariant(QString::fromLatin1("hello")); + QTest::newRow("qbytearray") << QVariant(QByteArray("hello")); + QTest::newRow("short") << QVariant(short('r')); + QTest::newRow("ushort") << QVariant(short('b')); + QTest::newRow("char") << QVariant(char('r')); + QTest::newRow("uchar") << QVariant(uchar('b')); + QTest::newRow("qchar") << QVariant(QString::fromUtf8("Ã¥").at(0)); + QTest::newRow("qdate") << QVariant(QDate(1925, 5, 8)); + QTest::newRow("qtime") << QVariant(QTime(4, 5, 6)); + QTest::newRow("qregularexpression") << QVariant(QRegularExpression(".*")); + QTest::newRow("qpointf") << QVariant(QPointF(42, 24)); + QTest::newRow("qvariantlist") << QVariant(QVariantList() << 42.24 << 5 << "hello"); + QTest::newRow("qvariantlist_point") << QVariant(QVariantList() << 42.24 << QPointF(42.24, 24.42) << QPointF(24.42, 42.24)); + QVariantMap vm; vm.insert("test", 55); vm.insert("abc", 42.42);; + QTest::newRow("qvariantmap") << QVariant(vm); + vm.clear(); vm.insert("point1", QPointF(42.24, 24.42)); vm.insert("point2", QPointF(42.24, 24.42)); + QTest::newRow("qvariantmap_point") << QVariant(vm); + QTest::newRow("qvariant") << QVariant(QVariant(42)); + QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>() << 1 << 2 << 3 << 4); + QTest::newRow("QVector<int>") << QVariant::fromValue(QVector<int>() << 1 << 2 << 3 << 4); + QTest::newRow("QList<QString>") << QVariant::fromValue(QVector<QString>() << "1" << "2" << "3" << "4"); + QTest::newRow("QStringList") << QVariant::fromValue(QStringList() << "1" << "2" << "3" << "4"); + QTest::newRow("QMap<QString, QString>") << QVariant::fromValue(QMap<QString, QString>{{ "1", "2" }, { "3", "4" }}); + QTest::newRow("QHash<QString, QString>") << QVariant::fromValue(QHash<QString, QString>{{ "1", "2" }, { "3", "4" }}); + QTest::newRow("QMap<QString, QPointF>") << QVariant::fromValue(QMap<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }}); + QTest::newRow("QHash<QString, QPointF>") << QVariant::fromValue(QHash<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }}); +} + +void tst_QJSEngine::toScriptValue() +{ + QFETCH(QVariant, input); + + QJSEngine engine; + QJSValue outputJS = engine.toScriptValue(input); + QVariant output = engine.fromScriptValue<QVariant>(outputJS); + + QCOMPARE(input, output); +} + +void tst_QJSEngine::toScriptValuenotroundtripped_data() +{ + QTest::addColumn<QVariant>("input"); + QTest::addColumn<QVariant>("output"); + + QTest::newRow("QList<QObject*>") << QVariant::fromValue(QList<QObject*>() << this) << QVariant(QVariantList() << QVariant::fromValue(this)); + QTest::newRow("QObjectList") << QVariant::fromValue(QObjectList() << this) << QVariant(QVariantList() << QVariant::fromValue(this)); + QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); + QTest::newRow("QVector<QPoint>") << QVariant::fromValue(QVector<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)); + QTest::newRow("VoidStar") << QVariant(int(QMetaType::VoidStar), nullptr) << QVariant(int(QMetaType::Nullptr), nullptr); + QTest::newRow("qregex") << QVariant(QRegExp(".*", Qt::CaseSensitive, QRegExp::RegExp2)) << QVariant(QRegularExpression(".*")); +} + +// This is almost the same as toScriptValue, but the inputs don't roundtrip to +// exactly the same value. +void tst_QJSEngine::toScriptValuenotroundtripped() +{ + QFETCH(QVariant, input); + QFETCH(QVariant, output); + + QJSEngine engine; + QJSValue outputJS = engine.toScriptValue(input); + QVariant actualOutput = engine.fromScriptValue<QVariant>(outputJS); + + QCOMPARE(actualOutput, output); +} + void tst_QJSEngine::newVariant() { QJSEngine eng; { QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2))); QVERIFY(!opaque.isUndefined()); - QCOMPARE(opaque.isVariant(), true); + QCOMPARE(opaque.isVariant(), false); QVERIFY(!opaque.isCallable()); QCOMPARE(opaque.isObject(), true); QVERIFY(!opaque.prototype().isUndefined()); - QCOMPARE(opaque.prototype().isVariant(), true); + QCOMPARE(opaque.prototype().isVariant(), false); QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque)); } } @@ -462,7 +589,7 @@ void tst_QJSEngine::newVariant_valueOfToString() QJSValue value = object.property("valueOf").callWithInstance(object); QVERIFY(value.isObject()); QVERIFY(value.strictlyEquals(object)); - QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)")); + QCOMPARE(object.toString(), QString::fromLatin1("QPoint(10, 20)")); } } @@ -480,23 +607,27 @@ void tst_QJSEngine::newVariant_valueOfEnum() void tst_QJSEngine::newRegExp() { QJSEngine eng; - QJSValue rexp = eng.toScriptValue(QRegExp("foo")); - QVERIFY(!rexp.isUndefined()); - QCOMPARE(rexp.isRegExp(), true); - QCOMPARE(rexp.isObject(), true); - QCOMPARE(rexp.isCallable(), false); - // prototype should be RegExp.prototype - QVERIFY(!rexp.prototype().isUndefined()); - QCOMPARE(rexp.prototype().isObject(), true); - QCOMPARE(rexp.prototype().isRegExp(), true); - // Get [[Class]] internal property of RegExp Prototype Object. - // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". - // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". - QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); - QCOMPARE(r.toString(), QString::fromLatin1("[object RegExp]")); - QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); - - QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); + QJSValue rexps[] = { + eng.toScriptValue(QRegularExpression("foo")), + eng.toScriptValue(QRegExp("foo")) + }; + for (const auto &rexp : rexps) { + QVERIFY(!rexp.isUndefined()); + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isObject(), true); + QCOMPARE(rexp.isCallable(), false); + // prototype should be RegExp.prototype + QVERIFY(!rexp.prototype().isUndefined()); + QCOMPARE(rexp.prototype().isObject(), true); + // Get [[Class]] internal property of RegExp Prototype Object. + // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". + // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". + QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); + QCOMPARE(r.toString(), QString::fromLatin1("[object Object]")); + QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); + + QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); + } } void tst_QJSEngine::jsRegExp() @@ -519,8 +650,7 @@ void tst_QJSEngine::jsRegExp() QVERIFY(r2.strictlyEquals(r)); QJSValue r3 = rxCtor.call(QJSValueList() << r << "gim"); - QVERIFY(r3.isError()); - QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another + QVERIFY(!r3.isError()); QJSValue r4 = rxCtor.call(QJSValueList() << "foo" << "gim"); QVERIFY(r4.isRegExp()); @@ -528,9 +658,7 @@ void tst_QJSEngine::jsRegExp() QJSValue r5 = rxCtor.callAsConstructor(QJSValueList() << r); QVERIFY(r5.isRegExp()); QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim")); - // In JSC, constructing a RegExp from another produces the same identical object. - // This is different from SpiderMonkey and old back-end. - QVERIFY(!r5.strictlyEquals(r)); + QVERIFY(r5.strictlyEquals(r)); // See ECMA-262 Section 15.10.4.1, "new RegExp(pattern, flags)". QJSValue r6 = rxCtor.callAsConstructor(QJSValueList() << "foo" << "bar"); @@ -612,10 +740,10 @@ void tst_QJSEngine::newQObject() QObject temp; { - QJSValue qobject = eng.newQObject(0); + QJSValue qobject = eng.newQObject(nullptr); QCOMPARE(qobject.isNull(), true); QCOMPARE(qobject.isObject(), false); - QCOMPARE(qobject.toQObject(), (QObject *)0); + QCOMPARE(qobject.toQObject(), (QObject *)nullptr); } { QJSValue qobject = eng.newQObject(&temp); @@ -636,7 +764,7 @@ void tst_QJSEngine::newQObject_ownership() QJSEngine eng; { QPointer<QObject> ptr = new QObject(); - QVERIFY(ptr != 0); + QVERIFY(ptr != nullptr); { QJSValue v = eng.newQObject(ptr); } @@ -647,7 +775,7 @@ void tst_QJSEngine::newQObject_ownership() } { QPointer<QObject> ptr = new QObject(this); - QVERIFY(ptr != 0); + QVERIFY(ptr != nullptr); { QJSValue v = eng.newQObject(ptr); } @@ -662,11 +790,11 @@ void tst_QJSEngine::newQObject_ownership() QJSValue v = eng.newQObject(child); QCOMPARE(v.toQObject(), child); delete parent; - QCOMPARE(v.toQObject(), (QObject *)0); + QCOMPARE(v.toQObject(), (QObject *)nullptr); } { QPointer<QObject> ptr = new QObject(); - QVERIFY(ptr != 0); + QVERIFY(ptr != nullptr); { QJSValue v = eng.newQObject(ptr); } @@ -679,18 +807,18 @@ void tst_QJSEngine::newQObject_ownership() { QObject *parent = new QObject(); QPointer<QObject> child = new QObject(parent); - QVERIFY(child != 0); + QVERIFY(child != nullptr); { QJSValue v = eng.newQObject(child); } eng.collectGarbage(); // has parent, so it should be like QtOwnership - QVERIFY(child != 0); + QVERIFY(child != nullptr); delete parent; } { QPointer<QObject> ptr = new QObject(); - QVERIFY(ptr != 0); + QVERIFY(ptr != nullptr); { QQmlEngine::setObjectOwnership(ptr.data(), QQmlEngine::CppOwnership); QJSValue v = eng.newQObject(ptr); @@ -732,23 +860,18 @@ public: }; Q_ENUMS(Enum1 Enum2) - Q_INVOKABLE TestQMetaObject() - : m_called(1) { - } + Q_INVOKABLE TestQMetaObject() {} Q_INVOKABLE TestQMetaObject(int) - : m_called(2) { - } + : m_called(2) {} Q_INVOKABLE TestQMetaObject(QString) - : m_called(3) { - } + : m_called(3) {} Q_INVOKABLE TestQMetaObject(QString, int) - : m_called(4) { - } + : m_called(4) {} int called() const { return m_called; } private: - int m_called; + int m_called = 1; }; void tst_QJSEngine::newQObjectPropertyCache() @@ -939,12 +1062,14 @@ void tst_QJSEngine::globalObjectProperties_enumerate() << "decodeURIComponent" << "Date" << "Array" + << "Symbol" << "escape" << "unescape" << "SyntaxError" << "undefined" << "JSON" << "ArrayBuffer" + << "SharedArrayBuffer" << "DataView" << "Int8Array" << "Uint8Array" @@ -955,6 +1080,14 @@ void tst_QJSEngine::globalObjectProperties_enumerate() << "Uint32Array" << "Float32Array" << "Float64Array" + << "WeakSet" + << "Set" + << "WeakMap" + << "Map" + << "Reflect" + << "Proxy" + << "Atomics" + << "Promise" ; QSet<QString> actualNames; { @@ -1297,8 +1430,8 @@ void tst_QJSEngine::errorMessage_QT679() struct Foo { public: - int x, y; - Foo() : x(-1), y(-1) { } + int x = -1, y = -1; + Foo() {} }; Q_DECLARE_METATYPE(Foo) @@ -1374,7 +1507,7 @@ void tst_QJSEngine::valueConversion_basic() QCOMPARE(eng.fromScriptValue<QChar>(eng.toScriptValue(c)), c); } - QVERIFY(eng.toScriptValue(static_cast<void *>(0)).isNull()); + QVERIFY(eng.toScriptValue(static_cast<void *>(nullptr)).isNull()); } void tst_QJSEngine::valueConversion_QVariant() @@ -1454,13 +1587,13 @@ void tst_QJSEngine::valueConversion_QVariant() { QVariant var = qVariantFromValue(QPoint(123, 456)); QJSValue val = eng.toScriptValue(var); - QVERIFY(val.isVariant()); + QVERIFY(!val.isVariant()); QCOMPARE(val.toVariant(), var); } QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123)); - QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull()); + QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, nullptr)).isNull()); QVERIFY(eng.toScriptValue(QVariant::fromValue(nullptr)).isNull()); { @@ -1560,6 +1693,28 @@ void tst_QJSEngine::valueConversion_regExp() } } +void tst_QJSEngine::valueConversion_RegularExpression() +{ + QJSEngine eng; + { + QRegularExpression in = QRegularExpression("foo"); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QRegularExpression out = qjsvalue_cast<QRegularExpression>(val); + QCOMPARE(out.pattern(), in.pattern()); + QCOMPARE(out.patternOptions(), in.patternOptions()); + } + { + QRegularExpression in = QRegularExpression("foo", + QRegularExpression::CaseInsensitiveOption); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QCOMPARE(qjsvalue_cast<QRegularExpression>(val), in); + QRegularExpression out = qjsvalue_cast<QRegularExpression>(val); + QCOMPARE(out.patternOptions(), in.patternOptions()); + } +} + Q_DECLARE_METATYPE(QGradient) Q_DECLARE_METATYPE(QGradient*) Q_DECLARE_METATYPE(QLinearGradient) @@ -1571,7 +1726,7 @@ class Klazz : public QWidget, Q_INTERFACES(QGraphicsItem) Q_OBJECT public: - Klazz(QWidget *parent = 0) : QWidget(parent) { } + Klazz(QWidget *parent = nullptr) : QWidget(parent) { } virtual QRectF boundingRect() const { return QRectF(); } virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { } }; @@ -1600,7 +1755,7 @@ void tst_QJSEngine::collectGarbage() a = eng.newObject(); a = eng.newObject(); QPointer<QObject> ptr = new QObject(); - QVERIFY(ptr != 0); + QVERIFY(ptr != nullptr); (void)eng.newQObject(ptr); eng.collectGarbage(); if (ptr) @@ -1720,6 +1875,22 @@ void tst_QJSEngine::stacktrace() QJSValue result2 = eng.evaluate(script2, fileName); QVERIFY(!result2.isError()); QVERIFY(result2.isString()); + + { + QString script3 = QString::fromLatin1( + "'use strict'\n" + "function throwUp() { throw new Error('up') }\n" + "function indirectlyThrow() { return throwUp() }\n" + "indirectlyThrow()\n" + ); + QJSValue result3 = eng.evaluate(script3); + QVERIFY(result3.isError()); + QJSValue stack = result3.property("stack"); + QVERIFY(stack.isString()); + QString stackTrace = stack.toString(); + QVERIFY(!stackTrace.contains(QStringLiteral("indirectlyThrow"))); + QVERIFY(stackTrace.contains(QStringLiteral("elide"))); + } } void tst_QJSEngine::numberParsing_data() @@ -2893,6 +3064,8 @@ void tst_QJSEngine::reentrancy_objectCreation() QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')"); QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2)); QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1)); + QCOMPARE(qjsvalue_cast<QRegularExpression>(r1), qjsvalue_cast<QRegularExpression>(r2)); + QCOMPARE(qjsvalue_cast<QRegularExpression>(r2), qjsvalue_cast<QRegularExpression>(r1)); } { QJSValue o1 = eng1.newQObject(temp); @@ -2997,7 +3170,7 @@ void tst_QJSEngine::arraySort() void tst_QJSEngine::lookupOnDisappearingProperty() { QJSEngine eng; - QJSValue func = eng.evaluate("(function(){\"use strict\"; return eval(\"function(obj) { return obj.someProperty; }\")})()"); + QJSValue func = eng.evaluate("(function(){\"use strict\"; return eval(\"(function(obj) { return obj.someProperty; })\")})()"); QVERIFY(func.isCallable()); QJSValue o = eng.newObject(); @@ -3010,6 +3183,31 @@ void tst_QJSEngine::lookupOnDisappearingProperty() QVERIFY(func.call(QJSValueList()<< o).isUndefined()); } +void tst_QJSEngine::arrayConcat() +{ + QJSEngine eng; + QJSValue v = eng.evaluate("var x = [1, 2, 3, 4, 5, 6];" + "var y = [];" + "for (var i = 0; i < 5; ++i)" + " x.shift();" + "for (var i = 10; i < 13; ++i)" + " x.push(i);" + "x.toString();"); + QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12")); +} + +void tst_QJSEngine::recursiveBoundFunctions() +{ + + QJSEngine eng; + QJSValue v = eng.evaluate("function foo(x, y, z)" + "{ return this + x + y + z; }" + "var bar = foo.bind(-1, 10);" + "var baz = bar.bind(-2, 20);" + "baz(30)"); + QCOMPARE(v.toInt(), 59); +} + static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } void tst_QJSEngine::qRegExpInport_data() @@ -3063,6 +3261,56 @@ void tst_QJSEngine::qRegExpInport() } } +void tst_QJSEngine::qRegularExpressionImport_data() +{ + QTest::addColumn<QRegularExpression>("rx"); + QTest::addColumn<QString>("string"); + QTest::addColumn<QString>("matched"); + + QTest::newRow("normal") << QRegularExpression("(test|foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("normal2") << QRegularExpression("(Test|Foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive") << QRegularExpression("(test|foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive2") << QRegularExpression("(Test|Foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo"; + QTest::newRow("b(a*)(b*)") << QRegularExpression("b(a*)(b*)", QRegularExpression::CaseInsensitiveOption) << "aaabbBbaAabaAaababaaabbaaab"; + QTest::newRow("greedy") << QRegularExpression("a*(a*)", QRegularExpression::CaseInsensitiveOption) << "aaaabaaba"; + QTest::newRow("wildcard") << QRegularExpression(".*\\.txt") << "file.txt"; + QTest::newRow("wildcard 2") << QRegularExpression("a.b\\.txt") << "ab.txt abb.rtc acb.txt"; + QTest::newRow("slash") << QRegularExpression("g/.*/s", QRegularExpression::CaseInsensitiveOption) << "string/string/string"; + QTest::newRow("slash2") << QRegularExpression("g / .* / s", QRegularExpression::CaseInsensitiveOption) << "string / string / string"; + QTest::newRow("fixed") << QRegularExpression("a\\*aa\\.a\\(ba\\)\\*a\\\\ba", QRegularExpression::CaseInsensitiveOption) << "aa*aa.a(ba)*a\\ba"; + QTest::newRow("fixed insensitive") << QRegularExpression("A\\*A", QRegularExpression::CaseInsensitiveOption) << "a*A A*a A*A a*a"; + QTest::newRow("fixed sensitive") << QRegularExpression("A\\*A") << "a*A A*a A*A a*a"; + QTest::newRow("html") << QRegularExpression("<b>(.*)</b>") << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("html minimal") << QRegularExpression("^<b>(.*)</b>$") << "<b>bold</b><i>italic</i><b>bold</b>"; + QTest::newRow("aaa") << QRegularExpression("a{2,5}") << "aAaAaaaaaAa"; + QTest::newRow("aaa minimal") << QRegularExpression("^a{2,5}$") << "aAaAaaaaaAa"; + QTest::newRow("minimal") << QRegularExpression("^.*\\} [*8]$") << "}?} ?} *"; + QTest::newRow(".? minimal") << QRegularExpression("^.?$") << ".?"; + QTest::newRow(".+ minimal") << QRegularExpression("^.+$") << ".+"; + QTest::newRow("[.?] minimal") << QRegularExpression("^[.?]$") << ".?"; + QTest::newRow("[.+] minimal") << QRegularExpression("^[.+]$") << ".+"; +} + +void tst_QJSEngine::qRegularExpressionImport() +{ + QFETCH(QRegularExpression, rx); + QFETCH(QString, string); + + QJSEngine eng; + QJSValue rexp; + rexp = eng.toScriptValue(rx); + + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isCallable(), false); + + QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })"); + QJSValue result = func.call(QJSValueList() << string << rexp); + + const QRegularExpressionMatch match = rx.match(string); + for (int i = 0; i <= match.lastCapturedIndex(); i++) + QCOMPARE(result.property(i).toString(), match.captured(i)); +} + // QScriptValue::toDateTime() returns a local time, whereas JS dates // are always stored as UTC. Qt Script must respect the current time // zone, and correctly adjust for daylight saving time that may be in @@ -3072,7 +3320,7 @@ void tst_QJSEngine::dateRoundtripJSQtJS() #ifdef Q_OS_WIN QSKIP("This test fails on Windows due to a bug in QDateTime."); #endif - uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t(); + qint64 secs = QDateTime(QDate(2009, 1, 1)).toUTC().toSecsSinceEpoch(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0)); @@ -3105,7 +3353,7 @@ void tst_QJSEngine::dateConversionJSQt() #ifdef Q_OS_WIN QSKIP("This test fails on Windows due to a bug in QDateTime."); #endif - uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t(); + qint64 secs = QDateTime(QDate(2009, 1, 1)).toUTC().toSecsSinceEpoch(); QJSEngine eng; for (int i = 0; i < 8000; ++i) { QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0)); @@ -3153,10 +3401,9 @@ class ThreadedTestEngine : public QThread { Q_OBJECT; public: - int result; + int result = 0; - ThreadedTestEngine() - : result(0) {} + ThreadedTestEngine() {} void run() { QJSEngine firstEngine; @@ -3180,9 +3427,7 @@ void tst_QJSEngine::threadedEngine() void tst_QJSEngine::functionDeclarationsInConditionals() { - // Even though this is bad practice (and test262 covers it with best practices test cases), - // we do allow for function declarations in if and while statements, as unfortunately that's - // real world JavaScript. (QTBUG-33064 for example) + // Visibility of function declarations inside blocks is limited to the block QJSEngine eng; QJSValue result = eng.evaluate("if (true) {\n" " function blah() { return false; }\n" @@ -3190,8 +3435,7 @@ void tst_QJSEngine::functionDeclarationsInConditionals() " function blah() { return true; }\n" "}\n" "blah();"); - QVERIFY(result.isBool()); - QCOMPARE(result.toBool(), true); + QVERIFY(result.isError()); } void tst_QJSEngine::arrayPop_QTBUG_35979() @@ -3289,6 +3533,28 @@ void tst_QJSEngine::regexpLastMatch() } +void tst_QJSEngine::regexpLastIndex() +{ + QJSEngine eng; + QJSValue result; + result = eng.evaluate("function test(text, rx) {" + " var res;" + " while (res = rx.exec(text)) { " + " return true;" + " }" + " return false;" + " }" + "function tester(text) {" + " return test(text, /,\\s*/g);" + "}"); + QVERIFY(!result.isError()); + + result = eng.evaluate("tester(\", \\n\");"); + QVERIFY(result.toBool()); + result = eng.evaluate("tester(\", \\n\");"); + QVERIFY(result.toBool()); +} + void tst_QJSEngine::indexedAccesses() { QJSEngine engine; @@ -3308,7 +3574,7 @@ void tst_QJSEngine::prototypeChainGc() QJSValue getProto = engine.evaluate("Object.getPrototypeOf"); - QJSValue factory = engine.evaluate("function() { return Object.create(Object.create({})); }"); + QJSValue factory = engine.evaluate("(function() { return Object.create(Object.create({})); })"); QVERIFY(factory.isCallable()); QJSValue obj = factory.call(); engine.collectGarbage(); @@ -3347,7 +3613,7 @@ void tst_QJSEngine::dynamicProperties() QQmlEngine qmlEngine; QQmlComponent component(&qmlEngine); component.setData("import QtQml 2.0; QtObject { property QtObject subObject: QtObject {} }", QUrl()); - QObject *root = component.create(0); + QObject *root = component.create(nullptr); QVERIFY(root); QVERIFY(qmlContext(root)); @@ -3578,7 +3844,7 @@ void tst_QJSEngine::translateScript() QJSEngine engine; TranslationScope tranScope(":/translations/translatable_la"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation); } @@ -3587,7 +3853,7 @@ void tst_QJSEngine::translateScript_crossScript() { QJSEngine engine; TranslationScope tranScope(":/translations/translatable_la"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QString fileName = QString::fromLatin1("translatable.js"); QString fileName2 = QString::fromLatin1("translatable2.js"); @@ -3609,7 +3875,7 @@ void tst_QJSEngine::translateScript_trNoOp() { QJSEngine engine; TranslationScope tranScope(":/translations/translatable_la"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined()); QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One")); @@ -3623,7 +3889,7 @@ void tst_QJSEngine::translateScript_callQsTrFromCpp() { QJSEngine engine; TranslationScope tranScope(":/translations/translatable_la"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); // There is no context, but it shouldn't crash QCOMPARE(engine.globalObject().property("qsTr").call(QJSValueList() << "One").toString(), QString::fromLatin1("One")); @@ -3656,7 +3922,7 @@ void tst_QJSEngine::translateWithInvalidArgs() QFETCH(QString, expression); QFETCH(QString, expectedError); QJSEngine engine; - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QJSValue result = engine.evaluate(expression); QVERIFY(result.isError()); QCOMPARE(result.toString(), expectedError); @@ -3692,7 +3958,7 @@ void tst_QJSEngine::translationContext() TranslationScope tranScope(":/translations/translatable_la"); QJSEngine engine; - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QFETCH(QString, path); QFETCH(QString, text); @@ -3707,7 +3973,7 @@ void tst_QJSEngine::translateScriptIdBased() QJSEngine engine; TranslationScope tranScope(":/translations/idtranslatable_la"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QString fileName = QString::fromLatin1("idtranslatable.js"); @@ -3789,7 +4055,7 @@ void tst_QJSEngine::translateScriptUnicode() QJSEngine engine; TranslationScope tranScope(":/translations/translatable-unicode"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation); } @@ -3819,7 +4085,7 @@ void tst_QJSEngine::translateScriptUnicodeIdBased() QJSEngine engine; TranslationScope tranScope(":/translations/idtranslatable-unicode"); - engine.installTranslatorFunctions(); + engine.installExtensions(QJSEngine::TranslationExtension); QCOMPARE(engine.evaluate(expression).toString(), expectedTranslation); } @@ -3827,7 +4093,7 @@ void tst_QJSEngine::translateScriptUnicodeIdBased() void tst_QJSEngine::translateFromBuiltinCallback() { QJSEngine eng; - eng.installTranslatorFunctions(); + eng.installExtensions(QJSEngine::TranslationExtension); // Callback has no translation context. eng.evaluate("function foo() { qsTr('foo'); }"); @@ -3839,6 +4105,73 @@ void tst_QJSEngine::translateFromBuiltinCallback() eng.evaluate("[10,20].forEach(foo)", "script.js"); } +void tst_QJSEngine::translationFilePath_data() +{ + QTest::addColumn<QString>("filename"); + + QTest::newRow("relative") << QStringLiteral("script.js"); + QTest::newRow("absolute unix") << QStringLiteral("/script.js"); + QTest::newRow("absolute /windows/") << QStringLiteral("c:/script.js"); +#ifdef Q_OS_WIN + QTest::newRow("absolute \\windows\\") << QStringLiteral("c:\\script.js"); +#endif + QTest::newRow("relative url") << QStringLiteral("file://script.js"); + QTest::newRow("absolute url unix") << QStringLiteral("file:///script.js"); + QTest::newRow("absolute url windows") << QStringLiteral("file://c:/script.js"); +} + +class DummyTranslator : public QTranslator +{ + Q_OBJECT + +public: + DummyTranslator(const char *sourceText) + : srcTxt(sourceText) + {} + + QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override + { + Q_UNUSED(disambiguation); + Q_UNUSED(n); + + if (srcTxt == sourceText) + ctxt = QByteArray(context); + + return QString(sourceText); + } + + bool isEmpty() const override + { + return false; + } + + const char *sourceText() const + { return srcTxt.constData(); } + + QByteArray context() const + { return ctxt; } + +private: + QByteArray srcTxt; + mutable QByteArray ctxt; +}; + +void tst_QJSEngine::translationFilePath() +{ + QFETCH(QString, filename); + + DummyTranslator translator("some text"); + QCoreApplication::installTranslator(&translator); + QByteArray scriptContent = QByteArray("qsTr('%1')").replace("%1", translator.sourceText()); + + QJSEngine engine; + engine.installExtensions(QJSEngine::TranslationExtension); + QJSValue result = engine.evaluate(scriptContent, filename); + QCOMPARE(translator.context(), QByteArray("script")); + + QCoreApplication::removeTranslator(&translator); +} + void tst_QJSEngine::installConsoleFunctions() { QJSEngine engine; @@ -4033,9 +4366,9 @@ class TestObject : public QObject { Q_OBJECT public: - TestObject() : called(false) {} + TestObject() {} - bool called; + bool called = false; Q_INVOKABLE void callMe(QQmlV4Function *) { called = true; @@ -4089,12 +4422,372 @@ void tst_QJSEngine::basicBlockMergeAfterLoopPeeling() } +void tst_QJSEngine::modulusCrash() +{ + QJSEngine engine; + QJSValue result = engine.evaluate( + "var a = -2147483648; var b = -1; var c = a % b; c;" + ); + QVERIFY(result.isNumber() && result.toNumber() == 0.); +} + void tst_QJSEngine::malformedExpression() { QJSEngine engine; engine.evaluate("5%55555&&5555555\n7-0"); } +void tst_QJSEngine::scriptScopes() +{ + QJSEngine engine; + + QJSValue def = engine.evaluate("'use strict'; function foo() { return 42 }"); + QVERIFY(!def.isError()); + QJSValue globalObject = engine.globalObject(); + QJSValue foo = globalObject.property("foo"); + QVERIFY(foo.isObject()); + QVERIFY(foo.isCallable()); + + QJSValue use = engine.evaluate("'use strict'; foo()"); + QVERIFY(use.isNumber()); + QCOMPARE(use.toInt(), 42); +} + +void tst_QJSEngine::binaryNumbers() +{ + QJSEngine engine; + + QJSValue result = engine.evaluate("0b1001"); + QVERIFY(result.isNumber()); + QVERIFY(result.toNumber() == 9); + + result = engine.evaluate("0B1001"); + QVERIFY(result.isNumber()); + QVERIFY(result.toNumber() == 9); + + result = engine.evaluate("0b2"); + QVERIFY(result.isError()); +} + +void tst_QJSEngine::octalNumbers() +{ + QJSEngine engine; + + QJSValue result = engine.evaluate("0o11"); + QVERIFY(result.isNumber()); + QVERIFY(result.toNumber() == 9); + + result = engine.evaluate("0O11"); + QVERIFY(result.isNumber()); + QVERIFY(result.toNumber() == 9); + + result = engine.evaluate("0o9"); + QVERIFY(result.isError()); +} + +void tst_QJSEngine::incrementAfterNewline() +{ + QJSEngine engine; + + QJSValue result = engine.evaluate("var x = 0; if (\n++x) x; else -x;"); + QVERIFY(result.isNumber()); + QVERIFY(result.toNumber() == 1); + + result = engine.evaluate("var x = 0; if (\n--x) x; else -x;"); + QVERIFY(result.isNumber()); + QVERIFY(result.toNumber() == -1); +} + +void tst_QJSEngine::deleteInsideForIn() +{ + QJSEngine engine; + + QJSValue iterationCount = engine.evaluate( + "var o = { a: 1, b: 2, c: 3, d: 4};\n" + "var count = 0;\n" + "for (var prop in o) { count++; delete o[prop]; }\n" + "count"); + QVERIFY(iterationCount.isNumber()); + QCOMPARE(iterationCount.toInt(), 4); +} + +void tst_QJSEngine::functionToString_data() +{ + QTest::addColumn<QString>("source"); + QTest::addColumn<QString>("expectedString"); + + QTest::newRow("named function") << QString::fromLatin1("function f() {}; f.toString()") + << QString::fromLatin1("function f() { [native code] }"); + QTest::newRow("anonymous function") << QString::fromLatin1("(function() {}).toString()") + << QString::fromLatin1("function() { [native code] }"); +} + +// Tests that function.toString() prints the function's name. +void tst_QJSEngine::functionToString() +{ + QFETCH(QString, source); + QFETCH(QString, expectedString); + + QJSEngine engine; + engine.installExtensions(QJSEngine::AllExtensions); + QJSValue evaluationResult = engine.evaluate(source); + QVERIFY(!evaluationResult.isError()); + QCOMPARE(evaluationResult.toString(), expectedString); +} + +void tst_QJSEngine::stringReplace() +{ + QJSEngine engine; + + QJSValue val = engine.evaluate("'x'.replace('x', '$1')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$1")); + + val = engine.evaluate("'x'.replace('x', '$10')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$10")); + + val = engine.evaluate("'x'.replace('x', '$01')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$01")); + + val = engine.evaluate("'x'.replace('x', '$0')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$0")); + + val = engine.evaluate("'x'.replace('x', '$00')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$00")); + + val = engine.evaluate("'x'.replace(/(x)/, '$1')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("x")); + + val = engine.evaluate("'x'.replace(/(x)/, '$01')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("x")); + + val = engine.evaluate("'x'.replace(/(x)/, '$2')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$2")); + + val = engine.evaluate("'x'.replace(/(x)/, '$02')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$02")); + + val = engine.evaluate("'x'.replace(/(x)/, '$0')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$0")); + + val = engine.evaluate("'x'.replace(/(x)/, '$00')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("$00")); + + val = engine.evaluate("'x'.replace(/()()()()()()()()()(x)/, '$11')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("1")); + + val = engine.evaluate("'x'.replace(/()()()()()()()()()(x)/, '$10')"); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), QString("x")); +} + +void tst_QJSEngine::protoChanges_QTBUG68369() +{ + QJSEngine engine; + QJSValue ok = engine.evaluate( + "var o = { x: true };" + "var p1 = {};" + "var p2 = {};" + "o.__proto__ = p1;" + "o.__proto__ = p2;" + "o.__proto__ = p1;" + "p1.y = true;" + "o.y" + ); + QVERIFY(ok.toBool() == true); +} + +void tst_QJSEngine::multilineStrings() +{ + QJSEngine engine; + QJSValue result = engine.evaluate( + "var x = `a\nb`; x;" + ); + QVERIFY(result.isString()); + QVERIFY(result.toString() == QStringLiteral("a\nb")); + +} + +void tst_QJSEngine::throwError() +{ + QJSEngine engine; + QJSValue wrappedThis = engine.newQObject(this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + engine.globalObject().setProperty("testCase", wrappedThis); + + QJSValue result = engine.evaluate( + "function test(){\n" + "try {\n" + " return testCase.throwingCppMethod1();\n" + "} catch (error) {\n" + " return error;\n" + "}\n" + "return \"not reached!\";\n" + "}\n" + "test();" + ); + QVERIFY(result.isError()); + QCOMPARE(result.errorType(), QJSValue::GenericError); + QCOMPARE(result.property("lineNumber").toString(), "3"); + QCOMPARE(result.property("message").toString(), "blub"); + QVERIFY(!result.property("stack").isUndefined()); +} + +void tst_QJSEngine::throwErrorObject() +{ + QJSEngine engine; + QJSValue wrappedThis = engine.newQObject(this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + engine.globalObject().setProperty("testCase", wrappedThis); + + QJSValue result = engine.evaluate( + "function test(){\n" + " try {\n" + " testCase.throwingCppMethod2();\n" + " } catch (error) {\n" + " if (error instanceof TypeError) {\n" + " return error;\n" + " }\n" + " }\n" + " return null;\n" + "}\n" + "test();" + ); + QVERIFY(result.isError()); + QCOMPARE(result.errorType(), QJSValue::TypeError); + QCOMPARE(result.property("lineNumber").toString(), "3"); + QCOMPARE(result.property("message").toString(), "Wrong type"); + QVERIFY(!result.property("stack").isUndefined()); +} + +void tst_QJSEngine::returnError() +{ + QJSEngine engine; + QJSValue wrappedThis = engine.newQObject(this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + engine.globalObject().setProperty("testCase", wrappedThis); + + QJSValue result = engine.evaluate("testCase.throwingCppMethod3()"); + QVERIFY(result.isError()); + QCOMPARE(result.errorType(), QJSValue::EvalError); + QCOMPARE(result.property("lineNumber").toString(), "1"); + QCOMPARE(result.property("message").toString(), "Something is wrong"); + QVERIFY(!result.property("stack").isUndefined()); +} + +QJSValue tst_QJSEngine::throwingCppMethod1() +{ + qjsEngine(this)->throwError("blub"); + return QJSValue(47); +} + +void tst_QJSEngine::throwingCppMethod2() +{ + qjsEngine(this)->throwError(QJSValue::TypeError, "Wrong type"); +} + +QJSValue tst_QJSEngine::throwingCppMethod3() +{ + return qjsEngine(this)->newErrorObject(QJSValue::EvalError, "Something is wrong"); +} + +void tst_QJSEngine::mathMinMax() +{ + QJSEngine engine; + + QJSValue result = engine.evaluate("var a = .5; Math.min(1, 2, 3.5 + a, '5')"); + QCOMPARE(result.toNumber(), 1.0); + QVERIFY(QJSValuePrivate::getValue(&result) != nullptr); + QVERIFY(QJSValuePrivate::getValue(&result)->isInteger()); + + result = engine.evaluate("var a = .5; Math.max('0', 1, 2, 3.5 + a)"); + QCOMPARE(result.toNumber(), 4.0); + QVERIFY(QJSValuePrivate::getValue(&result) != nullptr); + QVERIFY(QJSValuePrivate::getValue(&result)->isInteger()); +} + +void tst_QJSEngine::importModule() +{ + // This is just a basic test for the API. Primary test coverage is via the ES test suite. + QJSEngine engine; + QJSValue ns = engine.importModule(QStringLiteral(":/testmodule.mjs")); + QCOMPARE(ns.property("value").toInt(), 42); + ns.property("sideEffect").call(); + + // Make sure that importing a second time will return the same instance. + QJSValue secondNamespace = engine.importModule(QStringLiteral(":/testmodule.mjs")); + QCOMPARE(secondNamespace.property("value").toInt(), 43); +} + +void tst_QJSEngine::importModuleRelative() +{ + const QString oldWorkingDirectory = QDir::currentPath(); + auto workingDirectoryGuard = qScopeGuard([oldWorkingDirectory]{ + QDir::setCurrent(oldWorkingDirectory); + }); + + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + QDir::setCurrent(tempDir.path()); + + { + QFile f(QStringLiteral("relativemodule.mjs")); + QVERIFY(f.open(QIODevice::WriteOnly|QIODevice::Truncate)); + f.write(QByteArrayLiteral("var value = 100; export { value }; export function change() { value = value + 1 }")); + } + + QJSEngine engine; + + { + QJSValue module = engine.importModule(QStringLiteral("relativemodule.mjs")); + QVERIFY2(!module.isError(), qPrintable(module.toString())); + QCOMPARE(module.property("value").toInt(), 100); + + module.property("change").call(); + } + + { + QJSValue sameModule = engine.importModule(tempDir.filePath(QStringLiteral("relativemodule.mjs"))); + QVERIFY2(!sameModule.isError(), qPrintable(sameModule.toString())); + QCOMPARE(sameModule.property("value").toInt(), 101); + } +} + +void tst_QJSEngine::importModuleWithLexicallyScopedVars() +{ + QJSEngine engine; + QJSValue ns = engine.importModule(QStringLiteral(":/modulewithlexicals.mjs")); + QVERIFY2(!ns.isError(), qPrintable(ns.toString())); + QCOMPARE(ns.property("main").call().toInt(), 10); +} + +void tst_QJSEngine::importExportErrors() +{ + { + QJSEngine engine; + QJSValue result = engine.importModule(QStringLiteral(":/importerror1.mjs")); + QVERIFY(result.isError()); + QCOMPARE(result.property("lineNumber").toInt(), 2); + } + { + QJSEngine engine; + QJSValue result = engine.importModule(QStringLiteral(":/exporterror1.mjs")); + QVERIFY(result.isError()); + QCOMPARE(result.property("lineNumber").toInt(), 2); + } +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qjsonbinding/qjsonbinding.pro b/tests/auto/qml/qjsonbinding/qjsonbinding.pro index 75b48aa854..f7a185d27a 100644 --- a/tests/auto/qml/qjsonbinding/qjsonbinding.pro +++ b/tests/auto/qml/qjsonbinding/qjsonbinding.pro @@ -3,7 +3,6 @@ TARGET = tst_qjsonbinding macx:CONFIG -= app_bundle SOURCES += tst_qjsonbinding.cpp -INCLUDEPATH += ../../shared include (../../shared/util.pri) diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 12c33909cf..2b80970559 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -30,7 +30,7 @@ #include <QtWidgets/QPushButton> tst_QJSValue::tst_QJSValue() - : engine(0) + : engine(nullptr) { } @@ -45,7 +45,7 @@ void tst_QJSValue::ctor_invalid() { QJSValue v; QVERIFY(v.isUndefined()); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } } @@ -111,7 +111,7 @@ void tst_QJSValue::ctor_int() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } } @@ -141,7 +141,7 @@ void tst_QJSValue::ctor_uint() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } } @@ -171,7 +171,7 @@ void tst_QJSValue::ctor_float() QCOMPARE(v.isNumber(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toNumber(), 1.0); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } } @@ -196,7 +196,7 @@ void tst_QJSValue::ctor_string() QCOMPARE(v.isString(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toString(), QLatin1String("ciao")); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } { QJSValue v("ciao"); @@ -204,7 +204,7 @@ void tst_QJSValue::ctor_string() QCOMPARE(v.isString(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toString(), QLatin1String("ciao")); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } } @@ -246,7 +246,7 @@ void tst_QJSValue::ctor_undefined() QJSValue v(QJSValue::UndefinedValue); QVERIFY(v.isUndefined()); QCOMPARE(v.isObject(), false); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } void tst_QJSValue::ctor_null() @@ -255,7 +255,7 @@ void tst_QJSValue::ctor_null() QVERIFY(!v.isUndefined()); QCOMPARE(v.isNull(), true); QCOMPARE(v.isObject(), false); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } void tst_QJSValue::ctor_bool() @@ -266,7 +266,7 @@ void tst_QJSValue::ctor_bool() QCOMPARE(v.isBool(), true); QCOMPARE(v.isObject(), false); QCOMPARE(v.toBool(), false); - QCOMPARE(v.engine(), (QJSEngine *)0); + QCOMPARE(v.engine(), (QJSEngine *)nullptr); } void tst_QJSValue::ctor_copyAndAssign() @@ -274,12 +274,12 @@ void tst_QJSValue::ctor_copyAndAssign() QJSValue v(1.0); QJSValue v2(v); QCOMPARE(v2.strictlyEquals(v), true); - QCOMPARE(v2.engine(), (QJSEngine *)0); + QCOMPARE(v2.engine(), (QJSEngine *)nullptr); QJSValue v3(v); QCOMPARE(v3.strictlyEquals(v), true); QCOMPARE(v3.strictlyEquals(v2), true); - QCOMPARE(v3.engine(), (QJSEngine *)0); + QCOMPARE(v3.engine(), (QJSEngine *)nullptr); QJSValue v4(2.0); QCOMPARE(v4.strictlyEquals(v), false); @@ -404,8 +404,8 @@ void tst_QJSValue::toString() // variant should use internal valueOf(), then fall back to QVariant::toString(), // then fall back to "QVariant(typename)" QJSValue variant = eng.toScriptValue(QPoint(10, 20)); - QVERIFY(variant.isVariant()); - QCOMPARE(variant.toString(), QString::fromLatin1("QVariant(QPoint)")); + QVERIFY(!variant.isVariant()); + QCOMPARE(variant.toString(), QString::fromLatin1("QPoint(10, 20)")); variant = eng.toScriptValue(QUrl()); QVERIFY(variant.isVariant()); QVERIFY(variant.toString().isEmpty()); @@ -970,10 +970,6 @@ void tst_QJSValue::toUInt() QCOMPARE(qjsvalue_cast<quint32>(inv), quint32(0)); } -#if defined Q_CC_MSVC && _MSC_VER < 1300 -Q_DECLARE_METATYPE(QVariant) -#endif - void tst_QJSValue::toVariant() { QJSEngine eng; @@ -1031,6 +1027,20 @@ void tst_QJSValue::toVariant() QJSValue rxObject = eng.toScriptValue(rx); QVERIFY(rxObject.isRegExp()); QVariant var = rxObject.toVariant(); + + // We can't roundtrip a QRegExp this way, as toVariant() has no information on whether we + // want QRegExp or QRegularExpression. It will always create a QRegularExpression. + QCOMPARE(var.type(), QMetaType::QRegularExpression); + QRegularExpression result = var.toRegularExpression(); + QCOMPARE(result.pattern(), rx.pattern()); + QCOMPARE(result.patternOptions() & QRegularExpression::CaseInsensitiveOption, 0); + } + + { + QRegularExpression rx = QRegularExpression("[0-9a-z]+"); + QJSValue rxObject = eng.toScriptValue(rx); + QVERIFY(rxObject.isRegExp()); + QVariant var = rxObject.toVariant(); QCOMPARE(var, QVariant(rx)); } @@ -1111,16 +1121,16 @@ void tst_QJSValue::toQObject_nonQObject_data() QTest::newRow("array") << engine->newArray(); QTest::newRow("date") << engine->evaluate("new Date(124)"); QTest::newRow("variant(12345)") << engine->toScriptValue(QVariant(12345)); - QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(qVariantFromValue((QObject*)0)); - QTest::newRow("newQObject(0)") << engine->newQObject(0); + QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(qVariantFromValue((QObject*)nullptr)); + QTest::newRow("newQObject(0)") << engine->newQObject(nullptr); } void tst_QJSValue::toQObject_nonQObject() { QFETCH(QJSValue, value); - QCOMPARE(value.toQObject(), (QObject *)0); - QCOMPARE(qjsvalue_cast<QObject*>(value), (QObject *)0); + QCOMPARE(value.toQObject(), (QObject *)nullptr); + QCOMPARE(qjsvalue_cast<QObject*>(value), (QObject *)nullptr); } // unfortunately, this is necessary in order to do qscriptvalue_cast<QPushButton*>(...) @@ -1134,7 +1144,7 @@ void tst_QJSValue::toQObject() QJSValue qobject = eng.newQObject(&temp); QCOMPARE(qobject.toQObject(), (QObject *)&temp); QCOMPARE(qjsvalue_cast<QObject*>(qobject), (QObject *)&temp); - QCOMPARE(qjsvalue_cast<QWidget*>(qobject), (QWidget *)0); + QCOMPARE(qjsvalue_cast<QWidget*>(qobject), (QWidget *)nullptr); QWidget widget; QJSValue qwidget = eng.newQObject(&widget); @@ -1198,6 +1208,32 @@ void tst_QJSValue::toRegExp() QVERIFY(qjsvalue_cast<QRegExp>(eng.toScriptValue(QVariant())).isEmpty()); } +void tst_QJSValue::toRegularExpression() +{ + QJSEngine eng; + { + QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/foo/")); + QVERIFY(rx.isValid()); + QCOMPARE(rx.pattern(), QString::fromLatin1("foo")); + QVERIFY(!(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption)); + } + { + QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/bar/gi")); + QVERIFY(rx.isValid()); + QCOMPARE(rx.pattern(), QString::fromLatin1("bar")); + QVERIFY(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption); + } + + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("[]")).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("{}")).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.globalObject()).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue()).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(123)).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(false)).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("null")).pattern().isEmpty()); + QVERIFY(qjsvalue_cast<QRegularExpression>(eng.toScriptValue(QVariant())).pattern().isEmpty()); +} + void tst_QJSValue::isArray_data() { newEngine(); @@ -1263,7 +1299,7 @@ void tst_QJSValue::isError_propertiesOfGlobalObject() for (int i = 0; i < errors.size(); ++i) { QJSValue ctor = eng.globalObject().property(errors.at(i)); QVERIFY(ctor.isCallable()); - QVERIFY(ctor.property("prototype").isError()); + QVERIFY(ctor.property("prototype").isObject()); } } @@ -1598,7 +1634,7 @@ void tst_QJSValue::getSetProperty() QCOMPARE(object.property("baz").toNumber(), num.toNumber()); QJSValue strstr = QJSValue("bar"); - QCOMPARE(strstr.engine(), (QJSEngine *)0); + QCOMPARE(strstr.engine(), (QJSEngine *)nullptr); object.setProperty("foo", strstr); QCOMPARE(object.property("foo").toString(), strstr.toString()); QCOMPARE(strstr.engine(), &eng); // the value has been bound to the engine @@ -1642,7 +1678,7 @@ void tst_QJSValue::getSetPrototype_evalCyclicPrototype() QJSEngine eng; QJSValue ret = eng.evaluate("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o"); QCOMPARE(ret.isError(), true); - QCOMPARE(ret.toString(), QLatin1String("TypeError: Cyclic __proto__ value")); + QCOMPARE(ret.toString(), QLatin1String("TypeError: Could not change prototype.")); } void tst_QJSValue::getSetPrototype_eval() @@ -2099,7 +2135,7 @@ void tst_QJSValue::equals() QJSValue qobj1 = eng.newQObject(&temp); QJSValue qobj2 = eng.newQObject(&temp); - QJSValue qobj3 = eng.newQObject(0); + QJSValue qobj3 = eng.newQObject(nullptr); // FIXME: No ScriptOwnership: QJSValue qobj4 = eng.newQObject(new QObject(), QScriptEngine::ScriptOwnership); QJSValue qobj4 = eng.newQObject(new QObject()); @@ -2252,8 +2288,8 @@ void tst_QJSValue::strictlyEquals() { QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a")); QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a")); - QVERIFY(var1.isArray()); - QVERIFY(var2.isArray()); + QVERIFY(!var1.isArray()); + QVERIFY(!var2.isArray()); QVERIFY(!var1.strictlyEquals(var2)); } { @@ -2285,7 +2321,7 @@ void tst_QJSValue::castToPointer() QColor c(123, 210, 231); QJSValue v = eng.toScriptValue(c); QColor *cp = qjsvalue_cast<QColor*>(v); - QVERIFY(cp != 0); + QVERIFY(cp != nullptr); QCOMPARE(*cp, c); QBrush *bp = qjsvalue_cast<QBrush*>(v); diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h index b8b9f4403c..9532b1f10e 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h @@ -76,6 +76,7 @@ private slots: void toQObject(); void toDateTime(); void toRegExp(); + void toRegularExpression(); void isArray_data(); void isArray(); void isDate(); diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 59566ad927..86f36286d9 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs +QT_FOR_CONFIG += qml METATYPETESTS += \ qqmlmetatype @@ -7,8 +8,9 @@ PUBLICTESTS += \ parserstress \ qjsvalueiterator \ qjsonbinding \ + qqmlfile \ + qqmlfileselector -!boot2qt { PUBLICTESTS += \ qmlmin \ qqmlcomponent \ @@ -24,21 +26,19 @@ PUBLICTESTS += \ qqmlnotifier \ qqmlqt \ qqmlxmlhttprequest \ + qqmlpromise \ qtqmlmodules \ qquickfolderlistmodel \ qqmlapplicationengine \ qqmlsettings \ qqmlstatemachine \ qmldiskcache -} PRIVATETESTS += \ qqmlcpputils \ qqmldirparser \ - v4misc \ qmlcachegen -!boot2qt { PRIVATETESTS += \ animation \ qqmlecmascript \ @@ -70,21 +70,29 @@ PRIVATETESTS += \ qqmltranslation \ qqmlimport \ qqmlobjectmodel \ + qqmltablemodel \ + qv4assembler \ qv4mm \ - ecmascripttests -} + qv4identifiertable \ + qv4regexp \ + ecmascripttests \ + bindingdependencyapi \ + v4misc qtHaveModule(widgets) { PUBLICTESTS += \ qjsengine \ - qjsvalue + qjsvalue \ +# qwidgetsinqml } SUBDIRS += $$PUBLICTESTS SUBDIRS += $$METATYPETESTS -qtConfig(process):!boot2qt { - !contains(QT_CONFIG, no-qml-debug): SUBDIRS += debugger - SUBDIRS += qmllint qmlplugindump +qtConfig(process) { + qtConfig(qml-debug): SUBDIRS += debugger + !boot2qt { + SUBDIRS += qmllint qmlplugindump + } } qtConfig(library) { @@ -97,3 +105,7 @@ qtConfig(private_tests): \ qtNomakeTools( \ qmlplugindump \ ) + +QtConfig(qml_tracing) { + PRIVATETESTS += v4traced +} diff --git a/tests/auto/qml/qmlcachegen/Enums.qml b/tests/auto/qml/qmlcachegen/Enums.qml new file mode 100644 index 0000000000..830babb73e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/Enums.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 +QtObject { + enum Test { + First = 100, + Second = 200 + } + property int value: 0 + Component.onCompleted: value = Enums.Second +} diff --git a/tests/auto/qml/qmlcachegen/Retain.qml b/tests/auto/qml/qmlcachegen/Retain.qml new file mode 100644 index 0000000000..0e69012662 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/Retain.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +QtObject {} diff --git a/tests/auto/qml/qmlcachegen/jsimport.qml b/tests/auto/qml/qmlcachegen/jsimport.qml new file mode 100644 index 0000000000..9c40878e60 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/jsimport.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "script.js" as Script + +QtObject { + property int value: Script.getter() +} diff --git a/tests/auto/qml/qmlcachegen/jsmoduleimport.qml b/tests/auto/qml/qmlcachegen/jsmoduleimport.qml new file mode 100644 index 0000000000..c1fad7fee2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/jsmoduleimport.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "script.mjs" as Script + +QtObject { + property bool ok: Script.ok() +} diff --git a/tests/auto/qml/qmlcachegen/library.js b/tests/auto/qml/qmlcachegen/library.js new file mode 100644 index 0000000000..51fb41dc23 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/library.js @@ -0,0 +1,4 @@ + +function getter() { + return 42; +} diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index 8d8b37be15..c7820ac1cd 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -1,7 +1,27 @@ -CONFIG += testcase +CONFIG += testcase qtquickcompiler TARGET = tst_qmlcachegen macos:CONFIG -= app_bundle SOURCES += tst_qmlcachegen.cpp +workerscripts_test.files = worker.js worker.qml +workerscripts_test.prefix = /workerscripts +RESOURCES += workerscripts_test + +RESOURCES += versionchecks.qml + +RESOURCES += trickypaths.qrc + +RESOURCES += jsimport.qml script.js library.js + +RESOURCES += Enums.qml + +# QTBUG-46375 +!win32: RESOURCES += trickypaths_umlaut.qrc + +RESOURCES += jsmoduleimport.qml script.mjs + +RESOURCES += retain.qrc +QTQUICK_COMPILER_RETAINED_RESOURCES += retain.qrc + QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmlcachegen/retain.qrc b/tests/auto/qml/qmlcachegen/retain.qrc new file mode 100644 index 0000000000..af042b25d8 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/retain.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>Retain.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/script.js b/tests/auto/qml/qmlcachegen/script.js new file mode 100644 index 0000000000..fa55f9069e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/script.js @@ -0,0 +1,6 @@ + +.import "library.js" as Library + +function getter() { + return Library.getter() +} diff --git a/tests/auto/qml/qmlcachegen/script.mjs b/tests/auto/qml/qmlcachegen/script.mjs new file mode 100644 index 0000000000..459c336125 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/script.mjs @@ -0,0 +1,4 @@ + +export function ok() { + return true +} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qml b/tests/auto/qml/qmlcachegen/trickypaths.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qrc b/tests/auto/qml/qmlcachegen/trickypaths.qrc new file mode 100644 index 0000000000..57977ccf6d --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/directory with spaces"> +<file alias="file name with spaces.qml">trickypaths.qml</file> +<file>versionStyleSuffix-1.2-core-yc.qml</file> +<file>versionStyleSuffix-1.2-more.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc new file mode 100644 index 0000000000..9ca889d692 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> +<file alias="Bäh.qml">umlaut.qml</file> +</qresource> +</RCC> diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index b7e616a050..8cfa4cb6af 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -32,7 +32,11 @@ #include <QQmlEngine> #include <QProcess> #include <QLibraryInfo> +#include <QStandardPaths> #include <QSysInfo> +#include <QLoggingCategory> +#include <private/qqmlcomponent_p.h> +#include <qtranslator.h> class tst_qmlcachegen: public QObject { @@ -43,6 +47,25 @@ private slots: void loadGeneratedFile(); void translationExpressionSupport(); + void signalHandlerParameters(); + void errorOnArgumentsInSignalHandler(); + void aheadOfTimeCompilation(); + void functionExpressions(); + void versionChecksForAheadOfTimeUnits(); + void retainedResources(); + + void workerScripts(); + + void trickyPaths_data(); + void trickyPaths(); + + void qrcScriptImport(); + void fsScriptImport(); + void moduleScriptImport(); + + void enums(); + + void sourceFileIndices(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -67,15 +90,20 @@ public: } }; -static bool generateCache(const QString &qmlFileName) +static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr = nullptr) { QProcess proc; - proc.setProcessChannelMode(QProcess::ForwardedChannels); + if (capturedStderr == nullptr) + proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.setProgram(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + QLatin1String("qmlcachegen")); - proc.setArguments(QStringList() << (QLatin1String("--target-architecture=") + QSysInfo::buildCpuArchitecture()) << (QLatin1String("--target-abi=") + QSysInfo::buildAbi()) << qmlFileName); + proc.setArguments(QStringList() << qmlFileName); proc.start(); if (!proc.waitForFinished()) return false; + + if (capturedStderr) + *capturedStderr = proc.readAllStandardError(); + if (proc.exitStatus() != QProcess::NormalExit) return false; return proc.exitCode() == 0; @@ -84,6 +112,13 @@ static bool generateCache(const QString &qmlFileName) void tst_qmlcachegen::initTestCase() { qputenv("QML_FORCE_DISK_CACHE", "1"); + QStandardPaths::setTestModeEnabled(true); + + // make sure there's no pre-existing cache dir + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + if (!cacheDir.isEmpty()) + //QDir(cacheDir).removeRecursively(); + qDebug() << cacheDir; } void tst_qmlcachegen::loadGeneratedFile() @@ -108,6 +143,17 @@ void tst_qmlcachegen::loadGeneratedFile() const QString cacheFilePath = testFilePath + QLatin1Char('c'); QVERIFY(QFile::exists(cacheFilePath)); + + { + QFile cache(cacheFilePath); + QVERIFY(cache.open(QIODevice::ReadOnly)); + const QV4::CompiledData::Unit *cacheUnit = reinterpret_cast<const QV4::CompiledData::Unit *>(cache.map(/*offset*/0, sizeof(QV4::CompiledData::Unit))); + QVERIFY(cacheUnit); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + QCOMPARE(uint(cacheUnit->sourceFileIndex), uint(0)); + } + QVERIFY(QFile::remove(testFilePath)); QQmlEngine engine; @@ -115,13 +161,36 @@ void tst_qmlcachegen::loadGeneratedFile() QScopedPointer<QObject> obj(component.create()); QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); + + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit; + QVERIFY(compilationUnit); + auto unitData = compilationUnit->unitData(); + QVERIFY(unitData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::StaticData); } +class QTestTranslator : public QTranslator +{ +public: + QString translate(const char *context, const char *sourceText, const char */*disambiguation*/, int /*n*/) const override + { + m_lastContext = QString::fromUtf8(context); + return QString::fromUtf8(sourceText).toUpper(); + } + bool isEmpty() const override { return true; } + mutable QString m_lastContext; +}; + void tst_qmlcachegen::translationExpressionSupport() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); + QTestTranslator translator; + qApp->installTranslator(&translator); + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { QFile f(tempDir.path() + '/' + fileName); const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); @@ -155,7 +224,395 @@ void tst_qmlcachegen::translationExpressionSupport() CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); QScopedPointer<QObject> obj(component.create()); QVERIFY(!obj.isNull()); - QCOMPARE(obj->property("text").toString(), QString("All Ok")); + QCOMPARE(obj->property("text").toString(), QString("ALL Ok")); + QCOMPARE(translator.m_lastContext, QStringLiteral("test")); +} + +void tst_qmlcachegen::signalHandlerParameters() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.0\n" + "QtObject {\n" + " property real result: 0\n" + " signal testMe(real value);\n" + " onTestMe: result = value;\n" + " function runTest() { testMe(42); }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + { + QFile cache(cacheFilePath); + QVERIFY(cache.open(QIODevice::ReadOnly)); + const QV4::CompiledData::Unit *cacheUnit = reinterpret_cast<const QV4::CompiledData::Unit *>(cache.map(/*offset*/0, sizeof(QV4::CompiledData::Unit))); + QVERIFY(cacheUnit); + } + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QMetaObject::invokeMethod(obj.data(), "runTest"); + QCOMPARE(obj->property("result").toInt(), 42); + + { + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit; + QVERIFY(compilationUnit); + QVERIFY(compilationUnit->unitData()); + + // Verify that the QML objects don't come from the original data. + QVERIFY(compilationUnit->objectAt(0) != compilationUnit->unitData()->qmlUnit()->objectAt(0)); + + // Typically the final file name is one of those strings that is not in the original + // pre-compiled qml file's string table, while for example the signal parameter + // name ("value") is. + const auto isStringIndexInStringTable = [compilationUnit](uint index) { + return index < compilationUnit->unitData()->stringTableSize; + }; + + QVERIFY(isStringIndexInStringTable(compilationUnit->objectAt(0)->signalAt(0)->parameterAt(0)->nameIndex)); + QVERIFY(!compilationUnit->dynamicStrings.isEmpty()); + } +} + +void tst_qmlcachegen::errorOnArgumentsInSignalHandler() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.2\n" + "QtObject {\n" + " signal mySignal(var arguments);\n" + " onMySignal: console.log(arguments);\n" + "}"); + + + QByteArray errorOutput; + QVERIFY(!generateCache(testFilePath, &errorOutput)); + QVERIFY2(errorOutput.contains("error: The use of eval() or the use of the arguments object in signal handlers is"), errorOutput); +} + +void tst_qmlcachegen::aheadOfTimeCompilation() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.0\n" + "QtObject {\n" + " function runTest() { var x = 0; while (x < 42) { ++x }; return x; }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVariant result; + QMetaObject::invokeMethod(obj.data(), "runTest", Q_RETURN_ARG(QVariant, result)); + QCOMPARE(result.toInt(), 42); +} + +static QQmlPrivate::CachedQmlUnit *temporaryModifiedCachedUnit = nullptr; + +void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() +{ + QVERIFY(QFile::exists(":/versionchecks.qml")); + QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + + Q_ASSERT(!temporaryModifiedCachedUnit); + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + QVERIFY(originalUnit); + QV4::CompiledData::Unit *tweakedUnit = (QV4::CompiledData::Unit *)malloc(originalUnit->unitSize); + memcpy(reinterpret_cast<void *>(tweakedUnit), reinterpret_cast<const void *>(originalUnit), originalUnit->unitSize); + tweakedUnit->version = QV4_DATA_STRUCTURE_VERSION - 1; + temporaryModifiedCachedUnit = new QQmlPrivate::CachedQmlUnit{tweakedUnit, nullptr, nullptr}; + + auto testHandler = [](const QUrl &url) -> const QQmlPrivate::CachedQmlUnit * { + if (url == QUrl("qrc:/versionchecks.qml")) + return temporaryModifiedCachedUnit; + return nullptr; + }; + QQmlMetaType::prependCachedUnitLookupFunction(testHandler); + + { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error)); + QCOMPARE(error, QQmlMetaType::CachedUnitLookupError::VersionMismatch); + } + + { + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc:/versionchecks.qml")); + QCOMPARE(component.status(), QQmlComponent::Error); + QCOMPARE(component.errorString(), QString("qrc:/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")); + } + + Q_ASSERT(temporaryModifiedCachedUnit); + free(const_cast<QV4::CompiledData::Unit *>(temporaryModifiedCachedUnit->qmlData)); + delete temporaryModifiedCachedUnit; + temporaryModifiedCachedUnit = nullptr; + + QQmlMetaType::removeCachedUnitLookupFunction(testHandler); +} + +void tst_qmlcachegen::retainedResources() +{ + QFile file(":/Retain.qml"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QVERIFY(file.readAll().startsWith("import QtQml 2.0")); +} + +void tst_qmlcachegen::workerScripts() +{ + QVERIFY(QFile::exists(":/workerscripts/worker.js")); + QVERIFY(QFile::exists(":/workerscripts/worker.qml")); + QCOMPARE(QFileInfo(":/workerscripts/worker.js").size(), 0); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/worker.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_VERIFY(obj->property("success").toBool()); +} + +void tst_qmlcachegen::functionExpressions() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile( + "test.qml", + "import QtQuick 2.0\n" + "Item {\n" + " id: di\n" + " \n" + " property var f\n" + " property bool f_called: false\n" + " f : function() { f_called = true }\n" + " \n" + " signal g\n" + " property bool g_handler_called: false\n" + " onG: function() { g_handler_called = true }\n" + " \n" + " signal h(int i)\n" + " property bool h_connections_handler_called: false\n" + " Connections {\n" + " target: di\n" + " onH: function(magic) { h_connections_handler_called = (magic == 42)\n }\n" + " }\n" + " \n" + " function runTest() { \n" + " f()\n" + " g()\n" + " h(42)\n" + " }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QCOMPARE(obj->property("f_called").toBool(), false); + QCOMPARE(obj->property("g_handler_called").toBool(), false); + QCOMPARE(obj->property("h_connections_handler_called").toBool(), false); + + QMetaObject::invokeMethod(obj.data(), "runTest"); + + QCOMPARE(obj->property("f_called").toBool(), true); + QCOMPARE(obj->property("g_handler_called").toBool(), true); + QCOMPARE(obj->property("h_connections_handler_called").toBool(), true); +} + +void tst_qmlcachegen::trickyPaths_data() +{ + QTest::addColumn<QString>("filePath"); + QTest::newRow("path with spaces") << QStringLiteral(":/directory with spaces/file name with spaces.qml"); + QTest::newRow("version style suffix 1") << QStringLiteral(":/directory with spaces/versionStyleSuffix-1.2-core-yc.qml"); + QTest::newRow("version style suffix 2") << QStringLiteral(":/directory with spaces/versionStyleSuffix-1.2-more.qml"); + + // QTBUG-46375 +#if !defined(Q_OS_WIN) + QTest::newRow("path with umlaut") << QStringLiteral(":/Bäh.qml"); +#endif +} + +void tst_qmlcachegen::trickyPaths() +{ + QFETCH(QString, filePath); + QVERIFY2(QFile::exists(filePath), qPrintable(filePath)); + QCOMPARE(QFileInfo(filePath).size(), 0); + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc" + filePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("success").toInt(), 42); +} + +void tst_qmlcachegen::qrcScriptImport() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsimport.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_COMPARE(obj->property("value").toInt(), 42); +} + +void tst_qmlcachegen::fsScriptImport() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile( + "test.qml", + "import QtQml 2.0\n" + "import \"test.js\" as ScriptTest\n" + "QtObject {\n" + " property int value: ScriptTest.value\n" + "}\n"); + + const QString scriptFilePath = writeTempFile( + "test.js", + "var value = 42" + ); + + QVERIFY(generateCache(scriptFilePath)); + QVERIFY(generateCache(testFilePath)); + + const QString scriptCacheFilePath = scriptFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(scriptFilePath)); + + { + QFile cache(scriptCacheFilePath); + QVERIFY(cache.open(QIODevice::ReadOnly)); + const QV4::CompiledData::Unit *cacheUnit = reinterpret_cast<const QV4::CompiledData::Unit *>(cache.map(/*offset*/0, sizeof(QV4::CompiledData::Unit))); + QVERIFY(cacheUnit); + QVERIFY(cacheUnit->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(!(cacheUnit->flags & QV4::CompiledData::Unit::PendingTypeCompilation)); + QCOMPARE(uint(cacheUnit->sourceFileIndex), uint(0)); + } + + // Remove source code to make sure that when loading succeeds, it is because we loaded + // the existing cache files. + QVERIFY(QFile::remove(testFilePath)); + QVERIFY(QFile::remove(scriptFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); +} + +void tst_qmlcachegen::moduleScriptImport() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsmoduleimport.qml")); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_VERIFY(obj->property("ok").toBool()); + + QVERIFY(QFile::exists(":/script.mjs")); + QCOMPARE(QFileInfo(":/script.mjs").size(), 0); + + { + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit(); + QVERIFY(compilationUnit); + auto unitData = compilationUnit->unitData(); + QVERIFY(unitData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::IsESModule); + + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/script.mjs"), &error); + QVERIFY(unitFromResources); + + QCOMPARE(unitFromResources, compilationUnit->unitData()); + } +} + +void tst_qmlcachegen::enums() +{ + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl("qrc:///Enums.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QTRY_COMPARE(obj->property("value").toInt(), 200); +} + +void tst_qmlcachegen::sourceFileIndices() +{ + QVERIFY(QFile::exists(":/versionchecks.qml")); + QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + QVERIFY(unitFromResources); + QVERIFY(unitFromResources->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + QCOMPARE(uint(unitFromResources->sourceFileIndex), uint(0)); } QTEST_GUILESS_MAIN(tst_qmlcachegen) diff --git a/tests/auto/qml/qmlcachegen/umlaut.qml b/tests/auto/qml/qmlcachegen/umlaut.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/umlaut.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/versionchecks.qml b/tests/auto/qml/qmlcachegen/versionchecks.qml new file mode 100644 index 0000000000..77d67e7da4 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionchecks.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property bool ok: true +} diff --git a/tests/auto/qml/qmlcachegen/worker.js b/tests/auto/qml/qmlcachegen/worker.js new file mode 100644 index 0000000000..dd2d0b843d --- /dev/null +++ b/tests/auto/qml/qmlcachegen/worker.js @@ -0,0 +1,3 @@ +WorkerScript.onMessage = function(message) { + WorkerScript.sendMessage({ reply: message }); +} diff --git a/tests/auto/qml/qmlcachegen/worker.qml b/tests/auto/qml/qmlcachegen/worker.qml new file mode 100644 index 0000000000..1f1c9d1ac7 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/worker.qml @@ -0,0 +1,12 @@ +import QtQml 2.0 +import QtQuick 2.0 +QtObject { + property bool success: false + property WorkerScript worker: WorkerScript { + source: "worker.js" + onMessage: { + success = true + } + } + Component.onCompleted: worker.sendMessage("Hello") +} diff --git a/tests/auto/qml/qmldiskcache/importmodule.qml b/tests/auto/qml/qmldiskcache/importmodule.qml new file mode 100644 index 0000000000..f890d4cb5c --- /dev/null +++ b/tests/auto/qml/qmldiskcache/importmodule.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +import "module.mjs" as Module +QtObject { + property bool ok: Module.ok() +} diff --git a/tests/auto/qml/qmldiskcache/module.mjs b/tests/auto/qml/qmldiskcache/module.mjs new file mode 100644 index 0000000000..32e0651b8b --- /dev/null +++ b/tests/auto/qml/qmldiskcache/module.mjs @@ -0,0 +1,2 @@ + +export function ok() { return true; } diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro index f98a157b6a..74aefa6944 100644 --- a/tests/auto/qml/qmldiskcache/qmldiskcache.pro +++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro @@ -4,6 +4,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qmldiskcache.cpp -RESOURCES += test.qml +RESOURCES += test.qml importmodule.qml module.mjs QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 3402aeebc1..70a5a73e0f 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -30,10 +30,10 @@ #include <private/qv4compileddata_p.h> #include <private/qv4compiler_p.h> -#include <private/qv4jsir_p.h> -#include <private/qv4isel_p.h> #include <private/qv8engine_p.h> #include <private/qv4engine_p.h> +#include <private/qv4codegen_p.h> +#include <private/qqmlcomponent_p.h> #include <QQmlComponent> #include <QQmlEngine> #include <QQmlFileSelector> @@ -48,7 +48,9 @@ class tst_qmldiskcache: public QObject private slots: void initTestCase(); + void cleanupTestCase(); + void loadLocalAsFallback(); void regenerateAfterChange(); void registerImportForImplicitComponent(); void basicVersionChecks(); @@ -59,6 +61,11 @@ private slots: void cacheResources(); void stableOrderOfDependentCompositeTypes(); void singletonDependency(); + void cppRegisteredSingletonDependency(); + void cacheModuleScripts(); + +private: + QDir m_qmlCacheDirectory; }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -89,7 +96,13 @@ static void waitForFileSystem() // the newly written file has a modification date newer than an existing cache file, we must // wait. // Similar effects of lacking precision have been observed on some Linux systems. - QThread::sleep(1); + static const bool fsHasSubSecondResolution = []() { + QDateTime mtime = QFileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).lastModified(); + // 1:1000 chance of a false negative + return mtime.toMSecsSinceEpoch() % 1000; + }(); + if (!fsHasSubSecondResolution) + QThread::sleep(1); } struct TestCompiler @@ -106,7 +119,7 @@ struct TestCompiler { closeMapping(); testFilePath = baseDirectory + QStringLiteral("/test.qml"); - cacheFilePath = baseDirectory + QStringLiteral("/test.qmlc"); + cacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); mappedFile.setFileName(cacheFilePath); } @@ -173,9 +186,8 @@ struct TestCompiler bool verify() { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); - return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), v4->iselFactory.data(), &lastErrorString); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); + return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString); } void closeMapping() @@ -205,6 +217,58 @@ struct TestCompiler void tst_qmldiskcache::initTestCase() { qputenv("QML_FORCE_DISK_CACHE", "1"); + QStandardPaths::setTestModeEnabled(true); + + const QString cacheDirectory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + m_qmlCacheDirectory.setPath(cacheDirectory + QLatin1String("/qmlcache")); + if (m_qmlCacheDirectory.exists()) + QVERIFY(m_qmlCacheDirectory.removeRecursively()); + QVERIFY(QDir::root().mkpath(m_qmlCacheDirectory.absolutePath())); +} + +void tst_qmldiskcache::cleanupTestCase() +{ + m_qmlCacheDirectory.removeRecursively(); +} + +void tst_qmldiskcache::loadLocalAsFallback() +{ + QQmlEngine engine; + TestCompiler testCompiler(&engine); + + QVERIFY(testCompiler.tempDir.isValid()); + + const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n" + "QtObject {\n" + " property string blah: Qt.platform;\n" + "}"); + + QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); + + // Create an invalid side-by-side .qmlc + { + QFile f(testCompiler.tempDir.path() + "/test.qmlc"); + QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); + QV4::CompiledData::Unit unit = {}; + memcpy(unit.magic, QV4::CompiledData::magic_str, sizeof(unit.magic)); + unit.version = QV4_DATA_STRUCTURE_VERSION; + unit.qtVersion = QT_VERSION; + unit.sourceTimeStamp = testCompiler.mappedFile.fileTime(QFile::FileModificationTime).toMSecsSinceEpoch(); + unit.unitSize = ~0U; // make the size a silly number + // write something to the library hash that should cause it not to be loaded + memset(unit.libraryVersionHash, 'z', sizeof(unit.libraryVersionHash)); + memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); + + // leave the other fields unset, since they don't matter + + f.write(reinterpret_cast<const char *>(&unit), sizeof(unit)); + } + + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); + bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), QFileInfo(testCompiler.testFilePath).lastModified(), + &testCompiler.lastErrorString); + QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString)); + QCOMPARE(unit->objectCount(), 1); } void tst_qmldiskcache::regenerateAfterChange() @@ -225,17 +289,21 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); - QCOMPARE(quint32(testUnit->nObjects), quint32(1)); + const QV4::CompiledData::QmlUnit *qmlUnit = testUnit->qmlUnit(); + + QCOMPARE(quint32(qmlUnit->nObjects), quint32(1)); - const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(1)); QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script)); - QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(1)); + QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(0)); - QCOMPARE(quint32(testUnit->functionTableSize), quint32(2)); + QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); - const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); - QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); + const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(0); + QCOMPARE(testUnit->stringAtInternal(bindingFunction->nameIndex), QString("expression for blah")); // check if we have the correct function + QVERIFY(bindingFunction->codeSize > 0); + QVERIFY(bindingFunction->codeOffset < testUnit->unitSize); } { @@ -249,17 +317,21 @@ void tst_qmldiskcache::regenerateAfterChange() const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); - QCOMPARE(quint32(testUnit->nObjects), quint32(1)); + const QV4::CompiledData::QmlUnit *qmlUnit = testUnit->qmlUnit(); - const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + QCOMPARE(quint32(qmlUnit->nObjects), quint32(1)); + + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(2)); QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number)); - QCOMPARE(obj->bindingTable()->valueAsNumber(), double(42)); + QCOMPARE(obj->bindingTable()->valueAsNumber(reinterpret_cast<const QV4::Value *>(testUnit->constants())), double(42)); - QCOMPARE(quint32(testUnit->functionTableSize), quint32(2)); + QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); - const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1); - QVERIFY(bindingFunction->codeOffset > testUnit->unitSize); + const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(0); + QCOMPARE(testUnit->stringAtInternal(bindingFunction->nameIndex), QString("expression for blah")); // check if we have the correct function + QVERIFY(bindingFunction->codeSize > 0); + QVERIFY(bindingFunction->codeOffset < testUnit->unitSize); } } @@ -280,22 +352,23 @@ void tst_qmldiskcache::registerImportForImplicitComponent() const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit(); QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString)); - QCOMPARE(quint32(testUnit->nImports), quint32(2)); - QCOMPARE(testUnit->stringAt(testUnit->importAt(0)->uriIndex), QStringLiteral("QtQuick")); + const QV4::CompiledData::QmlUnit *qmlUnit = testUnit->qmlUnit(); + QCOMPARE(quint32(qmlUnit->nImports), quint32(2)); + QCOMPARE(testUnit->stringAtInternal(qmlUnit->importAt(0)->uriIndex), QStringLiteral("QtQuick")); - QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); + QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); - QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->uriIndex), QString(componentType->module())); - QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->qualifierIndex), QStringLiteral("QmlInternals")); + QCOMPARE(testUnit->stringAtInternal(qmlUnit->importAt(1)->uriIndex), QString(componentType.module())); + QCOMPARE(testUnit->stringAtInternal(qmlUnit->importAt(1)->qualifierIndex), QStringLiteral("QmlInternals")); - QCOMPARE(quint32(testUnit->nObjects), quint32(3)); + QCOMPARE(quint32(qmlUnit->nObjects), quint32(3)); - const QV4::CompiledData::Object *obj = testUnit->objectAt(0); + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0); QCOMPARE(quint32(obj->nBindings), quint32(1)); QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object)); - const QV4::CompiledData::Object *implicitComponent = testUnit->objectAt(obj->bindingTable()->value.objectIndex); - QCOMPARE(testUnit->stringAt(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType->elementName()); + const QV4::CompiledData::Object *implicitComponent = qmlUnit->objectAt(obj->bindingTable()->value.objectIndex); + QCOMPARE(testUnit->stringAtInternal(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType.elementName()); } } @@ -341,30 +414,6 @@ void tst_qmldiskcache::basicVersionChecks() QVERIFY(!testCompiler.verify()); QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("V4 data structure version mismatch. Found 0 expected %1").arg(QV4_DATA_STRUCTURE_VERSION, 0, 16)); } - - { - testCompiler.clearCache(); - QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); - - testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { - header->architectureIndex = 0; - }); - - QVERIFY(!testCompiler.verify()); - QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Architecture mismatch. Found expected %1").arg(QSysInfo::buildAbi())); - } - - { - testCompiler.clearCache(); - QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); - - testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) { - header->codeGeneratorIndex = 0; - }); - - QVERIFY(!testCompiler.verify()); - QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QV8Engine::getV4(&engine)->iselFactory->codeGeneratorName)); - } } class TypeVersion1 : public QObject @@ -468,6 +517,10 @@ void tst_qmldiskcache::recompileAfterDirectoryChange() testCompiler.clearCache(); QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString)); QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString)); + const QV4::CompiledData::Unit *unit = testCompiler.mapUnit(); + QVERIFY(unit->sourceFileIndex != 0); + const QString expectedPath = QUrl::fromLocalFile(testCompiler.testFilePath).toString(); + QCOMPARE(unit->stringAtInternal(unit->sourceFileIndex), expectedPath); testCompiler.closeMapping(); } @@ -520,7 +573,7 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 42); - QFile cacheFile(testFilePath + "c"); + QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } @@ -535,7 +588,7 @@ void tst_qmldiskcache::fileSelectors() QVERIFY(!obj.isNull()); QCOMPARE(obj->property("value").toInt(), 100); - QFile cacheFile(selectedTestFilePath + "c"); + QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(selectedTestFilePath))); QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName())); } } @@ -585,14 +638,8 @@ void tst_qmldiskcache::localAliases() void tst_qmldiskcache::cacheResources() { - const QString cacheDirectory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - QVERIFY(QDir::root().mkpath(cacheDirectory)); - - const QString qmlCacheDirectory = cacheDirectory + QLatin1String("/qmlcache/"); - QVERIFY(QDir(qmlCacheDirectory).removeRecursively()); - QVERIFY(QDir::root().mkpath(qmlCacheDirectory)); - QVERIFY(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot).isEmpty()); - + const QSet<QString> existingFiles = + m_qmlCacheDirectory.entryList(QDir::Files | QDir::NoDotAndDotDot).toSet(); QQmlEngine engine; @@ -603,13 +650,14 @@ void tst_qmldiskcache::cacheResources() QCOMPARE(obj->property("value").toInt(), 20); } - const QStringList entries = QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot | QDir::Files); + const QSet<QString> entries = + m_qmlCacheDirectory.entryList(QDir::NoDotAndDotDot | QDir::Files).toSet().subtract(existingFiles); QCOMPARE(entries.count(), 1); QDateTime cacheFileTimeStamp; { - QFile cacheFile(qmlCacheDirectory + QLatin1Char('/') + entries.constFirst()); + QFile cacheFile(m_qmlCacheDirectory.absoluteFilePath(*entries.cbegin())); QVERIFY2(cacheFile.open(QIODevice::ReadOnly), qPrintable(cacheFile.errorString())); QV4::CompiledData::Unit unit; QVERIFY(cacheFile.read(reinterpret_cast<char *>(&unit), sizeof(unit)) == sizeof(unit)); @@ -632,10 +680,12 @@ void tst_qmldiskcache::cacheResources() } { - const QStringList entries = QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot | QDir::Files); + const QSet<QString> entries = + m_qmlCacheDirectory.entryList(QDir::NoDotAndDotDot | QDir::Files).toSet().subtract(existingFiles); QCOMPARE(entries.count(), 1); - QCOMPARE(QFileInfo(qmlCacheDirectory + QLatin1Char('/') + entries.constFirst()).lastModified().toMSecsSinceEpoch(), cacheFileTimeStamp.toMSecsSinceEpoch()); + QCOMPARE(QFileInfo(m_qmlCacheDirectory.absoluteFilePath(*entries.cbegin())).lastModified().toMSecsSinceEpoch(), + cacheFileTimeStamp.toMSecsSinceEpoch()); } } @@ -688,7 +738,7 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes() QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData()); QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData()); - const QString testFileCachePath = testFilePath + QLatin1Char('c'); + const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -766,7 +816,7 @@ void tst_qmldiskcache::singletonDependency() QCOMPARE(obj->property("value").toInt(), 42); } - const QString testFileCachePath = testFilePath + QLatin1Char('c'); + const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); QVERIFY(QFile::exists(testFileCachePath)); QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); @@ -790,6 +840,107 @@ void tst_qmldiskcache::singletonDependency() } } +void tst_qmldiskcache::cppRegisteredSingletonDependency() +{ + qmlClearTypeRegistrations(); + QScopedPointer<QQmlEngine> engine(new QQmlEngine); + + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 42 }"); + + qmlRegisterSingletonType(QUrl::fromLocalFile(tempDir.path() + QLatin1String("/MySingleton.qml")), "CppRegisteredSingletonDependency", 1, 0, "Singly"); + + const QString testFilePath = writeTempFile("main.qml", "import QtQml 2.0\nimport CppRegisteredSingletonDependency 1.0\nQtObject {\n" + " function getValue() { return Singly.value; }\n" + "}"); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVariant value; + QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value))); + QCOMPARE(value.toInt(), 42); + } + + const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)); + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + + engine.reset(new QQmlEngine); + waitForFileSystem(); + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 100 }"); + waitForFileSystem(); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + { + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString())); + } + + QVariant value; + QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value))); + QCOMPARE(value.toInt(), 100); + } +} + +void tst_qmldiskcache::cacheModuleScripts() +{ + const QSet<QString> existingFiles = + m_qmlCacheDirectory.entryList(QDir::Files | QDir::NoDotAndDotDot).toSet(); + + QQmlEngine engine; + + { + CleanlyLoadingComponent component(&engine, QUrl("qrc:/importmodule.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVERIFY(obj->property("ok").toBool()); + + auto componentPrivate = QQmlComponentPrivate::get(&component); + QVERIFY(componentPrivate); + auto compilationUnit = componentPrivate->compilationUnit->dependentScripts.first()->compilationUnit(); + QVERIFY(compilationUnit); + auto unitData = compilationUnit->unitData(); + QVERIFY(unitData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::StaticData); + QVERIFY(unitData->flags & QV4::CompiledData::Unit::IsESModule); + QVERIFY(!compilationUnit->backingFile.isNull()); + } + + const QSet<QString> entries = + m_qmlCacheDirectory.entryList(QStringList("*.mjsc")).toSet().subtract(existingFiles); + + QCOMPARE(entries.count(), 1); + + QDateTime cacheFileTimeStamp; + + { + QFile cacheFile(m_qmlCacheDirectory.absoluteFilePath(*entries.cbegin())); + QVERIFY2(cacheFile.open(QIODevice::ReadOnly), qPrintable(cacheFile.errorString())); + QV4::CompiledData::Unit unit; + QVERIFY(cacheFile.read(reinterpret_cast<char *>(&unit), sizeof(unit)) == sizeof(unit)); + + QVERIFY(unit.flags & QV4::CompiledData::Unit::IsESModule); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" diff --git a/tests/auto/qml/qmlmin/qmlmin.pro b/tests/auto/qml/qmlmin/qmlmin.pro index 6af6653270..93e5caabcf 100644 --- a/tests/auto/qml/qmlmin/qmlmin.pro +++ b/tests/auto/qml/qmlmin/qmlmin.pro @@ -6,5 +6,7 @@ macx:CONFIG -= app_bundle SOURCES += tst_qmlmin.cpp DEFINES += SRCDIR=\\\"$$PWD\\\" - -cross_compile: DEFINES += QTEST_CROSS_COMPILED +# Boot2qt is cross compiled but it has sources available +!boot2qt { + cross_compile: DEFINES += QTEST_CROSS_COMPILED +} diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 171c2bda8a..c393149f59 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -98,6 +98,8 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmllanguage/data/insertedSemicolon.1.qml"; invalidFiles << "tests/auto/qml/qqmllanguage/data/nonexistantProperty.5.qml"; invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidRoot.1.qml"; + invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml"; + invalidFiles << "tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml"; invalidFiles << "tests/auto/qml/qquickfolderlistmodel/data/dummy.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/qtbug_22843.js"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.js"; @@ -123,6 +125,9 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; + invalidFiles << "tests/auto/qml/parserstress/tests/ecma_3/FunExpr/fe-001.js"; + invalidFiles << "tests/auto/qml/qjsengine/script/com/trolltech/syntaxerror/__init__.js"; + invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml"; } QStringList tst_qmlmin::findFiles(const QDir &d) diff --git a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml index b47d2e98f4..b47d2e98f4 100644 --- a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/Singleton.qml +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/Singleton.qml diff --git a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir index 8df57f6d47..c08e74db2e 100644 --- a/tests/auto/qml/qmlplugindump/tests/dumper/CompositeSingleton/qmldir +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeSingleton/qmldir @@ -1,3 +1,3 @@ -module tests.dumper.CompositeSingleton +module dumper.CompositeSingleton singleton Singleton 1.0 Singleton.qml depends QtQuick 2.0 diff --git a/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/Composite.qml b/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/Composite.qml new file mode 100644 index 0000000000..b1055b6992 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/Composite.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property int test: 0 +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/Singleton.qml b/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/Singleton.qml new file mode 100644 index 0000000000..e81b2b6cb5 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/Singleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + property Composite test: Composite {} +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/qmldir new file mode 100644 index 0000000000..5a9cb1bd96 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/CompositeWithinSingleton/qmldir @@ -0,0 +1,4 @@ +module dumper.CompositeWithinSingleton +singleton Singleton 1.0 Singleton.qml +Composite 1.0 Composite.qml +depends QtQuick 2.0 diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp new file mode 100644 index 0000000000..689ea4aa53 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dummy.h" + +Dummy::Dummy(QObject *parent): + QObject(parent) +{ +} + +Dummy::~Dummy() +{ +} + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h new file mode 100644 index 0000000000..7011f72523 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DUMMY_H +#define DUMMY_H + +#include <QObject> + +class Dummy : public QObject +{ + Q_OBJECT + +public: + Dummy(QObject *parent = nullptr); + ~Dummy(); +}; + +#endif // DUMMY_H + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.pro b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.pro new file mode 100644 index 0000000000..9f5bc927b4 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy.pro @@ -0,0 +1,23 @@ +TEMPLATE = lib +TARGET = Dummy +QT += qml +CONFIG += qt plugin + +CONFIG -= debug_and_release_target +!build_pass:qtConfig(debug_and_release): CONFIG += release + +TARGET = $$qtLibraryTarget($$TARGET) + +SOURCES += \ + dummy_plugin.cpp \ + dummy.cpp + +HEADERS += \ + dummy_plugin.h \ + dummy.h + +!equals(_PRO_FILE_PWD_, $$OUT_PWD) { + cp.files = qmldir plugins.qmltypes + cp.path = $$OUT_PWD + COPIES += cp +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp new file mode 100644 index 0000000000..763604daf0 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dummy_plugin.h" +#include "dummy.h" + +#include <qqml.h> + +void DummyPlugin::registerTypes(const char *uri) +{ + // @uri dumper.dummy + qmlRegisterType<Dummy>(uri, 1, 0, "Dummy"); +} + + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h new file mode 100644 index 0000000000..86e80e6a08 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/dummy_plugin.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DUMMY_PLUGIN_H +#define DUMMY_PLUGIN_H + +#include <QQmlExtensionPlugin> + +class DummyPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri); +}; + +#endif // DUMMY_PLUGIN_H + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/plugins.qmltypes new file mode 100644 index 0000000000..da0f2f24e4 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/plugins.qmltypes @@ -0,0 +1,17 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick dumper.Dummy 1.0 .' + +Module { + dependencies: [] + Component { + name: "Dummy" + prototype: "QObject" + exports: ["dumper.Dummy/Dummy 1.0"] + exportMetaObjectRevisions: [0] + } +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Dummy/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/qmldir new file mode 100644 index 0000000000..7d4107c040 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Dummy/qmldir @@ -0,0 +1,3 @@ +module dumper.Dummy +plugin Dummy + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/extendedtype.pro b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/extendedtype.pro new file mode 100644 index 0000000000..24243aa622 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/extendedtype.pro @@ -0,0 +1,22 @@ +TEMPLATE = lib +TARGET = ExtendedType +QT += qml +CONFIG += qt plugin + +CONFIG -= debug_and_release_target +!build_pass:qtConfig(debug_and_release): CONFIG += release + +TARGET = $$qtLibraryTarget($$TARGET) + +SOURCES += \ + plugin.cpp + +HEADERS += \ + plugin.h \ + types.h + +!equals(_PRO_FILE_PWD_, $$OUT_PWD) { + cp.files = qmldir plugins.qmltypes + cp.path = $$OUT_PWD + COPIES += cp +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp new file mode 100644 index 0000000000..423fbc1f4c --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "plugin.h" +#include "types.h" + +#include <qqml.h> + +void Plugin::registerTypes(const char *uri) +{ + // @uri dumper.ExtendedType + qmlRegisterType<Type>(uri, 1, 0, "Type"); + qmlRegisterExtendedType<Type, ExtendedType>(uri, 1, 1, "Type"); + qmlRegisterType<DerivedType2>(uri, 1, 1, "DerivedType"); +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h new file mode 100644 index 0000000000..b677fe2940 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugin.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 PLUGIN_H +#define PLUGIN_H + +#include <QQmlExtensionPlugin> + +class Plugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri); +}; + +#endif // PLUGIN_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugins.qmltypes new file mode 100644 index 0000000000..d84eb0011a --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/plugins.qmltypes @@ -0,0 +1,35 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick dumper.ExtendedType 1.1 .' + +Module { + dependencies: [] + Component { + name: "DerivedType1" + prototype: "Type" + Property { name: "m_exendedProperty2"; type: "int" } + } + Component { + name: "DerivedType2" + prototype: "DerivedType1" + exports: ["dumper.ExtendedType/DerivedType 1.1"] + exportMetaObjectRevisions: [0] + } + Component { + name: "Type" + defaultProperty: "data" + prototype: "QObject" + exports: [ + "dumper.ExtendedType/Type 1.0", + "dumper.ExtendedType/Type 1.1" + ] + exportMetaObjectRevisions: [0, 101] + Property { name: "baseProperty"; type: "int" } + Property { name: "extendedProperty"; revision: 101; type: "int" } + Property { name: "data"; revision: 101; type: "QObject"; isList: true; isReadonly: true } + } +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/qmldir new file mode 100644 index 0000000000..6693f403d9 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/qmldir @@ -0,0 +1,3 @@ +module dumper.ExtendedType +plugin ExtendedType + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h new file mode 100644 index 0000000000..dfba55a094 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/ExtendedType/types.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 TYPES_H +#define TYPES_H + +#include <QObject> +#include <QQmlListProperty> + +class Type : public QObject +{ + Q_OBJECT + Q_PROPERTY(int baseProperty MEMBER m_baseProperty) + +public: + Type(QObject *parent = nullptr) + : QObject(parent) {} + +private: + int m_baseProperty; +}; + +class ExtendedType : public QObject +{ + Q_OBJECT + Q_PROPERTY(int extendedProperty MEMBER m_extendedProperty) + Q_PROPERTY(QQmlListProperty<QObject> data READ data) + Q_CLASSINFO("DefaultProperty", "data") + +public: + ExtendedType(QObject *parent = nullptr) + : QObject(parent) {} + QQmlListProperty<QObject> data() { return QQmlListProperty<QObject>(this, m_data); } + +private: + QList<QObject *> m_data; + int m_extendedProperty; +}; + +class DerivedType1 : public Type +{ + Q_OBJECT + Q_PROPERTY(int m_exendedProperty2 MEMBER m_extendedProperty2) + +public: + DerivedType1(QObject *parent = nullptr) + : Type(parent) {} + +private: + int m_extendedProperty2; +}; + +class DerivedType2 : public DerivedType1 +{ + Q_OBJECT +public: + DerivedType2(QObject *parent = nullptr) + : DerivedType1(parent) {} +}; + +#endif // TYPES_H diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/CompositeImports.qml b/tests/auto/qml/qmlplugindump/data/dumper/Imports/CompositeImports.qml new file mode 100644 index 0000000000..b1055b6992 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/CompositeImports.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property int test: 0 +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp new file mode 100644 index 0000000000..a923fade2a --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "imports.h" + +Imports::Imports(QObject *parent): + QObject(parent) +{ +} + +Imports::~Imports() +{ +} + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h new file mode 100644 index 0000000000..d2b9036e4d --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef IMPORTS_H +#define IMPORTS_H + +#include <QObject> + +class Imports : public QObject +{ + Q_OBJECT + +public: + Imports(QObject *parent = nullptr); + ~Imports(); +}; + +#endif // IMPORTS_H + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro new file mode 100644 index 0000000000..d20ea967ea --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +TARGET = Imports +QT += qml +CONFIG += qt plugin + +CONFIG -= debug_and_release_target +!build_pass:qtConfig(debug_and_release): CONFIG += release + +TARGET = $$qtLibraryTarget($$TARGET) + +SOURCES += \ + imports_plugin.cpp \ + imports.cpp + +HEADERS += \ + imports_plugin.h \ + imports.h + +!equals(_PRO_FILE_PWD_, $$OUT_PWD) { + cp.files = qmldir plugins.qmltypes CompositeImports.qml + cp.path = $$OUT_PWD + COPIES += cp +} + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp new file mode 100644 index 0000000000..183ba56ac1 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "imports_plugin.h" +#include "imports.h" + +#include <qqml.h> + +void ImportsPlugin::registerTypes(const char *uri) +{ + // @uri dumper.imports + qmlRegisterType<Imports>(uri, 1, 0, "Imports"); +} + + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h new file mode 100644 index 0000000000..fd09584d47 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports_plugin.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef IMPORTS_PLUGIN_H +#define IMPORTS_PLUGIN_H + +#include <QQmlExtensionPlugin> + +class ImportsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri); +}; + +#endif // IMPORTS_PLUGIN_H + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes new file mode 100644 index 0000000000..937dd60a9e --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes @@ -0,0 +1,17 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick dumper.Imports 1.0 .' + +Module { + dependencies: ["QtQuick 2.0"] + Component { + name: "Imports" + prototype: "QObject" + exports: ["dumper.Imports/Imports 1.0"] + exportMetaObjectRevisions: [0] + } +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir new file mode 100644 index 0000000000..c9058a7f95 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir @@ -0,0 +1,3 @@ +module dumper.Imports +plugin Imports +CompositeImports 1.0 CompositeImports.qml diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Singleton/CompositeSingleton.qml b/tests/auto/qml/qmlplugindump/data/dumper/Singleton/CompositeSingleton.qml new file mode 100644 index 0000000000..b47d2e98f4 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Singleton/CompositeSingleton.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + property int test: 0 +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Singleton/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/Singleton/qmldir new file mode 100644 index 0000000000..6ed6d6f1d4 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Singleton/qmldir @@ -0,0 +1,2 @@ +module dumper.Singleton +singleton CompositeSingleton 1.0 CompositeSingleton.qml diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/plugin.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/Versions/plugin.qmltypes new file mode 100644 index 0000000000..3a33590139 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/plugin.qmltypes @@ -0,0 +1,23 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick dumper.Versions 1.1 .' + +Module { + dependencies: [] + Component { + name: "Versions" + prototype: "QObject" + exports: [ + "dumper.Versions/Versions 1.0", + "dumper.Versions/Versions 1.1" + ] + exportMetaObjectRevisions: [0, 1] + Property { name: "foo"; type: "int" } + Property { name: "bar"; revision: 1; type: "int" } + Property { name: "baz"; revision: 2; type: "int" } + } +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/Versions/plugins.qmltypes new file mode 100644 index 0000000000..3a33590139 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/plugins.qmltypes @@ -0,0 +1,23 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick dumper.Versions 1.1 .' + +Module { + dependencies: [] + Component { + name: "Versions" + prototype: "QObject" + exports: [ + "dumper.Versions/Versions 1.0", + "dumper.Versions/Versions 1.1" + ] + exportMetaObjectRevisions: [0, 1] + Property { name: "foo"; type: "int" } + Property { name: "bar"; revision: 1; type: "int" } + Property { name: "baz"; revision: 2; type: "int" } + } +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/Versions/qmldir new file mode 100644 index 0000000000..a47a2a4573 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/qmldir @@ -0,0 +1,3 @@ +module dumper.Versions +plugin Versions + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp new file mode 100644 index 0000000000..3422275d78 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "versions.h" + +Versions::Versions(QObject *parent): + QObject(parent) +{ +} + +Versions::~Versions() +{ +} + diff --git a/tests/auto/qml/debugger/shared/qqmlinspectorclient.h b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h index bfb489c8f7..ba88f478ca 100644 --- a/tests/auto/qml/debugger/shared/qqmlinspectorclient.h +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.h @@ -25,35 +25,37 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QQMLINSPECTORCLIENT_H -#define QQMLINSPECTORCLIENT_H -#include <private/qqmldebugclient_p.h> +#ifndef VERSIONS_H +#define VERSIONS_H -class QQmlInspectorClient : public QQmlDebugClient +#include <QObject> + +class Versions : public QObject { Q_OBJECT + Q_PROPERTY(int foo READ foo WRITE setFoo NOTIFY fooChanged) + Q_PROPERTY(int bar READ bar WRITE setBar NOTIFY barChanged REVISION 1) + Q_PROPERTY(int baz READ baz WRITE setBaz NOTIFY bazChanged REVISION 2) public: - QQmlInspectorClient(QQmlDebugConnection *connection); - - int setInspectToolEnabled(bool enabled); - int setShowAppOnTop(bool showOnTop); - int setAnimationSpeed(qreal speed); - int select(const QList<int> &objectIds); - int createObject(const QString &qml, int parentId, const QStringList &imports, - const QString &filename); - int moveObject(int childId, int newParentId); - int destroyObject(int objectId); - + Versions(QObject *parent = nullptr); + ~Versions(); + int foo() const { return m_foo; } + void setFoo(int value) { m_foo = value; } + int bar() const { return m_bar; } + void setBar(int value) { m_bar = value; } + int baz() const { return m_baz; } + void setBaz(int value) { m_baz = value; } signals: - void responseReceived(int requestId, bool result); - -protected: - void messageReceived(const QByteArray &message); - + void fooChanged(); + void barChanged(); + void bazChanged(); private: - int m_lastRequestId; + int m_foo; + int m_bar; + int m_baz; }; -#endif // QQMLINSPECTORCLIENT_H +#endif // VERSIONS_H + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.pro b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.pro new file mode 100644 index 0000000000..6bbb9e556b --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions.pro @@ -0,0 +1,23 @@ +TEMPLATE = lib +TARGET = Versions +QT += qml +CONFIG += qt plugin + +CONFIG -= debug_and_release_target +!build_pass:qtConfig(debug_and_release): CONFIG += release + +TARGET = $$qtLibraryTarget($$TARGET) + +SOURCES += \ + versions_plugin.cpp \ + versions.cpp + +HEADERS += \ + versions_plugin.h \ + versions.h + +!equals(_PRO_FILE_PWD_, $$OUT_PWD) { + cpqmldir.files = qmldir plugins.qmltypes + cpqmldir.path = $$OUT_PWD + COPIES += cpqmldir +} diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp new file mode 100644 index 0000000000..4bd290aff1 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "versions_plugin.h" +#include "versions.h" + +#include <qqml.h> + +void VersionsPlugin::registerTypes(const char *uri) +{ + // @uri dumper.versions + qmlRegisterType<Versions>(uri, 1, 0, "Versions"); + qmlRegisterType<Versions, 1>(uri, 1, 1, "Versions"); +} + + diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h new file mode 100644 index 0000000000..4ba68a8125 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/data/dumper/Versions/versions_plugin.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VERSIONS_PLUGIN_H +#define VERSIONS_PLUGIN_H + +#include <QQmlExtensionPlugin> + +class VersionsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri); +}; + +#endif // VERSIONS_PLUGIN_H + diff --git a/tests/auto/qml/qmlplugindump/qmlplugindump.pro b/tests/auto/qml/qmlplugindump/qmlplugindump.pro index c713edc541..34eb58c981 100644 --- a/tests/auto/qml/qmlplugindump/qmlplugindump.pro +++ b/tests/auto/qml/qmlplugindump/qmlplugindump.pro @@ -1,6 +1,8 @@ -CONFIG += testcase -TARGET = tst_qmlplugindump -QT += testlib gui-private -macx:CONFIG -= app_bundle +TEMPLATE = subdirs -SOURCES += tst_qmlplugindump.cpp +SUBDIRS += \ + tst_qmlplugindump.pro \ + data/dumper/Dummy/dummy.pro \ + data/dumper/Imports/imports.pro \ + data/dumper/Versions/versions.pro \ + data/dumper/ExtendedType/extendedtype.pro diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp index 68e11e3551..17766a89b5 100644 --- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp @@ -26,6 +26,8 @@ ** ****************************************************************************/ +#include "util.h" + #include <qtest.h> #include <QLibraryInfo> #include <QDir> @@ -33,7 +35,7 @@ #include <QDebug> #include <cstdlib> -class tst_qmlplugindump : public QObject +class tst_qmlplugindump : public QQmlDataTest { Q_OBJECT public: @@ -43,6 +45,10 @@ private slots: void initTestCase(); void builtins(); void singleton(); + void compositeWithinSingleton(); + + void plugin_data(); + void plugin(); private: QString qmlplugindumpPath; @@ -54,6 +60,7 @@ tst_qmlplugindump::tst_qmlplugindump() void tst_qmlplugindump::initTestCase() { + QQmlDataTest::initTestCase(); qmlplugindumpPath = QLibraryInfo::location(QLibraryInfo::BinariesPath); #if defined(Q_OS_WIN) @@ -102,8 +109,8 @@ void tst_qmlplugindump::singleton() { QProcess dumper; QStringList args; - args << QLatin1String("tests.dumper.CompositeSingleton") << QLatin1String("1.0") - << QLatin1String("."); + args << QLatin1String("dumper.CompositeSingleton") << QLatin1String("1.0") + << QLatin1String(QT_QMLTEST_DIR "/data"); dumper.start(qmlplugindumpPath, args); QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); @@ -113,6 +120,54 @@ void tst_qmlplugindump::singleton() QVERIFY2(result.contains(QLatin1String("exportMetaObjectRevisions: [0]")), qPrintable(result)); } +void tst_qmlplugindump::compositeWithinSingleton() +{ + QProcess dumper; + QStringList args; + args << QLatin1String("dumper.CompositeWithinSingleton") << QLatin1String("1.0") + << QLatin1String(QT_QMLTEST_DIR "/data"); + dumper.start(qmlplugindumpPath, args); + QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); + QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); + + const QString &result = dumper.readAllStandardOutput(); + QVERIFY2(result.contains(QLatin1String("exports: [\"Composite 1.0\"]")), qPrintable(result)); + QVERIFY2(result.contains(QLatin1String("exportMetaObjectRevisions: [0]")), qPrintable(result)); +} + +void tst_qmlplugindump::plugin_data() +{ + QTest::addColumn<QString>("import"); + QTest::addColumn<QString>("version"); + QTest::addColumn<QString>("expectedPath"); + + QTest::newRow("dumper.Dummy") << "dumper.Dummy" << "1.0" << testFile("dumper/Dummy/plugins.qmltypes"); + QTest::newRow("dumper.Imports") << "dumper.Imports" << "1.0" << testFile("dumper/Imports/plugins.qmltypes"); + QTest::newRow("dumper.Versions") << "dumper.Versions" << "1.1" << testFile("dumper/Versions/plugins.qmltypes"); + QTest::newRow("dumper.ExtendedType") << "dumper.ExtendedType" + << "1.1" << testFile("dumper/ExtendedType/plugins.qmltypes"); +} + +void tst_qmlplugindump::plugin() +{ + QFETCH(QString, import); + QFETCH(QString, version); + QFETCH(QString, expectedPath); + + QProcess dumper; + dumper.setWorkingDirectory(dataDirectory()); + QStringList args = { QLatin1String("-nonrelocatable"), QLatin1String("-noforceqtquick"), import, version, QLatin1String(".") }; + dumper.start(qmlplugindumpPath, args); + QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); + QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); + + const QString &result = dumper.readAllStandardOutput(); + QFile expectedFile(expectedPath); + QVERIFY2(expectedFile.open(QIODevice::ReadOnly), qPrintable(expectedFile.errorString())); + const QString expected = expectedFile.readAll(); + QCOMPARE(result, expected); +} + QTEST_MAIN(tst_qmlplugindump) #include "tst_qmlplugindump.moc" diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.pro b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.pro new file mode 100644 index 0000000000..be0a0a49b6 --- /dev/null +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.pro @@ -0,0 +1,9 @@ +QT += testlib gui-private qml +macx:CONFIG -= app_bundle + +CONFIG += testcase + +include(../../shared/util.pri) + +DEFINES += QT_QMLTEST_DIR=\\\"$${_PRO_FILE_PWD_}\\\" +SOURCES += tst_qmlplugindump.cpp diff --git a/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro b/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro index 4a2dde7c47..3adad3759b 100644 --- a/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro +++ b/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro @@ -1,3 +1,5 @@ TEMPLATE = subdirs -SUBDIRS = tst_qqmlapplicationengine.pro \ - testapp +SUBDIRS = testapp \ + tst_qqmlapplicationengine.pro + +CONFIG += ordered diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/delayedExit.qml b/tests/auto/qml/qqmlapplicationengine/testapp/delayedExit.qml new file mode 100644 index 0000000000..3d67c958bb --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/delayedExit.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 + +QtObject { + id: root + property Timer t: Timer { interval: 1; running: true; onTriggered: Qt.exit(0); } + property Connections c: Connections { + target: Qt.application + onAboutToQuit: console.log("End"); + } + Component.onCompleted: console.log("Start: " + Qt.application.arguments[1]); +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.qml b/tests/auto/qml/qqmlapplicationengine/testapp/delayedQuit.qml index c75485a7f7..c75485a7f7 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.qml +++ b/tests/auto/qml/qqmlapplicationengine/testapp/delayedQuit.qml diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/immediateExit.qml b/tests/auto/qml/qqmlapplicationengine/testapp/immediateExit.qml new file mode 100644 index 0000000000..46634f3f51 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/immediateExit.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + Component.onCompleted: { + console.log("End: " + Qt.application.arguments[1]); + Qt.exit(0) + } +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/immediateQuit.qml b/tests/auto/qml/qqmlapplicationengine/testapp/immediateQuit.qml new file mode 100644 index 0000000000..1da9d1201a --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/immediateQuit.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + Component.onCompleted: { + console.log("End: " + Qt.application.arguments[1]); + Qt.quit() + } +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp index a57889fe86..be0d98a2df 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp @@ -32,6 +32,6 @@ int main (int argc, char *argv[]) { QCoreApplication app(argc, argv); - QQmlApplicationEngine e(QUrl("qrc:///main.qml")); + QQmlApplicationEngine e(QUrl(QString("qrc:///") + argv[1])); return app.exec(); } diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc b/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc index 5f6483ac33..82b695bbd8 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc @@ -1,5 +1,8 @@ <RCC> <qresource prefix="/"> - <file>main.qml</file> + <file>immediateQuit.qml</file> + <file>immediateExit.qml</file> + <file>delayedQuit.qml</file> + <file>delayedExit.qml</file> </qresource> </RCC> diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index daeb9b5455..ce654dc45e 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -46,6 +46,7 @@ private slots: void initTestCase(); void basicLoading(); void testNonResolvedPath(); + void application_data(); void application(); void applicationProperties(); void removeObjectsWhenDestroyed(); @@ -111,35 +112,56 @@ void tst_qqmlapplicationengine::testNonResolvedPath() } } +void tst_qqmlapplicationengine::application_data() +{ + QTest::addColumn<QByteArray>("qmlFile"); + QTest::addColumn<QByteArray>("expectedStdErr"); + + QTest::newRow("delayed quit") << QByteArray("delayedQuit.qml") + << QByteArray("qml: Start: delayedQuit.qml\nqml: End\n"); + QTest::newRow("delayed exit") << QByteArray("delayedExit.qml") + << QByteArray("qml: Start: delayedExit.qml\nqml: End\n"); + QTest::newRow("immediate quit") << QByteArray("immediateQuit.qml") + << QByteArray("qml: End: immediateQuit.qml\n"); + QTest::newRow("immediate exit") << QByteArray("immediateExit.qml") + << QByteArray("qml: End: immediateExit.qml\n"); +} + void tst_qqmlapplicationengine::application() { /* This test batches together some tests about running an external application written with QQmlApplicationEngine. The application tests the following functionality which is easier to do by watching a separate process: - -Loads relative paths from the working directory - -quits when quit is called - -emits aboutToQuit after quit is called - -has access to application command line arguments + - Loads relative paths from the working directory + - Quits when quit is called + - Exits when exit is called + - Emits aboutToQuit after quit is called + - Has access to application command line arguments Note that checking the output means that on builds with extra debugging, this might fail with a false positive. Also the testapp is automatically built and installed in shadow builds, so it does NOT use testData */ + + QFETCH(QByteArray, qmlFile); + QFETCH(QByteArray, expectedStdErr); + #if QT_CONFIG(process) QDir::setCurrent(buildDir); QProcess *testProcess = new QProcess(this); QStringList args; - args << QLatin1String("testData"); + args << qmlFile; // QML file passed as an argument is going to be run by testapp. testProcess->start(QLatin1String("testapp/testapp"), args); QVERIFY(testProcess->waitForFinished(5000)); QCOMPARE(testProcess->exitCode(), 0); - QByteArray test_stdout = testProcess->readAllStandardOutput(); - QByteArray test_stderr = testProcess->readAllStandardError(); - QByteArray test_stderr_target("qml: Start: testData\nqml: End\n"); + QByteArray testStdOut = testProcess->readAllStandardOutput(); + QByteArray testStdErr = testProcess->readAllStandardError(); #ifdef Q_OS_WIN - test_stderr_target.replace('\n', QByteArray("\r\n")); + expectedStdErr.replace('\n', QByteArray("\r\n")); #endif - QCOMPARE(test_stdout, QByteArray("")); - QVERIFY(QString(test_stderr).endsWith(QString(test_stderr_target))); + QCOMPARE(testStdOut, QByteArray("")); + QVERIFY2(QString(testStdErr).endsWith(QString(expectedStdErr)), + QByteArray("\nExpected ending:\n") + expectedStdErr + + QByteArray("\nActual output:\n") + testStdErr); delete testProcess; QDir::setCurrent(srcDir); #else // process diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index 4b485d2ce8..34cf21024d 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -65,10 +65,10 @@ void tst_qqmlbinding::binding() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("test-binding.qml")); QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); - QVERIFY(rect != 0); + QVERIFY(rect != nullptr); QQmlBind *binding3 = qobject_cast<QQmlBind*>(rect->findChild<QQmlBind*>("binding3")); - QVERIFY(binding3 != 0); + QVERIFY(binding3 != nullptr); QCOMPARE(rect->color(), QColor("yellow")); QCOMPARE(rect->property("text").toString(), QString("Hello")); @@ -80,7 +80,7 @@ void tst_qqmlbinding::binding() QCOMPARE(binding3->when(), true); QQmlBind *binding = qobject_cast<QQmlBind*>(rect->findChild<QQmlBind*>("binding1")); - QVERIFY(binding != 0); + QVERIFY(binding != nullptr); QCOMPARE(binding->object(), qobject_cast<QObject*>(rect)); QCOMPARE(binding->property(), QLatin1String("text")); QCOMPARE(binding->value().toString(), QLatin1String("Hello")); @@ -94,7 +94,7 @@ void tst_qqmlbinding::whenAfterValue() QQmlComponent c(&engine, testFileUrl("test-binding2.qml")); QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); - QVERIFY(rect != 0); + QVERIFY(rect != nullptr); QCOMPARE(rect->color(), QColor("yellow")); QCOMPARE(rect->property("text").toString(), QString("Hello")); @@ -109,10 +109,10 @@ void tst_qqmlbinding::restoreBinding() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBinding.qml")); QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); - QVERIFY(rect != 0); + QVERIFY(rect != nullptr); QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); - QVERIFY(myItem != 0); + QVERIFY(myItem != nullptr); myItem->setY(25); QCOMPARE(myItem->x(), qreal(100-25)); @@ -139,10 +139,10 @@ void tst_qqmlbinding::restoreBindingWithLoop() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBindingWithLoop.qml")); QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); - QVERIFY(rect != 0); + QVERIFY(rect != nullptr); QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); - QVERIFY(myItem != 0); + QVERIFY(myItem != nullptr); myItem->setY(25); QCOMPARE(myItem->x(), qreal(25 + 100)); @@ -175,10 +175,10 @@ void tst_qqmlbinding::restoreBindingWithoutCrash() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBindingWithoutCrash.qml")); QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); - QVERIFY(rect != 0); + QVERIFY(rect != nullptr); QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); - QVERIFY(myItem != 0); + QVERIFY(myItem != nullptr); myItem->setY(25); QCOMPARE(myItem->x(), qreal(100-25)); @@ -215,9 +215,9 @@ void tst_qqmlbinding::deletedObject() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("deletedObject.qml")); QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create()); - QVERIFY(rect != 0); + QVERIFY(rect != nullptr); - QGuiApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QGuiApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); //don't crash rect->setProperty("activateBinding", true); @@ -289,7 +289,7 @@ void tst_qqmlbinding::delayed() QQmlComponent c(&engine, testFileUrl("delayed.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); // update on creation QCOMPARE(item->property("changeCount").toInt(), 1); diff --git a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp index da7956a5fb..f12c3432c2 100644 --- a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp +++ b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp @@ -26,6 +26,7 @@ ** ****************************************************************************/ #include <qtest.h> +#include <qrandom.h> #include <private/qqmlchangeset_p.h> class tst_qqmlchangeset : public QObject @@ -1462,23 +1463,19 @@ void tst_qqmlchangeset::debug() void tst_qqmlchangeset::random_data() { - QTest::addColumn<int>("seed"); QTest::addColumn<int>("combinations"); QTest::addColumn<int>("depth"); - QTest::newRow("1*5") << 32 << 1 << 5; - QTest::newRow("2*2") << 32 << 2 << 2; - QTest::newRow("3*2") << 32 << 3 << 2; - QTest::newRow("3*5") << 32 << 3 << 5; + QTest::newRow("1*5") << 1 << 5; + QTest::newRow("2*2") << 2 << 2; + QTest::newRow("3*2") << 3 << 2; + QTest::newRow("3*5") << 3 << 5; } void tst_qqmlchangeset::random() { - QFETCH(int, seed); QFETCH(int, combinations); QFETCH(int, depth); - qsrand(seed); - int failures = 0; for (int i = 0; i < 20000; ++i) { QQmlChangeSet accumulatedSet; @@ -1490,27 +1487,27 @@ void tst_qqmlchangeset::random() for (int j = 0; j < combinations; ++j) { QQmlChangeSet set; for (int k = 0; k < depth; ++k) { - switch (-(qrand() % 3)) { + switch (-QRandomGenerator::global()->bounded(3)) { case InsertOp: { - int index = qrand() % (modelCount + 1); - int count = qrand() % 5 + 1; + int index = QRandomGenerator::global()->bounded(modelCount + 1); + int count = QRandomGenerator::global()->bounded(5) + 1; set.insert(index, count); input.append(Insert(index, count)); modelCount += count; break; } case RemoveOp: { - const int index = qrand() % (modelCount + 1); - const int count = qrand() % (modelCount - index + 1); + const int index = QRandomGenerator::global()->bounded(modelCount + 1); + const int count = QRandomGenerator::global()->bounded(modelCount - index + 1); set.remove(index, count); input.append(Remove(index, count)); modelCount -= count; break; } case MoveOp: { - const int from = qrand() % (modelCount + 1); - const int count = qrand() % (modelCount - from + 1); - const int to = qrand() % (modelCount - count + 1); + const int from = QRandomGenerator::global()->bounded(modelCount + 1); + const int count = QRandomGenerator::global()->bounded(modelCount - from + 1); + const int to = QRandomGenerator::global()->bounded(modelCount - count + 1); const int moveId = moveCount++; set.move(from, to, count, moveId); input.append(Move(from, to, count, moveId)); diff --git a/tests/auto/qml/qqmlcomponent/data/InitialPropertyTest.qml b/tests/auto/qml/qqmlcomponent/data/InitialPropertyTest.qml new file mode 100644 index 0000000000..7de276f2d8 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/InitialPropertyTest.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property bool ok: false +} diff --git a/tests/auto/qml/qqmlcomponent/data/QtObjectComponent#2.qml b/tests/auto/qml/qqmlcomponent/data/QtObjectComponent#2.qml new file mode 100644 index 0000000000..431c659424 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/QtObjectComponent#2.qml @@ -0,0 +1,3 @@ +import QtQml 2.0 + +QtObject {} diff --git a/tests/auto/qml/qqmlcomponent/data/QtObjectComponent.qml b/tests/auto/qml/qqmlcomponent/data/QtObjectComponent.qml new file mode 100644 index 0000000000..431c659424 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/QtObjectComponent.qml @@ -0,0 +1,3 @@ +import QtQml 2.0 + +QtObject {} diff --git a/tests/auto/qml/qqmlcomponent/data/nonExistentInitialProperty.qml b/tests/auto/qml/qqmlcomponent/data/nonExistentInitialProperty.qml new file mode 100644 index 0000000000..ef89e46088 --- /dev/null +++ b/tests/auto/qml/qqmlcomponent/data/nonExistentInitialProperty.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 +QtObject { + property Component factory: Qt.createComponent(Qt.resolvedUrl("InitialPropertyTest.qml"), Component.PreferSynchronous) + property var incubator + function startIncubation() + { + incubator = factory.incubateObject(null, { ok: true, nonExistent: 42 }, Qt.Asynchronous) + } +} diff --git a/tests/auto/qml/qqmlcomponent/qqmlcomponent.pro b/tests/auto/qml/qqmlcomponent/qqmlcomponent.pro index 9f8c8a4e24..54012e050c 100644 --- a/tests/auto/qml/qqmlcomponent/qqmlcomponent.pro +++ b/tests/auto/qml/qqmlcomponent/qqmlcomponent.pro @@ -2,12 +2,13 @@ CONFIG += testcase TARGET = tst_qqmlcomponent macx:CONFIG -= app_bundle -INCLUDEPATH += ../../shared/ SOURCES += tst_qqmlcomponent.cpp \ ../../shared/testhttpserver.cpp HEADERS += ../../shared/testhttpserver.h +RESOURCES += data/QtObjectComponent.qml + include (../../shared/util.pri) TESTDATA = data/* diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index f2b0b9973e..7c7c7d3bd0 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -89,7 +89,7 @@ public slots: static void gc(QQmlEngine &engine) { engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } @@ -116,7 +116,11 @@ private slots: void onDestructionCount(); void recursion(); void recursionContinuation(); + void partialComponentCreation(); void callingContextForInitialProperties(); + void setNonExistentInitialProperty(); + void relativeUrl_data(); + void relativeUrl(); private: QQmlEngine engine; @@ -155,7 +159,7 @@ void tst_qqmlcomponent::qmlIncubateObject() { QQmlComponent component(&engine, testFileUrl("incubateObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), false); @@ -169,8 +173,8 @@ void tst_qqmlcomponent::qmlCreateWindow() QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("createWindow.qml")); - QQuickWindow* window = qobject_cast<QQuickWindow *>(component.create()); - QVERIFY(window); + QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create())); + QVERIFY(!window.isNull()); } void tst_qqmlcomponent::qmlCreateObjectAutoParent_data() @@ -188,8 +192,8 @@ void tst_qqmlcomponent::qmlCreateObjectAutoParent() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(testFile)); - QQuickItem *root = qobject_cast<QQuickItem *>(component.create()); - QVERIFY(root); + QScopedPointer<QObject> root(qobject_cast<QQuickItem *>(component.create())); + QVERIFY(!root.isNull()); QObject *qtobjectParent = root->property("qtobjectParent").value<QObject*>(); QQuickItem *itemParent = qobject_cast<QQuickItem *>(root->property("itemParent").value<QObject*>()); QQuickWindow *windowParent = qobject_cast<QQuickWindow *>(root->property("windowParent").value<QObject*>()); @@ -234,8 +238,8 @@ void tst_qqmlcomponent::qmlCreateObjectAutoParent() QCOMPARE(window_item->parent(), windowParent); QCOMPARE(window_window->parent(), windowParent); - QCOMPARE(qobject_cast<QQuickItem *>(qtobject_item)->parentItem(), (QQuickItem *)0); - QCOMPARE(qobject_cast<QQuickWindow *>(qtobject_window)->transientParent(), (QQuickWindow *)0); + QCOMPARE(qobject_cast<QQuickItem *>(qtobject_item)->parentItem(), (QQuickItem *)nullptr); + QCOMPARE(qobject_cast<QQuickWindow *>(qtobject_window)->transientParent(), (QQuickWindow *)nullptr); QCOMPARE(qobject_cast<QQuickItem *>(item_item)->parentItem(), itemParent); QCOMPARE(qobject_cast<QQuickWindow *>(item_window)->transientParent(), itemParent->window()); QCOMPARE(qobject_cast<QQuickItem *>(window_item)->parentItem(), windowParent->contentItem()); @@ -247,45 +251,52 @@ void tst_qqmlcomponent::qmlCreateObjectWithProperties() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("createObjectWithScript.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); - QObject *object = component.create(); - QVERIFY(object != 0); - - QObject *testObject1 = object->property("declarativerectangle").value<QObject*>(); - QVERIFY(testObject1); - QCOMPARE(testObject1->parent(), object); - QCOMPARE(testObject1->property("x").value<int>(), 17); - QCOMPARE(testObject1->property("y").value<int>(), 17); - QCOMPARE(testObject1->property("color").value<QColor>(), QColor(255,255,255)); - QCOMPARE(QQmlProperty::read(testObject1,"border.width").toInt(), 3); - QCOMPARE(QQmlProperty::read(testObject1,"innerRect.border.width").toInt(), 20); - delete testObject1; - - QObject *testObject2 = object->property("declarativeitem").value<QObject*>(); - QVERIFY(testObject2); - QCOMPARE(testObject2->parent(), object); - //QCOMPARE(testObject2->metaObject()->className(), "QDeclarativeItem_QML_2"); - QCOMPARE(testObject2->property("x").value<int>(), 17); - QCOMPARE(testObject2->property("y").value<int>(), 17); - QCOMPARE(testObject2->property("testBool").value<bool>(), true); - QCOMPARE(testObject2->property("testInt").value<int>(), 17); - QCOMPARE(testObject2->property("testObject").value<QObject*>(), object); - delete testObject2; - - QObject *testBindingObj = object->property("bindingTestObject").value<QObject*>(); - QVERIFY(testBindingObj); - QCOMPARE(testBindingObj->parent(), object); - QCOMPARE(testBindingObj->property("testValue").value<int>(), 300); - object->setProperty("width", 150); - QCOMPARE(testBindingObj->property("testValue").value<int>(), 150 * 3); - delete testBindingObj; - - QObject *testBindingThisObj = object->property("bindingThisTestObject").value<QObject*>(); - QVERIFY(testBindingThisObj); - QCOMPARE(testBindingThisObj->parent(), object); - QCOMPARE(testBindingThisObj->property("testValue").value<int>(), 900); - testBindingThisObj->setProperty("width", 200); - QCOMPARE(testBindingThisObj->property("testValue").value<int>(), 200 * 3); - delete testBindingThisObj; + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + { + QScopedPointer<QObject> testObject1(object->property("declarativerectangle") + .value<QObject*>()); + QVERIFY(testObject1); + QCOMPARE(testObject1->parent(), object.data()); + QCOMPARE(testObject1->property("x").value<int>(), 17); + QCOMPARE(testObject1->property("y").value<int>(), 17); + QCOMPARE(testObject1->property("color").value<QColor>(), QColor(255,255,255)); + QCOMPARE(QQmlProperty::read(testObject1.data(),"border.width").toInt(), 3); + QCOMPARE(QQmlProperty::read(testObject1.data(),"innerRect.border.width").toInt(), 20); + } + + { + QScopedPointer<QObject> testObject2(object->property("declarativeitem").value<QObject*>()); + QVERIFY(testObject2); + QCOMPARE(testObject2->parent(), object.data()); + //QCOMPARE(testObject2->metaObject()->className(), "QDeclarativeItem_QML_2"); + QCOMPARE(testObject2->property("x").value<int>(), 17); + QCOMPARE(testObject2->property("y").value<int>(), 17); + QCOMPARE(testObject2->property("testBool").value<bool>(), true); + QCOMPARE(testObject2->property("testInt").value<int>(), 17); + QCOMPARE(testObject2->property("testObject").value<QObject*>(), object.data()); + } + + { + QScopedPointer<QObject> testBindingObj(object->property("bindingTestObject") + .value<QObject*>()); + QVERIFY(testBindingObj); + QCOMPARE(testBindingObj->parent(), object.data()); + QCOMPARE(testBindingObj->property("testValue").value<int>(), 300); + object->setProperty("width", 150); + QCOMPARE(testBindingObj->property("testValue").value<int>(), 150 * 3); + } + + { + QScopedPointer<QObject> testBindingThisObj(object->property("bindingThisTestObject") + .value<QObject*>()); + QVERIFY(testBindingThisObj); + QCOMPARE(testBindingThisObj->parent(), object.data()); + QCOMPARE(testBindingThisObj->property("testValue").value<int>(), 900); + testBindingThisObj->setProperty("width", 200); + QCOMPARE(testBindingThisObj->property("testValue").value<int>(), 200 * 3); + } } void tst_qqmlcomponent::qmlCreateParentReference() @@ -299,7 +310,7 @@ void tst_qqmlcomponent::qmlCreateParentReference() QQmlComponent component(&engine, testFileUrl("createParentReference.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(QMetaObject::invokeMethod(object, "createChild")); delete object; @@ -325,7 +336,7 @@ void tst_qqmlcomponent::async() QCOMPARE(watcher.error, 0); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -346,7 +357,7 @@ void tst_qqmlcomponent::asyncHierarchy() QCOMPARE(watcher.error, 0); QObject *root = component.create(); - QVERIFY(root != 0); + QVERIFY(root != nullptr); // ensure that the parent-child relationship hierarchy is correct // (use QQuickItem* for all children rather than types which are not publicly exported) @@ -411,7 +422,7 @@ void tst_qqmlcomponent::componentUrlCanonicalization() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); } @@ -421,7 +432,7 @@ void tst_qqmlcomponent::componentUrlCanonicalization() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.2.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); } @@ -430,7 +441,7 @@ void tst_qqmlcomponent::componentUrlCanonicalization() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.3.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); } @@ -439,7 +450,7 @@ void tst_qqmlcomponent::componentUrlCanonicalization() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.4.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); } @@ -459,7 +470,7 @@ void tst_qqmlcomponent::onDestructionLookup() QQmlComponent component(&engine, testFileUrl("onDestructionLookup.qml")); QScopedPointer<QObject> object(component.create()); gc(engine); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); } @@ -475,7 +486,7 @@ void tst_qqmlcomponent::onDestructionCount() QTest::ignoreMessage(QtWarningMsg, warning.data()); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } // Warning should not be emitted any further @@ -485,7 +496,7 @@ void tst_qqmlcomponent::onDestructionCount() { QQmlTestMessageHandler messageHandler; - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); warnings = messageHandler.messages(); } @@ -503,7 +514,7 @@ void tst_qqmlcomponent::recursion() QTest::ignoreMessage(QtWarningMsg, QLatin1String("QQmlComponent: Component creation is recursing - aborting").data()); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // Sub-object creation does not succeed QCOMPARE(object->property("success").toBool(), false); @@ -518,12 +529,35 @@ void tst_qqmlcomponent::recursionContinuation() QTest::ignoreMessage(QtWarningMsg, QLatin1String("QQmlComponent: Component creation is recursing - aborting").data()); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // Eventual sub-object creation succeeds QVERIFY(object->property("success").toBool()); } +void tst_qqmlcomponent::partialComponentCreation() +{ + const int maxCount = 17; + QQmlEngine engine; + QScopedPointer<QQmlComponent> components[maxCount]; + QScopedPointer<QObject> objects[maxCount]; + QQmlTestMessageHandler messageHandler; + + QCOMPARE(engine.outputWarningsToStandardError(), true); + + for (int i = 0; i < maxCount; i++) { + components[i].reset(new QQmlComponent(&engine, testFileUrl("QtObjectComponent.qml"))); + objects[i].reset(components[i]->beginCreate(engine.rootContext())); + QVERIFY(objects[i].isNull() == false); + } + QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); + + for (int i = 0; i < maxCount; i++) { + components[i]->completeCreate(); + } + QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); +} + class CallingContextCheckingClass : public QObject { Q_OBJECT @@ -536,13 +570,13 @@ public: int value() const { return m_value; } void setValue(int v) { scopeObject.clear(); - callingContextData.setContextData(0); + callingContextData.setContextData(nullptr); m_value = v; QJSEngine *jsEngine = qjsEngine(this); if (!jsEngine) return; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(jsEngine); + QV4::ExecutionEngine *v4 = jsEngine->handle(); if (!v4) return; QV4::Scope scope(v4); @@ -581,6 +615,45 @@ void tst_qqmlcomponent::callingContextForInitialProperties() QVERIFY(checker->scopeObject->metaObject()->indexOfProperty("incubatedObject") != -1); } +void tst_qqmlcomponent::setNonExistentInitialProperty() +{ + QQmlIncubationController controller; + QQmlEngine engine; + engine.setIncubationController(&controller); + QQmlComponent component(&engine, testFileUrl("nonExistentInitialProperty.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QMetaObject::invokeMethod(obj.data(), "startIncubation"); + QJSValue incubatorStatus = obj->property("incubator").value<QJSValue>(); + incubatorStatus.property("forceCompletion").callWithInstance(incubatorStatus); + QJSValue objectWrapper = incubatorStatus.property("object"); + QVERIFY(objectWrapper.isQObject()); + QPointer<QObject> object(objectWrapper.toQObject()); + QVERIFY(object->property("ok").toBool()); +} + +void tst_qqmlcomponent::relativeUrl_data() +{ + QTest::addColumn<QUrl>("url"); + + QTest::addRow("fromLocalFile") << QUrl::fromLocalFile("data/QtObjectComponent.qml"); + QTest::addRow("fromLocalFileHash") << QUrl::fromLocalFile("data/QtObjectComponent#2.qml"); + QTest::addRow("constructor") << QUrl("data/QtObjectComponent.qml"); + QTest::addRow("absolute") << QUrl::fromLocalFile(QFINDTESTDATA("data/QtObjectComponent.qml")); + QTest::addRow("qrc") << QUrl("qrc:/data/QtObjectComponent.qml"); +} + +void tst_qqmlcomponent::relativeUrl() +{ + QFETCH(QUrl, url); + + QQmlComponent component(&engine); + // Shouldn't assert in QQmlTypeLoader; we want QQmlComponent to assume that + // data/QtObjectComponent.qml refers to the data/QtObjectComponent.qml in the current working directory. + component.loadUrl(url); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); +} + QTEST_MAIN(tst_qqmlcomponent) #include "tst_qqmlcomponent.moc" diff --git a/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml b/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml new file mode 100644 index 0000000000..80e459966b --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml @@ -0,0 +1,13 @@ +import QtQml 2.12 +import test.proxy 1.0 + +Proxy { + property int testEnum: 0; + id: proxy + property Connections connections: Connections { + target: proxy + onSomeSignal: testEnum = Proxy.EnumValue; + } + + Component.onCompleted: someSignal() +} diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp index 22e9724c61..dc29363fcf 100644 --- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp +++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp @@ -55,6 +55,7 @@ private slots: void disabledAtStart(); void clearImplicitTarget(); void onWithoutASignal(); + void noAcceleratedGlobalLookup(); private: QQmlEngine engine; @@ -70,7 +71,7 @@ void tst_qqmlconnections::defaultValues() QQmlComponent c(&engine, testFileUrl("test-connection3.qml")); QQmlConnections *item = qobject_cast<QQmlConnections*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QVERIFY(!item->target()); delete item; @@ -82,9 +83,9 @@ void tst_qqmlconnections::properties() QQmlComponent c(&engine, testFileUrl("test-connection2.qml")); QQmlConnections *item = qobject_cast<QQmlConnections*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QCOMPARE(item->target(), item); delete item; @@ -96,7 +97,7 @@ void tst_qqmlconnections::connection() QQmlComponent c(&engine, testFileUrl("test-connection.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QCOMPARE(item->property("tested").toBool(), false); QCOMPARE(item->width(), 50.); @@ -113,7 +114,7 @@ void tst_qqmlconnections::trimming() QQmlComponent c(&engine, testFileUrl("trimming.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("tested").toString(), QString("")); int index = object->metaObject()->indexOfSignal("testMe(int,QString)"); @@ -133,7 +134,7 @@ void tst_qqmlconnections::targetChanged() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("connection-targetchange.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QQmlConnections *connections = item->findChild<QQmlConnections*>("connections"); QVERIFY(connections); @@ -178,7 +179,7 @@ void tst_qqmlconnections::unknownSignals() QQmlEngine engine; QQmlComponent c(&engine, url); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // check that connection is created (they are all runtime errors) QQmlConnections *connections = object->findChild<QQmlConnections*>("connections"); @@ -222,7 +223,7 @@ class TestObject : public QObject Q_PROPERTY(bool ran READ ran WRITE setRan) public: - TestObject(QObject *parent = 0) : QObject(parent), m_ran(false) {} + TestObject(QObject *parent = nullptr) : QObject(parent), m_ran(false) {} ~TestObject() {} bool ran() const { return m_ran; } @@ -244,7 +245,7 @@ void tst_qqmlconnections::rewriteErrors() QQmlComponent c(&engine, testFileUrl("rewriteError-unnamed.qml")); QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal uses unnamed parameter followed by named parameter.").toLatin1()); TestObject *obj = qobject_cast<TestObject*>(c.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); obj->unnamedArgumentSignal(1, .5, "hello"); QCOMPARE(obj->ran(), false); @@ -256,7 +257,7 @@ void tst_qqmlconnections::rewriteErrors() QQmlComponent c(&engine, testFileUrl("rewriteError-global.qml")); QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal parameter \"parseInt\" hides global variable.").toLatin1()); TestObject *obj = qobject_cast<TestObject*>(c.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); obj->signalWithGlobalName(10); QCOMPARE(obj->ran(), false); @@ -272,7 +273,7 @@ Q_OBJECT Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) public: - MyTestSingletonType(QObject *parent = 0) : QObject(parent), m_intProp(0), m_changeCount(0) {} + MyTestSingletonType(QObject *parent = nullptr) : QObject(parent), m_intProp(0), m_changeCount(0) {} ~MyTestSingletonType() {} Q_INVOKABLE int otherMethod(int val) { return val + 4; } @@ -307,7 +308,7 @@ void tst_qqmlconnections::singletonTypeTarget() qmlRegisterSingletonType<MyTestSingletonType>("MyTestSingletonType", 1, 0, "Api", module_api_factory); QQmlComponent component(&engine, testFileUrl("singletontype-target.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("moduleIntPropChangedCount").toInt(), 0); QCOMPARE(object->property("moduleOtherSignalCount").toInt(), 0); @@ -333,7 +334,7 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("test-connection.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QQmlConnections *connections = item->findChild<QQmlConnections*>("connections"); QVERIFY(connections); @@ -361,7 +362,7 @@ void tst_qqmlconnections::disabledAtStart() QQmlComponent c(&engine, testFileUrl("disabled-at-start.qml")); QObject * const object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("tested").toBool(), false); const int index = object->metaObject()->indexOfSignal("testMe()"); @@ -379,7 +380,7 @@ void tst_qqmlconnections::clearImplicitTarget() QQmlComponent c(&engine, testFileUrl("test-connection-implicit.qml")); QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); // normal case: fire Connections item->setWidth(100.); @@ -389,7 +390,7 @@ void tst_qqmlconnections::clearImplicitTarget() // clear the implicit target QQmlConnections *connections = item->findChild<QQmlConnections*>(); QVERIFY(connections); - connections->setTarget(0); + connections->setTarget(nullptr); // target cleared: no longer fire Connections item->setWidth(150.); @@ -407,6 +408,30 @@ void tst_qqmlconnections::onWithoutASignal() QVERIFY(item == nullptr); // should parse error, and not give us an item (or crash). } +class Proxy : public QObject +{ + Q_OBJECT +public: + enum MyEnum { EnumValue = 20, AnotherEnumValue }; + Q_ENUM(MyEnum) + +signals: + void someSignal(); +}; + +void tst_qqmlconnections::noAcceleratedGlobalLookup() +{ + qRegisterMetaType<Proxy::MyEnum>(); + qmlRegisterType<Proxy>("test.proxy", 1, 0, "Proxy"); + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("override-proxy-type.qml")); + QVERIFY(c.isReady()); + QScopedPointer<QObject> object(c.create()); + const QVariant val = object->property("testEnum"); + QCOMPARE(val.type(), QMetaType::Int); + QCOMPARE(val.toInt(), int(Proxy::EnumValue)); +} + QTEST_MAIN(tst_qqmlconnections) #include "tst_qqmlconnections.moc" diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml index d19b6ecc41..d593f0dfa1 100644 --- a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml +++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml @@ -37,7 +37,7 @@ ** ****************************************************************************/ -import QtQuick 2.8 +import QtQuick 2.12 Item { id:root @@ -48,6 +48,12 @@ Item { } LoggingCategory { + id: testCategoryStartingFromWarning + name: "qt.test.warning" + defaultLogLevel: LoggingCategory.Warning + } + + LoggingCategory { id: emptyCategory } @@ -57,8 +63,14 @@ Item { console.info(testCategory, "console.info"); console.warn(testCategory, "console.warn"); console.error(testCategory, "console.error"); + console.debug(testCategoryStartingFromWarning, "console.debug"); + console.log(testCategoryStartingFromWarning, "console.log"); + console.info(testCategoryStartingFromWarning, "console.info"); + console.warn(testCategoryStartingFromWarning, "console.warn"); + console.error(testCategoryStartingFromWarning, "console.error"); testCategory.name = "qt.test2"; + testCategory.defaultLogLevel = LoggingCategory.Debug; console.error(emptyCategory, "console.error"); } diff --git a/tests/auto/qml/qqmlconsole/data/logging.qml b/tests/auto/qml/qqmlconsole/data/logging.qml index d55c99bcbd..1f929d311b 100644 --- a/tests/auto/qml/qqmlconsole/data/logging.qml +++ b/tests/auto/qml/qqmlconsole/data/logging.qml @@ -67,6 +67,10 @@ QtObject { console.log(1, "pong!", new Object); console.log(1, ["ping","pong"], new Object, 2); + console.log(contextStringListProperty); + console.log(customObject); + console.log([[1,2,3,[2,2,2,2],4],[5,6,7,8]]); + try { console.log(exception); } catch (e) { diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp index f832143935..b157314071 100644 --- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp +++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp @@ -29,6 +29,7 @@ #include <QDebug> #include <QQmlEngine> #include <QQmlComponent> +#include <QQmlContext> #include <QLoggingCategory> #include "../../shared/util.h" @@ -50,6 +51,15 @@ private: QQmlEngine engine; }; +struct CustomObject {}; + +QDebug operator<<(QDebug dbg, const CustomObject &) +{ + return dbg << "MY OBJECT"; +} + +Q_DECLARE_METATYPE(CustomObject) + void tst_qqmlconsole::logging() { QUrl testUrl = testFileUrl("logging.qml"); @@ -74,18 +84,28 @@ void tst_qqmlconsole::logging() QTest::ignoreMessage(QtDebugMsg, "{\"a\":\"hello\",\"d\":1}"); QTest::ignoreMessage(QtDebugMsg, "undefined"); QTest::ignoreMessage(QtDebugMsg, "12"); - QTest::ignoreMessage(QtDebugMsg, "function() { [code] }"); + QTest::ignoreMessage(QtDebugMsg, "function e() { [native code] }"); QTest::ignoreMessage(QtDebugMsg, "true"); // Printing QML object prints out the class/type of QML object with the memory address // QTest::ignoreMessage(QtDebugMsg, "QtObject_QML_0(0xABCD..)"); // QTest::ignoreMessage(QtDebugMsg, "[object Object]"); QTest::ignoreMessage(QtDebugMsg, "1 pong! [object Object]"); QTest::ignoreMessage(QtDebugMsg, "1 [ping,pong] [object Object] 2"); + QTest::ignoreMessage(QtDebugMsg, "[Hello,World]"); + 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); - QObject *object = component.create(); - QVERIFY(object != 0); - delete object; + QScopedPointer<QObject> object(component.create(loggingContext.data())); + QVERIFY(object != nullptr); } void tst_qqmlconsole::categorized_logging() @@ -102,13 +122,18 @@ void tst_qqmlconsole::categorized_logging() QQmlComponent component(&engine, testUrl); QObject *object = component.create(); - QVERIFY2(object != 0, component.errorString().toUtf8()); + QVERIFY2(object != nullptr, component.errorString().toUtf8()); QVERIFY(messageHandler.messages().contains("qt.test: console.info")); QVERIFY(messageHandler.messages().contains("qt.test: console.warn")); QVERIFY(messageHandler.messages().contains("qt.test: console.error")); + QVERIFY(!messageHandler.messages().contains("qt.test.warning: console.debug")); + QVERIFY(!messageHandler.messages().contains("qt.test.warning: console.log")); + QVERIFY(!messageHandler.messages().contains("qt.test.warning: console.info")); + QVERIFY(messageHandler.messages().contains("qt.test.warning: console.warn")); + QVERIFY(messageHandler.messages().contains("qt.test.warning: console.error")); - QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) + + QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(56).arg(5) + "QML LoggingCategory: Declaring the name of the LoggingCategory is mandatory and cannot be changed later !"; QVERIFY(messageHandler.messages().contains(emptyCategory)); @@ -116,7 +141,11 @@ void tst_qqmlconsole::categorized_logging() "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the Item is created"; QVERIFY(messageHandler.messages().contains(changedCategory)); - QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(63) + + QString changedDefaultLogLevel = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) + + "QML LoggingCategory: The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created"; + QVERIFY(messageHandler.messages().contains(changedDefaultLogLevel)); + + QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(75) + "Error: A QmlLoggingCatgory was provided without a valid name"; QVERIFY(messageHandler.messages().contains(useEmptyCategory)); @@ -135,7 +164,7 @@ void tst_qqmlconsole::tracing() QQmlComponent component(&engine, testUrl); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -149,7 +178,7 @@ void tst_qqmlconsole::profiling() QQmlComponent component(&engine, testUrl); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -170,7 +199,7 @@ void tst_qqmlconsole::testAssert() QQmlComponent component(&engine, testUrl); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -191,7 +220,7 @@ void tst_qqmlconsole::exception() QQmlComponent component(&engine, testUrl); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } diff --git a/tests/auto/qml/qqmlcontext/data/ContextLeak.js b/tests/auto/qml/qqmlcontext/data/ContextLeak.js new file mode 100644 index 0000000000..e43b1bb230 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/ContextLeak.js @@ -0,0 +1 @@ +var value = 42 diff --git a/tests/auto/qml/qqmlcontext/data/Drawer.qml b/tests/auto/qml/qqmlcontext/data/Drawer.qml new file mode 100644 index 0000000000..b35d5c8d34 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/Drawer.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 +import QtQuick.Window 2.11 + +Rectangle { + parent: Window.contentItem +} diff --git a/tests/auto/qml/qqmlcontext/data/MyItem.qml b/tests/auto/qml/qqmlcontext/data/MyItem.qml new file mode 100644 index 0000000000..2ffd984dfa --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/MyItem.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + Component.onCompleted: 5 + 5 +} diff --git a/tests/auto/qml/qqmlcontext/data/Singleton.qml b/tests/auto/qml/qqmlcontext/data/Singleton.qml new file mode 100644 index 0000000000..68ef5850e3 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/Singleton.qml @@ -0,0 +1,5 @@ +pragma Singleton +import QtQml 2.0 +QtObject { + readonly property string song: "Highway to Hell" +} diff --git a/tests/auto/qml/qqmlcontext/data/contextLeak.qml b/tests/auto/qml/qqmlcontext/data/contextLeak.qml new file mode 100644 index 0000000000..515b3a1aa2 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/contextLeak.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +import "ContextLeak.js" as ContextLeak +QtObject { + property int value: ContextLeak.value +} diff --git a/tests/auto/qml/qqmlcontext/data/contextObjectHierarchy.qml b/tests/auto/qml/qqmlcontext/data/contextObjectHierarchy.qml new file mode 100644 index 0000000000..91978d98a0 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/contextObjectHierarchy.qml @@ -0,0 +1,6 @@ +import QtQuick 2.6 +import QtQuick.Window 2.2 + +Window { + Drawer {} +} diff --git a/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml new file mode 100644 index 0000000000..2e0e6f20e2 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 + +import constants 1.0 + +QtObject { + function createClosure() { + return function() { return Sing.song; } + } + function createComponentFactory() { + return function(parentObj) { + return Qt.createQmlObject('import QtQml 2.0; QtObject { property string test: "ok"; }', parentObj); + } + } +} diff --git a/tests/auto/qml/qqmlcontext/data/outerContextObject.qml b/tests/auto/qml/qqmlcontext/data/outerContextObject.qml new file mode 100644 index 0000000000..992b760915 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/outerContextObject.qml @@ -0,0 +1,18 @@ +import QtQml 2.2 + +QtObject { + id: window + + property Component itemComponent: Qt.createComponent("MyItem.qml") + property MyItem item + + property Timer timer: Timer { + running: true + interval: 10 + repeat: true + onTriggered: { + item = itemComponent.createObject(null, {}); + gc(); + } + } +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index f49fd391ac..cb4bee0d3a 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -28,11 +28,14 @@ #include <qtest.h> #include <QDebug> +#include <QTimer> #include <QQmlEngine> #include <QQmlContext> #include <QQmlComponent> #include <QQmlExpression> #include <private/qqmlcontext_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4object_p.h> #include "../../shared/util.h" class tst_qqmlcontext : public QQmlDataTest @@ -47,6 +50,7 @@ private slots: void engineMethod(); void parentContext(); void setContextProperty(); + void setContextProperties(); void setContextObject(); void destruction(); void idAsContextProperty(); @@ -62,6 +66,11 @@ private slots: void evalAfterInvalidate(); void qobjectDerived(); void qtbug_49232(); + void contextViaClosureAfterDestruction(); + void contextLeak(); + + void outerContextObject(); + void contextObjectHierarchy(); private: QQmlEngine engine; @@ -111,7 +120,7 @@ void tst_qqmlcontext::resolvedUrl() QQmlContext ctxt2(ctxt); QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.qt-project.org/main2.qml")); - delete ctxt; ctxt = 0; + delete ctxt; ctxt = nullptr; QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl()); } @@ -139,7 +148,7 @@ void tst_qqmlcontext::engineMethod() QCOMPARE(ctxt3.engine(), engine); QCOMPARE(ctxt4.engine(), engine); - delete engine; engine = 0; + delete engine; engine = nullptr; QCOMPARE(ctxt.engine(), engine); QCOMPARE(ctxt2.engine(), engine); @@ -151,7 +160,7 @@ void tst_qqmlcontext::parentContext() { QQmlEngine *engine = new QQmlEngine; - QCOMPARE(engine->rootContext()->parentContext(), (QQmlContext *)0); + QCOMPARE(engine->rootContext()->parentContext(), (QQmlContext *)nullptr); QQmlContext *ctxt = new QQmlContext(engine); QQmlContext *ctxt2 = new QQmlContext(ctxt); @@ -169,23 +178,23 @@ void tst_qqmlcontext::parentContext() QCOMPARE(ctxt6->parentContext(), engine->rootContext()); QCOMPARE(ctxt7->parentContext(), engine->rootContext()); - delete ctxt2; ctxt2 = 0; + delete ctxt2; ctxt2 = nullptr; QCOMPARE(ctxt->parentContext(), engine->rootContext()); - QCOMPARE(ctxt3->parentContext(), (QQmlContext *)0); - QCOMPARE(ctxt4->parentContext(), (QQmlContext *)0); + QCOMPARE(ctxt3->parentContext(), (QQmlContext *)nullptr); + QCOMPARE(ctxt4->parentContext(), (QQmlContext *)nullptr); QCOMPARE(ctxt5->parentContext(), ctxt); QCOMPARE(ctxt6->parentContext(), engine->rootContext()); QCOMPARE(ctxt7->parentContext(), engine->rootContext()); - delete engine; engine = 0; + delete engine; engine = nullptr; - QCOMPARE(ctxt->parentContext(), (QQmlContext *)0); - QCOMPARE(ctxt3->parentContext(), (QQmlContext *)0); - QCOMPARE(ctxt4->parentContext(), (QQmlContext *)0); - QCOMPARE(ctxt5->parentContext(), (QQmlContext *)0); - QCOMPARE(ctxt6->parentContext(), (QQmlContext *)0); - QCOMPARE(ctxt7->parentContext(), (QQmlContext *)0); + QCOMPARE(ctxt->parentContext(), (QQmlContext *)nullptr); + QCOMPARE(ctxt3->parentContext(), (QQmlContext *)nullptr); + QCOMPARE(ctxt4->parentContext(), (QQmlContext *)nullptr); + QCOMPARE(ctxt5->parentContext(), (QQmlContext *)nullptr); + QCOMPARE(ctxt6->parentContext(), (QQmlContext *)nullptr); + QCOMPARE(ctxt7->parentContext(), (QQmlContext *)nullptr); delete ctxt7; delete ctxt6; @@ -362,6 +371,32 @@ void tst_qqmlcontext::setContextProperty() } } +void tst_qqmlcontext::setContextProperties() +{ + QQmlContext ctxt(&engine); + + TestObject obj1; + obj1.setA(3345); + TestObject obj2; + obj2.setA(-19); + + QVector<QQmlContext::PropertyPair> properties; + + properties.append({QString("a"), QVariant(10)}); + properties.append({QString("b"), QVariant(19)}); + properties.append({QString("d"), QVariant::fromValue<TestObject*>(&obj2)}); + properties.append({QString("c"), QVariant(QString("Hello World!"))}); + properties.append({QString("e"), QVariant::fromValue<TestObject*>(&obj1)}); + + ctxt.setContextProperties(properties); + + TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(10)); + TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(19)); + TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(QString("Hello World!"))); + TEST_CONTEXT_PROPERTY(&ctxt, d.a, QVariant(-19)); + TEST_CONTEXT_PROPERTY(&ctxt, e.a, QVariant(3345)); +} + void tst_qqmlcontext::setContextObject() { QQmlContext ctxt(&engine); @@ -422,12 +457,12 @@ void tst_qqmlcontext::destruction() QObject obj; QQmlEngine::setContextForObject(&obj, ctxt); - QQmlExpression expr(ctxt, 0, "a"); + QQmlExpression expr(ctxt, nullptr, "a"); QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj)); QCOMPARE(ctxt, expr.context()); - delete ctxt; ctxt = 0; + delete ctxt; ctxt = nullptr; QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj)); QCOMPARE(ctxt, expr.context()); @@ -476,7 +511,7 @@ void tst_qqmlcontext::readOnlyContexts() QCOMPARE(context->contextProperty("hello"), QVariant()); QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set context object for internal context."); - context->setContextObject(0); + context->setContextObject(nullptr); QCOMPARE(context->contextObject(), obj); delete obj; @@ -504,7 +539,7 @@ void tst_qqmlcontext::nameForObject() component.setData("import QtQuick 2.0; QtObject { id: root; property QtObject o: QtObject { id: nested } }", QUrl()); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(qmlContext(o)->nameForObject(o), QString("root")); QCOMPARE(qmlContext(o)->nameForObject(qvariant_cast<QObject*>(o->property("o"))), QString("nested")); @@ -517,12 +552,12 @@ class DeleteCommand : public QObject { Q_OBJECT public: - DeleteCommand() : object(0) {} + DeleteCommand() : object(nullptr) {} QObject *object; public slots: - void doCommand() { if (object) delete object; object = 0; } + void doCommand() { if (object) delete object; object = nullptr; } }; // Calling refresh expressions would crash if an expression or context was deleted during @@ -655,7 +690,7 @@ void tst_qqmlcontext::skipExpressionRefresh_qtbug_53431() { QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_53431.qml")); - QScopedPointer<QObject> object(component.create(0)); + QScopedPointer<QObject> object(component.create(nullptr)); QVERIFY(!object.isNull()); QCOMPARE(object->property("value").toInt(), 1); object->setProperty("value", 10); @@ -682,7 +717,7 @@ void tst_qqmlcontext::evalAfterInvalidate() QQmlComponent component(&engine, testFileUrl("evalAfterInvalidate.qml")); QScopedPointer<QObject> o(component.create()); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } @@ -723,6 +758,140 @@ void tst_qqmlcontext::qtbug_49232() QCOMPARE(obj->property("valueTwo"), QVariant(97)); } +void tst_qqmlcontext::contextViaClosureAfterDestruction() +{ + qmlRegisterSingletonType(testFileUrl("Singleton.qml"), "constants", 1, 0, "Sing"); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextViaClosureAfterDestruction.qml")); + QJSValue valueClosure; + QJSValue componentFactoryClosure; + { + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + // meta-calls don't support QJSValue return types, so do the call "by hand" + valueClosure = engine.newQObject(obj.data()).property(QStringLiteral("createClosure")).call(); + QVERIFY(valueClosure.isCallable()); + componentFactoryClosure = engine.newQObject(obj.data()).property(QStringLiteral("createComponentFactory")).call(); + QVERIFY(componentFactoryClosure.isCallable()); + } + QCOMPARE(valueClosure.call().toString(), QLatin1String("Highway to Hell")); + + QScopedPointer<QObject> parent(new QObject); + QJSValue parentWrapper = engine.newQObject(parent.data()); + QQmlEngine::setObjectOwnership(parent.data(), QQmlEngine::CppOwnership); + + QJSValue subObject = componentFactoryClosure.callWithInstance(componentFactoryClosure, QJSValueList() << parentWrapper); + QVERIFY(subObject.isError()); + QCOMPARE(subObject.toString(), QLatin1String("Error: Qt.createQmlObject(): Cannot create a component in an invalid context")); +} + +void tst_qqmlcontext::contextLeak() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextLeak.qml")); + + QQmlGuardedContextData scriptContext; + + { + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); + + QQmlData *ddata = QQmlData::get(obj.data()); + QVERIFY(ddata); + QQmlContextData *context = ddata->context; + QVERIFY(context); + QVERIFY(!context->importedScripts.isNullOrUndefined()); + QCOMPARE(int(context->importedScripts.valueRef()->as<QV4::Object>()->getLength()), 1); + + { + QV4::Scope scope(ddata->jsWrapper.engine()); + QV4::ScopedValue scriptContextWrapper(scope); + scriptContextWrapper = context->importedScripts.valueRef()->as<QV4::Object>()->get(uint(0)); + scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext(); + } + } + + engine.collectGarbage(); + + // Each time a JS file (non-pragma-shared) is imported, we create a QQmlContext(Data) for it. + // Make sure that context does not leak. + QVERIFY(scriptContext.isNull()); +} + + +static bool buildObjectList(QQmlContext *ctxt) +{ + static QHash<QObject *, QString> deletedObjects; + QQmlContextData *p = QQmlContextData::get(ctxt); + QObject *object = p->contextObject; + if (object) { + // If the object was actually deleted this is likely to crash in one way or another. + // Either the memory is still intact, then we will probably find the objectName, or it is + // not, then the connect() below is likely to fail. + if (deletedObjects.contains(object) && deletedObjects[object] == object->objectName()) + return false; + QObject::connect(object, &QObject::destroyed, [object]() { + object->setObjectName(QString::number(deletedObjects.size())); + deletedObjects.insert(object, object->objectName()); + }); + } + + QQmlContextData *child = p->childContexts; + while (child) { + if (!buildObjectList(child->asQQmlContext())) + return false; + child = child->nextChild; + } + + return true; +} + +void tst_qqmlcontext::outerContextObject() +{ + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("outerContextObject.qml")); + QVERIFY(component.isReady()); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + int iterations = 0; + QTimer timer; + timer.setInterval(1); + QObject::connect(&timer, &QTimer::timeout, &engine, [&]() { + if (!buildObjectList(engine.rootContext())) { + iterations = 100; + timer.stop(); + QFAIL("Deleted object found as context object"); + } else { + ++iterations; + } + }); + timer.start(); + + QTRY_VERIFY(iterations >= 100); +} + +void tst_qqmlcontext::contextObjectHierarchy() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("contextObjectHierarchy.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + + for (const QObject *child : root->children()) + QVERIFY(QQmlData::get(child)->outerContext != nullptr); + + connect(root.data(), &QObject::destroyed, [&root]() { + for (const QObject *child : root->children()) + QCOMPARE(QQmlData::get(child)->outerContext, nullptr); + }); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" diff --git a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp index 63b5de044e..99cabb4b09 100644 --- a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp +++ b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp @@ -102,7 +102,7 @@ void tst_qqmlcpputils::fastCast() } { - QObject *nullObj = 0; + QObject *nullObj = nullptr; QObject *obj = qmlobject_cast<QObject *>(nullObj); QCOMPARE(obj, nullObj); // shouldn't crash/assert. } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.1.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.1.qml index efca6cdb80..8912d2a314 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.1.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.1.qml @@ -11,25 +11,26 @@ MyTypeObject { dateTimeProperty = dateTimeVar dateTimeProperty2 = dateTimeVar2 - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getUTCDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && - (dateTimeProperty.getMinutes() == 0) && - (dateTimeProperty.getSeconds() == 1) && + (dateTimeProperty.getUTCDate() == 12) && + (dateTimeProperty.getUTCHours() == 0) && + (dateTimeProperty.getUTCMinutes() == 0) && + (dateTimeProperty.getUTCSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && - (dateTimeProperty2.getMinutes() == 59) && - (dateTimeProperty2.getSeconds() == 59) + (dateTimeProperty2.getUTCDate() == 12) && + (dateTimeProperty2.getUTCHours() == 23) && + (dateTimeProperty2.getUTCMinutes() == 59) && + (dateTimeProperty2.getUTCSeconds() == 59) } } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.2.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.2.qml index 71dd188f05..f8c1f6eb8f 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.2.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.2.qml @@ -12,24 +12,25 @@ MyTypeObject { var dateTimeVar = new Date("2009-05-12T00:00:01") var dateTimeVar2 = new Date("2009-05-12T23:59:59") - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getUTCDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && + (dateTimeProperty.getDate() == 12) && + (dateTimeProperty.getHours() == 0) && (dateTimeProperty.getMinutes() == 0) && (dateTimeProperty.getSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && + (dateTimeProperty2.getDate() == 12) && + (dateTimeProperty2.getHours() == 23) && (dateTimeProperty2.getMinutes() == 59) && (dateTimeProperty2.getSeconds() == 59) } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.3.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.3.qml index 2cf740645c..e960eef193 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.3.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.3.qml @@ -2,35 +2,36 @@ import Qt.test 1.0 import QtQuick 2.0 MyTypeObject { - dateProperty: if (1) "2009-05-12Z" + dateProperty: if (1) "2009-05-12" dateTimeProperty: if (1) "2009-05-12T00:00:01Z" dateTimeProperty2: if (1) "2009-05-12T23:59:59Z" boolProperty: false Component.onCompleted: { - var dateVar = new Date("2009-05-12Z") + var dateVar = new Date("2009-05-12") var dateTimeVar = new Date("2009-05-12T00:00:01Z") var dateTimeVar2 = new Date("2009-05-12T23:59:59Z") - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getUTCDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && - (dateTimeProperty.getMinutes() == 0) && - (dateTimeProperty.getSeconds() == 1) && + (dateTimeProperty.getUTCDate() == 12) && + (dateTimeProperty.getUTCHours() == 0) && + (dateTimeProperty.getUTCMinutes() == 0) && + (dateTimeProperty.getUTCSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && - (dateTimeProperty2.getMinutes() == 59) && - (dateTimeProperty2.getSeconds() == 59) + (dateTimeProperty2.getUTCDate() == 12) && + (dateTimeProperty2.getUTCHours() == 23) && + (dateTimeProperty2.getUTCMinutes() == 59) && + (dateTimeProperty2.getUTCSeconds() == 59) } } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.4.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.4.qml index 9b4975c833..6dd29afbc9 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.4.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.4.qml @@ -2,35 +2,36 @@ import Qt.test 1.0 import QtQuick 2.0 MyTypeObject { - dateProperty: if (1) new Date("2009-05-12Z") + dateProperty: if (1) new Date("2009-05-12") dateTimeProperty: if (1) new Date("2009-05-12T00:00:01Z") dateTimeProperty2: if (1) new Date("2009-05-12T23:59:59Z") boolProperty: false Component.onCompleted: { - var dateVar = new Date("2009-05-12Z") + var dateVar = new Date("2009-05-12") var dateTimeVar = new Date("2009-05-12T00:00:01Z") var dateTimeVar2 = new Date("2009-05-12T23:59:59Z") - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getUTCDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && - (dateTimeProperty.getMinutes() == 0) && - (dateTimeProperty.getSeconds() == 1) && + (dateTimeProperty.getUTCDate() == 12) && + (dateTimeProperty.getUTCHours() == 0) && + (dateTimeProperty.getUTCMinutes() == 0) && + (dateTimeProperty.getUTCSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && - (dateTimeProperty2.getMinutes() == 59) && - (dateTimeProperty2.getSeconds() == 59) + (dateTimeProperty2.getUTCDate() == 12) && + (dateTimeProperty2.getUTCHours() == 23) && + (dateTimeProperty2.getUTCMinutes() == 59) && + (dateTimeProperty2.getUTCSeconds() == 59) } } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.5.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.5.qml index 2cf740645c..cfadaafc54 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.5.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.5.qml @@ -2,35 +2,36 @@ import Qt.test 1.0 import QtQuick 2.0 MyTypeObject { - dateProperty: if (1) "2009-05-12Z" - dateTimeProperty: if (1) "2009-05-12T00:00:01Z" - dateTimeProperty2: if (1) "2009-05-12T23:59:59Z" + dateProperty: if (1) "2009-05-12" + dateTimeProperty: if (1) "2009-05-12T00:00:01+02:00" + dateTimeProperty2: if (1) "2009-05-12T23:59:59+02:00" boolProperty: false Component.onCompleted: { - var dateVar = new Date("2009-05-12Z") - var dateTimeVar = new Date("2009-05-12T00:00:01Z") - var dateTimeVar2 = new Date("2009-05-12T23:59:59Z") + var dateVar = new Date("2009-05-12") + var dateTimeVar = new Date("2009-05-12T00:00:01+02:00") + var dateTimeVar2 = new Date("2009-05-12T23:59:59+02:00") - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getUTCDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && - (dateTimeProperty.getMinutes() == 0) && - (dateTimeProperty.getSeconds() == 1) && + (dateTimeProperty.getUTCDate() == 11) && + (dateTimeProperty.getUTCHours() == 22) && + (dateTimeProperty.getUTCMinutes() == 0) && + (dateTimeProperty.getUTCSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && - (dateTimeProperty2.getMinutes() == 59) && - (dateTimeProperty2.getSeconds() == 59) + (dateTimeProperty2.getUTCDate() == 12) && + (dateTimeProperty2.getUTCHours() == 21) && + (dateTimeProperty2.getUTCMinutes() == 59) && + (dateTimeProperty2.getUTCSeconds() == 59) } } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.6.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.6.qml index 73e26db0c8..97cd0d1e60 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.6.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.6.qml @@ -3,34 +3,35 @@ import QtQuick 2.0 MyTypeObject { dateProperty: if (1) new Date("2009-05-12") - dateTimeProperty: if (1) new Date("2009-05-12T02:00:01+02:00") - dateTimeProperty2: if (1) new Date("2009-05-13T01:59:59+02:00") + dateTimeProperty: if (1) new Date("2009-05-12T00:00:01+02:00") + dateTimeProperty2: if (1) new Date("2009-05-12T23:59:59+02:00") boolProperty: false Component.onCompleted: { var dateVar = new Date("2009-05-12") - var dateTimeVar = new Date("2009-05-12T02:00:01+02:00") - var dateTimeVar2 = new Date("2009-05-13T01:59:59+02:00") + var dateTimeVar = new Date("2009-05-12T00:00:01+02:00") + var dateTimeVar2 = new Date("2009-05-12T23:59:59+02:00") - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getUTCDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && - (dateTimeProperty.getMinutes() == 0) && - (dateTimeProperty.getSeconds() == 1) && + (dateTimeProperty.getUTCDate() == 11) && + (dateTimeProperty.getUTCHours() == 22) && + (dateTimeProperty.getUTCMinutes() == 0) && + (dateTimeProperty.getUTCSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && - (dateTimeProperty2.getMinutes() == 59) && - (dateTimeProperty2.getSeconds() == 59) + (dateTimeProperty2.getUTCDate() == 12) && + (dateTimeProperty2.getUTCHours() == 21) && + (dateTimeProperty2.getUTCMinutes() == 59) && + (dateTimeProperty2.getUTCSeconds() == 59) } } diff --git a/tests/auto/qml/qqmlecmascript/data/assignDate.qml b/tests/auto/qml/qqmlecmascript/data/assignDate.qml index 14fe20787b..73677e99f1 100644 --- a/tests/auto/qml/qqmlecmascript/data/assignDate.qml +++ b/tests/auto/qml/qqmlecmascript/data/assignDate.qml @@ -7,28 +7,31 @@ MyTypeObject { var dateTimeVar = new Date("2009-05-12T00:00:01") var dateTimeVar2 = new Date("2009-05-12T23:59:59") + // Date, with no zone specified, is implicitly UTC dateProperty = dateVar + // Date-time, with no zone, is implicitly local-time dateTimeProperty = dateTimeVar dateTimeProperty2 = dateTimeVar2 - // Commented properties do not currently test true: - boolProperty = //(dateProperty.getTime() == dateVar.getTime()) && + boolProperty = (dateProperty.getTime() == dateVar.getTime()) && (dateProperty.getFullYear() == 2009) && (dateProperty.getMonth() == 5-1) && - //(dateProperty.getDate() == 12) && - (dateProperty.getHours() == 0) && + (dateProperty.getDate() == 12) && + (dateProperty.getUTCHours() == 0) && + (dateProperty.getUTCMinutes() == 0) && + (dateProperty.getUTCSeconds() == 0) && (dateTimeProperty.getTime() == dateTimeVar.getTime()) && (dateTimeProperty.getFullYear() == 2009) && (dateTimeProperty.getMonth() == 5-1) && - //(dateTimeProperty.getDate() == 12) && - //(dateTimeProperty.getHours() == 0) && + (dateTimeProperty.getDate() == 12) && + (dateTimeProperty.getHours() == 0) && (dateTimeProperty.getMinutes() == 0) && (dateTimeProperty.getSeconds() == 1) && (dateTimeProperty2.getTime() == dateTimeVar2.getTime()) && (dateTimeProperty2.getFullYear() == 2009) && (dateTimeProperty2.getMonth() == 5-1) && - //(dateTimeProperty2.getDate() == 12) && - //(dateTimeProperty2.getHours() == 23) && + (dateTimeProperty2.getDate() == 12) && + (dateTimeProperty2.getHours() == 23) && (dateTimeProperty2.getMinutes() == 59) && (dateTimeProperty2.getSeconds() == 59) } diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.8.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.8.qml new file mode 100644 index 0000000000..3f838fe8f2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.8.qml @@ -0,0 +1,16 @@ +import QtQml 2.0 +QtObject { + function tryWritingReadOnlySequence() { + try { + Qt.application.arguments.push("hello") + } catch (e) { + + try { + Qt.application.arguments.sort() + } catch (e) { + return true + } + } + return false + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml b/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml new file mode 100644 index 0000000000..8dbd2fd3d9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml @@ -0,0 +1,34 @@ +import QtQuick 2.6 + +QtObject { + property bool success: false + property var num: 100 + property var simple: 0 + property var complex: 0 + + + Component.onCompleted: { + function s(x) { + return x + } + function c(x) { + return x + num + } + + var bound = s.bind(undefined, 100) + simple = Qt.binding(bound) + if (simple != 100) + return; + var bound = c.bind(undefined, 100) + complex = Qt.binding(bound); + + if (complex != 200) + return; + num = 0; + if (complex != 100) + return; + + print("success!!!"); + success = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicString.qml b/tests/auto/qml/qqmlecmascript/data/dynamicString.qml index 5693794c71..c704161eb5 100644 --- a/tests/auto/qml/qqmlecmascript/data/dynamicString.qml +++ b/tests/auto/qml/qqmlecmascript/data/dynamicString.qml @@ -11,6 +11,6 @@ MyTypeObject { date.setHours(5); date.setMinutes(30); date.setSeconds(50); - stringProperty = stringProperty.arg("Hello World").arg(false).arg(true).arg(100).arg(-100).arg(3.1415926).arg(Qt.formatDateTime(date, "yyyy-MM-dd hh::mm:ss")); + stringProperty = stringProperty.arg("Hello World").arg(false).arg(true).arg(100).arg(-100).arg(Math.PI).arg(Qt.formatDateTime(date, "yyyy-MM-dd hh::mm:ss")); } } diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables.mjs b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables.mjs new file mode 100644 index 0000000000..19c012d19b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables.mjs @@ -0,0 +1,31 @@ +export function runTest(libraryUnderTest) { + let state1 = state(libraryUnderTest); + try { modifyFromOutside(libraryUnderTest); } catch (e) {} + let state2 = state(libraryUnderTest); + try { modifyFromInside(libraryUnderTest); } catch (e) {} + let state3 = state(libraryUnderTest); + return state1 + " " + state2 + " " + state3; +} + +function stringify(value) { + let s = "?"; + if (value !== undefined) + s = value.toString(); + return s; +} + +function state(libraryUnderTest) { + return (stringify(libraryUnderTest.varValue) + + stringify(libraryUnderTest.letValue) + + stringify(libraryUnderTest.constValue)); +} + +function modifyFromOutside(libraryUnderTest) { + ++libraryUnderTest.varValue; + ++libraryUnderTest.letValue; + ++libraryUnderTest.constValue; +} + +function modifyFromInside(libraryUnderTest) { + libraryUnderTest.incrementAll(); +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.mjs b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.mjs new file mode 100644 index 0000000000..b6eb1a2623 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.mjs @@ -0,0 +1,8 @@ +export var varValue = 0; +export let letValue = 0; +export const constValue = 0; +export function incrementAll() { + ++varValue; + ++letValue; + ++constValue; +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.qml b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.qml new file mode 100644 index 0000000000..bb4e759cbf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import "importLexicalVariables.mjs" as TestRunner +import "importLexicalVariables_module.mjs" as LibraryUnderTest + +QtObject { + id: root + function runTest() { + return TestRunner.runTest(LibraryUnderTest); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.js b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.js new file mode 100644 index 0000000000..f8c215a5e7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.js @@ -0,0 +1,9 @@ +.pragma library +var varValue = 0; +let letValue = 0; +const constValue = 0; +function incrementAll() { + ++varValue; + ++letValue; + ++constValue; +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.qml b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.qml new file mode 100644 index 0000000000..1b3fe7fa2e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import "importLexicalVariables.mjs" as TestRunner +import "importLexicalVariables_pragmaLibrary.js" as LibraryUnderTest + +QtObject { + id: root + function runTest() { + return TestRunner.runTest(LibraryUnderTest); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.js b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.js new file mode 100644 index 0000000000..4fb68abc87 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.js @@ -0,0 +1,8 @@ +var varValue = 0; +let letValue = 0; +const constValue = 0; +function incrementAll() { + ++varValue; + ++letValue; + ++constValue; +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.qml b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.qml new file mode 100644 index 0000000000..263511e802 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import "importLexicalVariables.mjs" as TestRunner +import "importLexicalVariables_script.js" as LibraryUnderTest + +QtObject { + id: root + function runTest() { + return TestRunner.runTest(LibraryUnderTest); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_callback.js b/tests/auto/qml/qqmlecmascript/data/include_callback.js index ea19eba300..7f3195bb1f 100644 --- a/tests/auto/qml/qqmlecmascript/data/include_callback.js +++ b/tests/auto/qml/qqmlecmascript/data/include_callback.js @@ -1,6 +1,6 @@ function go() { var a = Qt.include("missing.js", function(o) { test2 = o.status == o.NETWORK_ERROR }); - test1 = a.status == a.NETWORK_ERROR + test1 = (a.status == a.NETWORK_ERROR) && (a.statusText.indexOf("Error opening source file") != -1); var b = Qt.include("blank.js", function(o) { test4 = o.status == o.OK }); test3 = b.status == b.OK diff --git a/tests/auto/qml/qqmlecmascript/data/include_pragma_shadow.js b/tests/auto/qml/qqmlecmascript/data/include_pragma_shadow.js new file mode 100644 index 0000000000..500f04bec7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_pragma_shadow.js @@ -0,0 +1,7 @@ +.pragma library +var Shadowed = 2; +var global = (function(){return this})() + +// set Shadowed on the global object as well. This should be different from +// the variable above, as the library has it's on context +global.Shadowed = 1; diff --git a/tests/auto/qml/qqmlecmascript/data/include_pragma_shadow.qml b/tests/auto/qml/qqmlecmascript/data/include_pragma_shadow.qml new file mode 100644 index 0000000000..7cac09d342 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_pragma_shadow.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import "include_pragma_shadow.js" as Shadowed +import "include_pragma_shadow.js" as Other + +Item { + property bool result + + Component.onCompleted: { + result = false; + var global = (function(){return this})() + if (Shadowed.Shadowed === 2 && Other.Shadowed === 2 && global.Shadowed === 1) + result = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/js/include2.js b/tests/auto/qml/qqmlecmascript/data/js/include2.js index 2a0c039dfa..7cfcdd95e2 100644 --- a/tests/auto/qml/qqmlecmascript/data/js/include2.js +++ b/tests/auto/qml/qqmlecmascript/data/js/include2.js @@ -2,3 +2,8 @@ test2 = true var test2_1 = true Qt.include("include3.js"); + +function withTokensAllowedInJSButKeywordsInQML(char) +{ + var double; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js index fa6497d99b..66e18ac2a9 100644 --- a/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js @@ -7,5 +7,5 @@ function importIncrementedValue() { i = i + 1; // because LibraryImport is shared, and used in previous tests, // the value will be large (already incremented a bunch of times). - return (i + LibraryImport.importIncrementedValue()); + return (i + LibraryImport.importIncrementedValue()); // 11 + 5 } diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml index 01f08dbdc3..8264b7229d 100644 --- a/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml @@ -3,5 +3,5 @@ import "importPragmaLibraryWithPragmaLibraryImports.js" as LibraryImport QtObject { id: root - property int testValue: LibraryImport.importIncrementedValue(); // 10 + 1 + (7 due to previous tests) = 18 + property int testValue: LibraryImport.importIncrementedValue(); // 16 } diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsImport.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsImport.qml index ae43e90210..0e314b20ea 100644 --- a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsImport.qml +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsImport.qml @@ -1,7 +1,7 @@ import QtQuick 2.0 -import com.nokia.JsModule 1.0 -import com.nokia.JsModule 1.0 as RenamedModule +import com.qt.JsModule 1.0 +import com.qt.JsModule 1.0 as RenamedModule import "testJsModuleImport.js" as TestJsModuleImport QtObject { diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleImport.js b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleImport.js index 2d21953d2c..7440f610c1 100644 --- a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleImport.js +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleImport.js @@ -1,4 +1,4 @@ -.import com.nokia.JsModule 1.0 as JsModule +.import com.qt.JsModule 1.0 as JsModule function importedValue() { return JsModule.ScriptAPI.greeting(); diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleRemoteImport.js b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleRemoteImport.js index e6e41bc6b2..6826f09da2 100644 --- a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleRemoteImport.js +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsModuleRemoteImport.js @@ -1,4 +1,4 @@ -.import com.nokia.JsRemoteModule 1.0 as JsModule +.import com.qt.JsRemoteModule 1.0 as JsModule function importedValue() { return JsModule.ScriptAPI.greeting(); diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsRemoteImport.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsRemoteImport.qml index 4199bb022d..f49b38df23 100644 --- a/tests/auto/qml/qqmlecmascript/data/jsimport/testJsRemoteImport.qml +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testJsRemoteImport.qml @@ -1,7 +1,7 @@ import QtQuick 2.0 -import com.nokia.JsModule 1.0 -import com.nokia.JsModule 1.0 as RenamedModule +import com.qt.JsModule 1.0 +import com.qt.JsModule 1.0 as RenamedModule import "testJsModuleRemoteImport.js" as TestJsModuleImport QtObject { diff --git a/tests/auto/qml/qqmlecmascript/data/lib/com/nokia/JsModule/ScriptAPI.js b/tests/auto/qml/qqmlecmascript/data/lib/com/qt/JsModule/ScriptAPI.js index b90033eeb4..b90033eeb4 100644 --- a/tests/auto/qml/qqmlecmascript/data/lib/com/nokia/JsModule/ScriptAPI.js +++ b/tests/auto/qml/qqmlecmascript/data/lib/com/qt/JsModule/ScriptAPI.js diff --git a/tests/auto/qml/qqmlecmascript/data/lib/com/nokia/JsModule/qmldir b/tests/auto/qml/qqmlecmascript/data/lib/com/qt/JsModule/qmldir index c33d1e7a0d..c33d1e7a0d 100644 --- a/tests/auto/qml/qqmlecmascript/data/lib/com/nokia/JsModule/qmldir +++ b/tests/auto/qml/qqmlecmascript/data/lib/com/qt/JsModule/qmldir diff --git a/tests/auto/qml/qqmlecmascript/data/nans.qml b/tests/auto/qml/qqmlecmascript/data/nans.qml new file mode 100644 index 0000000000..ece69f2d79 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nans.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property var prop: undefined +} diff --git a/tests/auto/qml/qqmlecmascript/data/nonNotifyableConstant.qml b/tests/auto/qml/qqmlecmascript/data/nonNotifyableConstant.qml new file mode 100644 index 0000000000..424b3e8b07 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nonNotifyableConstant.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + objectName: trueProperty ? "foo" : "bar" +} diff --git a/tests/auto/qml/qqmlecmascript/data/qmlVarNullBinding.qml b/tests/auto/qml/qqmlecmascript/data/qmlVarNullBinding.qml new file mode 100644 index 0000000000..6666b85ffd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qmlVarNullBinding.qml @@ -0,0 +1,7 @@ +import QtQml 2.2 + +QtObject { + property var foo: null + property bool signalSeen: false + onFooChanged: signalSeen = true +} diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml new file mode 100644 index 0000000000..b22f8ab71e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + regularExpression: "[a-zA-z]" +} diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml new file mode 100644 index 0000000000..6f31ffd305 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + regularExpression: /[a-zA-z]/ +} diff --git a/tests/auto/qml/qqmlecmascript/data/remote/com/nokia/JsRemoteModule/ScriptAPI.js b/tests/auto/qml/qqmlecmascript/data/remote/com/qt/JsRemoteModule/ScriptAPI.js index b90033eeb4..b90033eeb4 100644 --- a/tests/auto/qml/qqmlecmascript/data/remote/com/nokia/JsRemoteModule/ScriptAPI.js +++ b/tests/auto/qml/qqmlecmascript/data/remote/com/qt/JsRemoteModule/ScriptAPI.js diff --git a/tests/auto/qml/qqmlecmascript/data/remote/com/nokia/JsRemoteModule/qmldir b/tests/auto/qml/qqmlecmascript/data/remote/com/qt/JsRemoteModule/qmldir index c33d1e7a0d..c33d1e7a0d 100644 --- a/tests/auto/qml/qqmlecmascript/data/remote/com/nokia/JsRemoteModule/qmldir +++ b/tests/auto/qml/qqmlecmascript/data/remote/com/qt/JsRemoteModule/qmldir diff --git a/tests/auto/qml/qqmlecmascript/data/removeBindingsWithNoDependencies.qml b/tests/auto/qml/qqmlecmascript/data/removeBindingsWithNoDependencies.qml new file mode 100644 index 0000000000..aacf16474d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/removeBindingsWithNoDependencies.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +Item { + property rect placement: Qt.rect(0, 0, 100, 100) + + function someFunction() { return 42; } + + property rect partialPlacement + partialPlacement.x: someFunction() +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml index 5103168fd3..99c49ebf62 100644 --- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml @@ -10,6 +10,11 @@ Item { objectName: "msco" } + Component { + id: mscoComponent + MySequenceConversionObject { } + } + property bool success: false property variant intList @@ -252,4 +257,13 @@ Item { if (testSequence.valueOf() == prevValueOf) referenceDeletion = false; if (testSequence.length == prevLength) referenceDeletion = false; } + + function jsonConversion() { + success = true + var msco = mscoComponent.createObject() + if (JSON.stringify(msco.intListProperty) != "[1,2,3,4]") success = false; + if (JSON.stringify(msco.qrealListProperty) != "[1.1,2.2,3.3,4.4]") success = false; + if (JSON.stringify(msco.boolListProperty) != "[true,false,true,false]") success = false; + if (JSON.stringify(msco.stringListProperty) != "[\"first\",\"second\",\"third\",\"fourth\"]") success = false; + } } diff --git a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml index cd68fb9b82..14326bb9e6 100644 --- a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml +++ b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml @@ -102,4 +102,17 @@ QtObject { }) return testSuccess } + + property QtObject subObject: QtObject { + id: subObject + property int value + property bool ok: false + onValueChanged: this.ok = true + } + + function testThisInSignalHandler() { + subObject.ok = false + subObject.value = subObject.value + 1 + return subObject.ok + } } diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml index ca3784322a..1c8984ce8c 100644 --- a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml +++ b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml @@ -1,12 +1,22 @@ /**************************************************************************** ** ** Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies). -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml index ef08745812..85549f4e87 100644 --- a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml +++ b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml @@ -1,12 +1,22 @@ /**************************************************************************** ** ** Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies). -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are diff --git a/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml b/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml index 04b39f73d5..7f5a22a459 100644 --- a/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml +++ b/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml @@ -8,6 +8,6 @@ MyQmlObject { return 321 } - value: try { var p = go() } catch(e) { var p = defaultValue } finally { p == 123 } + qjsvalue: try { var p = go() } catch(e) { var p = defaultValue } finally { p == 123 } } diff --git a/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml b/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml index 231aaf0683..39d4f74e97 100644 --- a/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml +++ b/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml @@ -7,6 +7,6 @@ MyQmlObject { return 321 } - value: try { var p = go() } catch(e) { var p = defaultValue } finally { p == 321 } + qjsvalue: try { var p = go() } catch(e) { var p = defaultValue } finally { p == 321 } } diff --git a/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro b/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro index 101181bba0..0dd9365366 100644 --- a/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro +++ b/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro @@ -7,7 +7,6 @@ SOURCES += tst_qqmlecmascript.cpp \ ../../shared/testhttpserver.cpp HEADERS += testtypes.h \ ../../shared/testhttpserver.h -INCLUDEPATH += ../../shared RESOURCES += qqmlecmascript.qrc diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index c4692fdf31..89887fe6c3 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -56,7 +56,7 @@ class AbstractExtensionObject : public QObject Q_PROPERTY(int abstractProperty READ abstractProperty WRITE setAbstractProperty NOTIFY abstractPropertyChanged) public: - AbstractExtensionObject(QObject *parent = 0) : QObject(parent), m_abstractProperty(-1) {} + AbstractExtensionObject(QObject *parent = nullptr) : QObject(parent), m_abstractProperty(-1) {} void setAbstractProperty(int abstractProperty) { m_abstractProperty = abstractProperty; emit abstractPropertyChanged(); } int abstractProperty() const { return m_abstractProperty; } @@ -75,7 +75,7 @@ class ImplementedExtensionObject : public AbstractExtensionObject Q_OBJECT Q_PROPERTY(int implementedProperty READ implementedProperty WRITE setImplementedProperty NOTIFY implementedPropertyChanged) public: - ImplementedExtensionObject(QObject *parent = 0) : AbstractExtensionObject(parent), m_implementedProperty(883) {} + ImplementedExtensionObject(QObject *parent = nullptr) : AbstractExtensionObject(parent), m_implementedProperty(883) {} void shouldBeImplemented() {} void setImplementedProperty(int implementedProperty) { m_implementedProperty = implementedProperty; emit implementedPropertyChanged(); } @@ -245,9 +245,16 @@ public: MyWorkerObject *o; }; +MyWorkerObject::~MyWorkerObject() +{ + if (m_thread) + m_thread->wait(); +} + void MyWorkerObject::doIt() { - new MyWorkerObjectThread(this); + Q_ASSERT(!m_thread); + m_thread = new MyWorkerObjectThread(this); } class MyDateClass : public QObject @@ -333,7 +340,7 @@ public: } }; -static MyInheritedQmlObject *theSingletonObject = 0; +static MyInheritedQmlObject *theSingletonObject = nullptr; static QObject *inheritedQmlObject_provider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */) { @@ -375,7 +382,7 @@ private: static int a = 0; static int *ptr = &a; *ptr = 1; - ptr = 0; + ptr = nullptr; } }; @@ -394,13 +401,13 @@ static QObject *create_singletonWithEnum(QQmlEngine *, QJSEngine *) } QObjectContainer::QObjectContainer() - : widgetParent(0) + : widgetParent(nullptr) , gcOnAppend(false) {} QQmlListProperty<QObject> QObjectContainer::data() { - return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear); + return QQmlListProperty<QObject>(this, nullptr, children_append, children_count, children_at, children_clear); } void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject *o) @@ -412,7 +419,7 @@ void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject if (that->gcOnAppend) { QQmlEngine *engine = qmlEngine(that); engine->collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } } @@ -441,7 +448,7 @@ void QObjectContainer::childDestroyed(QObject *child) { void FloatingQObject::classBegin() { - setParent(0); + setParent(nullptr); } void FloatingQObject::componentComplete() diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index eedeb66647..32120ee5b7 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -33,6 +33,7 @@ #include <QtQml/qqmlexpression.h> #include <QtCore/qpoint.h> #include <QtCore/qsize.h> +#include <QtCore/qregularexpression.h> #include <QtQml/qqmllist.h> #include <QtCore/qrect.h> #include <QtGui/qmatrix.h> @@ -101,6 +102,7 @@ class MyQmlObject : public QObject Q_PROPERTY(QQmlListProperty<QObject> objectListProperty READ objectListProperty CONSTANT) Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty) Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp) + Q_PROPERTY(QRegularExpression regularExpression READ regularExpression WRITE setRegularExpression) Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false) Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged) Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged) @@ -170,6 +172,12 @@ public: QRegExp regExp() { return m_regExp; } void setRegExp(const QRegExp ®Exp) { m_regExp = regExp; } + QRegularExpression regularExpression() { return m_regularExpression; } + void setRegularExpression(const QRegularExpression ®ularExpression) + { + m_regularExpression = regularExpression; + } + int console() const { return 11; } int nonscriptable() const { return 0; } @@ -270,6 +278,7 @@ private: int m_value; int m_resetProperty; QRegExp m_regExp; + QRegularExpression m_regularExpression; QVariant m_variant; QJSValue m_qjsvalue; int m_intProperty; @@ -1262,7 +1271,6 @@ public: { CircularReferenceObject *retn = new CircularReferenceObject(parent); retn->m_dtorCount = m_dtorCount; - retn->m_engine = m_engine; return retn; } @@ -1283,14 +1291,8 @@ public: thisObject->defineDefaultProperty(QStringLiteral("autoTestStrongRef"), v); } - void setEngine(QQmlEngine* declarativeEngine) - { - m_engine = QQmlEnginePrivate::get(declarativeEngine)->v8engine(); - } - private: int *m_dtorCount; - QV8Engine* m_engine; }; Q_DECLARE_METATYPE(CircularReferenceObject*) @@ -1509,7 +1511,7 @@ public: } break; case Qt::OffsetFromUTC: - m_offset = m_datetime.utcOffset() / 60; + m_offset = m_datetime.offsetFromUtc() / 60; m_timespec = QString("%1%2:%3").arg(m_offset < 0 ? '-' : '+') .arg(abs(m_offset) / 60) .arg(abs(m_offset) % 60); @@ -1532,12 +1534,17 @@ private: class MyWorkerObject : public QObject { Q_OBJECT +public: + ~MyWorkerObject(); public Q_SLOTS: void doIt(); Q_SIGNALS: void done(const QString &result); + +private: + QThread *m_thread = 0; }; class MyUnregisteredEnumTypeObject : public QObject @@ -1665,7 +1672,8 @@ class SingletonWithEnum : public QObject Q_ENUMS(TestEnum) public: enum TestEnum { - TestValue = 42 + TestValue = 42, + TestValue_MinusOne = -1 }; }; diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 45f312e934..b4349f79ca 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -43,11 +43,14 @@ #include "../../shared/util.h" #include <private/qv4functionobject_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4alloca_p.h> #include <private/qv4runtime_p.h> #include <private/qv4object_p.h> #include <private/qqmlcomponentattached_p.h> #include <private/qv4objectiterator_p.h> +#include <private/qqmlabstractbinding_p.h> +#include <private/qqmlvaluetypeproxybinding_p.h> #ifdef Q_CC_MSVC #define NO_INLINE __declspec(noinline) @@ -260,6 +263,7 @@ private slots: void doubleEvaluate(); void forInLoop(); void nonNotifyable(); + void nonNotifyableConstant(); void deleteWhileBindingRunning(); void callQtInvokables(); void resolveClashingProperties(); @@ -287,6 +291,7 @@ private slots: void withStatement(); void tryStatement(); void replaceBinding(); + void bindingBoundFunctions(); void deleteRootObjectInCreation(); void onDestruction(); void onDestructionViaGC(); @@ -342,11 +347,27 @@ private slots: void freeze_empty_object(); void singleBlockLoops(); void qtbug_60547(); + void delayLoadingArgs(); + void manyArguments(); + void forInIterator(); + void localForInIterator(); + void shadowedFunctionName(); + void anotherNaN(); + void callPropertyOnUndefined(); + void jumpStrictNotEqualUndefined(); + void removeBindingsWithNoDependencies(); + void temporaryDeadZone(); + void importLexicalVariables_data(); + void importLexicalVariables(); + void hugeObject(); + void templateStringTerminator(); + void arrayAndException(); + void numberToStringWithRadix(); + void tailCallWithArguments(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); static void verifyContextLifetime(QQmlContextData *ctxt); - QQmlEngine engine; // When calling into JavaScript, the specific type of the return value can differ if that return // value is a number. This is not only the case for non-integral numbers, or numbers that do not @@ -364,7 +385,7 @@ private: static void gc(QQmlEngine &engine) { engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } @@ -373,17 +394,15 @@ void tst_qqmlecmascript::initTestCase() { QQmlDataTest::initTestCase(); registerTypes(); - - QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib")); - engine.addImportPath(dataDir); } void tst_qqmlecmascript::assignBasicTypes() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue); @@ -412,7 +431,7 @@ void tst_qqmlecmascript::assignBasicTypes() { QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue); @@ -443,29 +462,38 @@ void tst_qqmlecmascript::assignBasicTypes() void tst_qqmlecmascript::assignDate_data() { QTest::addColumn<QUrl>("source"); + QTest::addColumn<int>("timeOffset"); // -1 for local-time, else minutes from UTC - QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml"); - QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml"); - QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml"); - QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml"); - QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml"); - QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml"); - QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml"); + QTest::newRow("Component.onComplete JS Parse") << testFileUrl("assignDate.qml") << -1; + QTest::newRow("Component.onComplete JS") << testFileUrl("assignDate.1.qml") << 0; + QTest::newRow("Binding JS") << testFileUrl("assignDate.2.qml") << -1; + QTest::newRow("Binding UTC") << testFileUrl("assignDate.3.qml") << 0; + QTest::newRow("Binding JS UTC") << testFileUrl("assignDate.4.qml") << 0; + QTest::newRow("Binding UTC+2") << testFileUrl("assignDate.5.qml") << 120; + QTest::newRow("Binding JS UTC+2 ") << testFileUrl("assignDate.6.qml") << 120; } void tst_qqmlecmascript::assignDate() { QFETCH(QUrl, source); + QFETCH(int, timeOffset); + QQmlEngine engine; QQmlComponent component(&engine, source); QScopedPointer<QObject> obj(component.create()); MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data()); - QVERIFY(object != 0); - - // Dates received from JS are automatically converted to local time - QDate expectedDate(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 0), Qt::UTC).toLocalTime().date()); - QDateTime expectedDateTime(QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::UTC).toLocalTime()); - QDateTime expectedDateTime2(QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::UTC).toLocalTime()); + QVERIFY(object != nullptr); + + QDate expectedDate(2009, 5, 12); + QDateTime expectedDateTime; + QDateTime expectedDateTime2; + if (timeOffset == -1) { + expectedDateTime = QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::LocalTime); + expectedDateTime2 = QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::LocalTime); + } else { + expectedDateTime = QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::OffsetFromUTC, timeOffset * 60); + expectedDateTime2 = QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::OffsetFromUTC, timeOffset * 60); + } QCOMPARE(object->dateProperty(), expectedDate); QCOMPARE(object->dateTimeProperty(), expectedDateTime); @@ -492,22 +520,22 @@ void tst_qqmlecmascript::exportDate_data() QTest::newRow("UTC late") << testFileUrl("exportDate.4.qml") << QDateTime(date, late, Qt::UTC); { QDateTime dt(date, early, Qt::OffsetFromUTC); - dt.setUtcOffset(offset); + dt.setOffsetFromUtc(offset); QTest::newRow("+11:30 early") << testFileUrl("exportDate.5.qml") << dt; } { QDateTime dt(date, late, Qt::OffsetFromUTC); - dt.setUtcOffset(offset); + dt.setOffsetFromUtc(offset); QTest::newRow("+11:30 late") << testFileUrl("exportDate.6.qml") << dt; } { QDateTime dt(date, early, Qt::OffsetFromUTC); - dt.setUtcOffset(-offset); + dt.setOffsetFromUtc(-offset); QTest::newRow("-11:30 early") << testFileUrl("exportDate.7.qml") << dt; } { QDateTime dt(date, late, Qt::OffsetFromUTC); - dt.setUtcOffset(-offset); + dt.setOffsetFromUtc(-offset); QTest::newRow("-11:30 late") << testFileUrl("exportDate.8.qml") << dt; } } @@ -525,17 +553,18 @@ void tst_qqmlecmascript::exportDate() QQmlComponent component(&e, source); QScopedPointer<QObject> obj(component.create()); MyTypeObject *object = qobject_cast<MyTypeObject *>(obj.data()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->boolProperty(), true); } void tst_qqmlecmascript::idShortcutInvalidates() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); - QVERIFY(object->objectProperty() != 0); + QVERIFY(object != nullptr); + QVERIFY(object->objectProperty() != nullptr); delete object->objectProperty(); QVERIFY(!object->objectProperty()); delete object; @@ -544,8 +573,8 @@ void tst_qqmlecmascript::idShortcutInvalidates() { QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); - QVERIFY(object->objectProperty() != 0); + QVERIFY(object != nullptr); + QVERIFY(object->objectProperty() != nullptr); delete object->objectProperty(); QVERIFY(!object->objectProperty()); delete object; @@ -554,17 +583,18 @@ void tst_qqmlecmascript::idShortcutInvalidates() void tst_qqmlecmascript::boolPropertiesEvaluateAsBool() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->stringProperty(), QLatin1String("pass")); delete object; } { QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->stringProperty(), QLatin1String("pass")); delete object; } @@ -572,10 +602,11 @@ void tst_qqmlecmascript::boolPropertiesEvaluateAsBool() void tst_qqmlecmascript::signalAssignment() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->string(), QString()); emit object->basicSignal(); QCOMPARE(object->string(), QString("pass")); @@ -585,7 +616,7 @@ void tst_qqmlecmascript::signalAssignment() { QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->string(), QString()); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2")); @@ -609,10 +640,11 @@ void tst_qqmlecmascript::signalAssignment() void tst_qqmlecmascript::signalArguments() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("signalArguments.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->string(), QString()); emit object->basicSignal(); QCOMPARE(object->string(), QString("pass")); @@ -623,7 +655,7 @@ void tst_qqmlecmascript::signalArguments() { QQmlComponent component(&engine, testFileUrl("signalArguments.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->string(), QString()); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2")); @@ -634,10 +666,11 @@ void tst_qqmlecmascript::signalArguments() void tst_qqmlecmascript::methods() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("methods.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->methodCalled(), false); QCOMPARE(object->methodIntCalled(), false); emit object->basicSignal(); @@ -649,7 +682,7 @@ void tst_qqmlecmascript::methods() { QQmlComponent component(&engine, testFileUrl("methods.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->methodCalled(), false); QCOMPARE(object->methodIntCalled(), false); emit object->basicSignal(); @@ -661,7 +694,7 @@ void tst_qqmlecmascript::methods() { QQmlComponent component(&engine, testFileUrl("methods.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 19); delete object; } @@ -669,7 +702,7 @@ void tst_qqmlecmascript::methods() { QQmlComponent component(&engine, testFileUrl("methods.4.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 19); QCOMPARE(object->property("test2").toInt(), 17); QCOMPARE(object->property("test3").toInt(), 16); @@ -679,7 +712,7 @@ void tst_qqmlecmascript::methods() { QQmlComponent component(&engine, testFileUrl("methods.5.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 9); delete object; } @@ -687,11 +720,12 @@ void tst_qqmlecmascript::methods() void tst_qqmlecmascript::bindingLoop() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("bindingLoop.qml")); QString warning = component.url().toString() + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\""; QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -728,6 +762,8 @@ void tst_qqmlecmascript::basicExpressions() QFETCH(QVariant, result); QFETCH(bool, nest); + QQmlEngine engine; + MyQmlObject object1; MyQmlObject object2; MyQmlObject object3; @@ -760,6 +796,7 @@ void tst_qqmlecmascript::arrayExpressions() QObject obj2; QObject obj3; + QQmlEngine engine; QQmlContext context(engine.rootContext()); context.setContextProperty("a", &obj1); context.setContextProperty("b", &obj2); @@ -779,6 +816,7 @@ void tst_qqmlecmascript::arrayExpressions() // Tests that modifying a context property will reevaluate expressions void tst_qqmlecmascript::contextPropertiesTriggerReeval() { + QQmlEngine engine; QQmlContext context(engine.rootContext()); MyQmlObject object1; MyQmlObject object2; @@ -842,6 +880,7 @@ void tst_qqmlecmascript::contextPropertiesTriggerReeval() void tst_qqmlecmascript::objectPropertiesTriggerReeval() { + QQmlEngine engine; QQmlContext context(engine.rootContext()); MyQmlObject object1; MyQmlObject object2; @@ -877,7 +916,7 @@ void tst_qqmlecmascript::objectPropertiesTriggerReeval() expr.changed = false; QCOMPARE(expr.evaluate(), QVariant("Cat")); - object1.setObjectProperty(0); + object1.setObjectProperty(nullptr); QCOMPARE(expr.changed, true); expr.changed = false; QCOMPARE(expr.evaluate(), QVariant()); @@ -908,19 +947,20 @@ void tst_qqmlecmascript::dependenciesWithFunctions() void tst_qqmlecmascript::deferredProperties() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); MyDeferredObject *object = qobject_cast<MyDeferredObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->value(), 0); QVERIFY(!object->objectProperty()); - QVERIFY(object->objectProperty2() != 0); + QVERIFY(object->objectProperty2() != nullptr); qmlExecuteDeferred(object); QCOMPARE(object->value(), 10); - QVERIFY(object->objectProperty() != 0); + QVERIFY(object->objectProperty() != nullptr); MyQmlObject *qmlObject = qobject_cast<MyQmlObject *>(object->objectProperty()); - QVERIFY(qmlObject != 0); + QVERIFY(qmlObject != nullptr); QCOMPARE(qmlObject->value(), 10); object->setValue(19); QCOMPARE(qmlObject->value(), 19); @@ -931,15 +971,16 @@ void tst_qqmlecmascript::deferredProperties() // Check errors on deferred properties are correctly emitted void tst_qqmlecmascript::deferredPropertiesErrors() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml")); MyDeferredObject *object = qobject_cast<MyDeferredObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->value(), 0); QVERIFY(!object->objectProperty()); QVERIFY(!object->objectProperty2()); - QString warning = component.url().toString() + ":6:21: Unable to assign [undefined] to QObject*"; + QString warning = component.url().toString() + ":6:5: Unable to assign [undefined] to QObject*"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); qmlExecuteDeferred(object); @@ -950,29 +991,30 @@ void tst_qqmlecmascript::deferredPropertiesErrors() void tst_qqmlecmascript::deferredPropertiesInComponents() { // Test that it works when the property is set inside and outside component + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deferredPropertiesInComponents.qml")); QObject *object = component.create(); if (!object) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").value<int>(), 10); MyDeferredObject *defObjectA = qobject_cast<MyDeferredObject *>(object->property("deferredInside").value<QObject*>()); - QVERIFY(defObjectA != 0); + QVERIFY(defObjectA != nullptr); QVERIFY(!defObjectA->objectProperty()); qmlExecuteDeferred(defObjectA); - QVERIFY(defObjectA->objectProperty() != 0); + QVERIFY(defObjectA->objectProperty() != nullptr); QCOMPARE(defObjectA->objectProperty()->property("value").value<int>(), 10); MyDeferredObject *defObjectB = qobject_cast<MyDeferredObject *>(object->property("deferredOutside").value<QObject*>()); - QVERIFY(defObjectB != 0); + QVERIFY(defObjectB != nullptr); QVERIFY(!defObjectB->objectProperty()); qmlExecuteDeferred(defObjectB); - QVERIFY(defObjectB->objectProperty() != 0); + QVERIFY(defObjectB->objectProperty() != nullptr); QCOMPARE(defObjectB->objectProperty()->property("value").value<int>(), 10); delete object; @@ -983,20 +1025,22 @@ void tst_qqmlecmascript::deferredPropertiesInDestruction() //Test that the component does not get created at all if creation is deferred until the containing context is destroyed //Very specific operation ordering is needed for this to occur, currently accessing object from object destructor. // + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deferredPropertiesInDestruction.qml")); QObject *object = component.create(); if (!object) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; //QTBUG-33112 was that this used to cause a crash } void tst_qqmlecmascript::extensionObjects() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("extensionObjects.qml")); MyExtendedObject *object = qobject_cast<MyExtendedObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->baseProperty(), 13); QCOMPARE(object->coreProperty(), 9); object->setProperty("extendedProperty", QVariant(11)); @@ -1018,11 +1062,12 @@ void tst_qqmlecmascript::extensionObjects() void tst_qqmlecmascript::overrideExtensionProperties() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml")); OverrideDefaultPropertyObject *object = qobject_cast<OverrideDefaultPropertyObject *>(component.create()); - QVERIFY(object != 0); - QVERIFY(object->secondProperty() != 0); + QVERIFY(object != nullptr); + QVERIFY(object->secondProperty() != nullptr); QVERIFY(!object->firstProperty()); delete object; @@ -1030,10 +1075,12 @@ void tst_qqmlecmascript::overrideExtensionProperties() void tst_qqmlecmascript::attachedProperties() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("attachedProperty.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 19); QCOMPARE(object->property("b").toInt(), 19); QCOMPARE(object->property("c").toInt(), 19); @@ -1044,7 +1091,7 @@ void tst_qqmlecmascript::attachedProperties() { QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 26); QCOMPARE(object->property("b").toInt(), 26); QCOMPARE(object->property("c").toInt(), 26); @@ -1056,13 +1103,13 @@ void tst_qqmlecmascript::attachedProperties() { QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "writeValue2"); MyQmlAttachedObject *attached = qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object)); - QVERIFY(attached != 0); + QVERIFY(attached != nullptr); QCOMPARE(attached->value2(), 9); delete object; @@ -1071,11 +1118,13 @@ void tst_qqmlecmascript::attachedProperties() void tst_qqmlecmascript::enums() { + QQmlEngine engine; + // Existent enums { QQmlComponent component(&engine, testFileUrl("enums.1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2); QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue); @@ -1100,13 +1149,13 @@ void tst_qqmlecmascript::enums() { QUrl file = testFileUrl("enums.2.qml"); QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'"); - QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9:21 depends on non-NOTIFYable properties:"); + QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl("enums.2.qml").toString() + QLatin1String(":9:5 depends on non-NOTIFYable properties:"); QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty"); - QString w4 = file.toString() + ":7:21: Unable to assign [undefined] to int"; - QString w5 = file.toString() + ":8:21: Unable to assign [undefined] to int"; - QString w6 = file.toString() + ":9:21: Unable to assign [undefined] to int"; - QString w7 = file.toString() + ":13:23: Unable to assign [undefined] to [unknown property type]"; - QString w8 = file.toString() + ":31:23: Unable to assign int to [unknown property type]"; + QString w4 = file.toString() + ":7:5: Unable to assign [undefined] to int"; + QString w5 = file.toString() + ":8:5: Unable to assign [undefined] to int"; + QString w6 = file.toString() + ":9:5: Unable to assign [undefined] to int"; + QString w7 = file.toString() + ":13:9: Unable to assign [undefined] to [unknown property type]"; + QString w8 = file.toString() + ":31:9: Unable to assign int to [unknown property type]"; QTest::ignoreMessage(QtWarningMsg, qPrintable(w1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(w2)); QTest::ignoreMessage(QtWarningMsg, qPrintable(w3)); @@ -1118,7 +1167,7 @@ void tst_qqmlecmascript::enums() QQmlComponent component(&engine, testFileUrl("enums.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 0); QCOMPARE(object->property("b").toInt(), 0); QCOMPARE(object->property("c").toInt(), 0); @@ -1145,7 +1194,7 @@ void tst_qqmlecmascript::enums() { QQmlComponent component(&engine, testFileUrl("enums.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // check the values are what we expect QCOMPARE(object->property("a").toInt(), 4); @@ -1177,9 +1226,10 @@ void tst_qqmlecmascript::enums() void tst_qqmlecmascript::valueTypeFunctions() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml")); MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->rectProperty(), QRect(0,0,100,100)); QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5)); @@ -1192,11 +1242,13 @@ binding. */ void tst_qqmlecmascript::constantsOverrideBindings() { + QQmlEngine engine; + // From ECMAScript { QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("c2").toInt(), 0); object->setProperty("c1", QVariant(9)); @@ -1215,7 +1267,7 @@ void tst_qqmlecmascript::constantsOverrideBindings() { QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("c1").toInt(), 0); QCOMPARE(object->property("c2").toInt(), 10); @@ -1251,7 +1303,7 @@ void tst_qqmlecmascript::constantsOverrideBindings() { QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("c1").toInt(), 0); QCOMPARE(object->property("c3").toInt(), 10); @@ -1269,10 +1321,11 @@ the original binding to be disabled. */ void tst_qqmlecmascript::outerBindingOverridesInnerBinding() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("outerBindingOverridesInnerBinding.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("c1").toInt(), 0); QCOMPARE(object->property("c2").toInt(), 0); @@ -1298,23 +1351,26 @@ Tests for a regression where this used to crash. */ void tst_qqmlecmascript::nonExistentAttachedObject() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml")); - QString warning = component.url().toString() + ":4:21: Unable to assign [undefined] to QString"; + QString warning = component.url().toString() + ":4:5: Unable to assign [undefined] to QString"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::scope() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("scope.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toInt(), 1); QCOMPARE(object->property("test2").toInt(), 2); @@ -1333,7 +1389,7 @@ void tst_qqmlecmascript::scope() { QQmlComponent component(&engine, testFileUrl("scope.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toInt(), 19); QCOMPARE(object->property("test2").toInt(), 19); @@ -1348,7 +1404,7 @@ void tst_qqmlecmascript::scope() { QQmlComponent component(&engine, testFileUrl("scope.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QEXPECT_FAIL("", "Properties resolvable at compile time come before the global object, which is not 100% compatible with older QML versions", Continue); @@ -1362,7 +1418,7 @@ void tst_qqmlecmascript::scope() { QQmlComponent component(&engine, testFileUrl("scope.4.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 0); QCOMPARE(object->property("test2").toString(), QString()); @@ -1378,7 +1434,7 @@ void tst_qqmlecmascript::scope() { QQmlComponent component(&engine, testFileUrl("scope.5.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); @@ -1389,7 +1445,7 @@ void tst_qqmlecmascript::scope() { QQmlComponent component(&engine, testFileUrl("scope.6.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1401,9 +1457,10 @@ void tst_qqmlecmascript::scope() // importing context void tst_qqmlecmascript::importScope() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("importScope.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 240); @@ -1416,9 +1473,10 @@ is essentially a test of QQmlMetaType::copy() */ void tst_qqmlecmascript::signalParameterTypes() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); emit object->basicSignal(); @@ -1443,9 +1501,10 @@ Test that two JS objects for the same QObject compare as equal. */ void tst_qqmlecmascript::objectsCompareAsEqual() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("objectsCompareAsEqual.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); @@ -1463,9 +1522,10 @@ Tests for a regression where the binding would not reevaluate. */ void tst_qqmlecmascript::aliasPropertyAndBinding() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("aliasPropertyAndBinding.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("c2").toInt(), 3); QCOMPARE(object->property("c3").toInt(), 3); @@ -1484,12 +1544,13 @@ and that the aliased property is reset correctly if possible. */ void tst_qqmlecmascript::aliasPropertyReset() { - QObject *object = 0; + QQmlEngine engine; + QObject *object = nullptr; // test that a manual write (of undefined) to a resettable aliased property succeeds QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml")); object = c1.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); QCOMPARE(object->property("aliasIsUndefined"), QVariant(false)); QMetaObject::invokeMethod(object, "resetAliased"); @@ -1500,7 +1561,7 @@ void tst_qqmlecmascript::aliasPropertyReset() // test that a manual write (of undefined) to a resettable alias property succeeds QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml")); object = c2.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false)); QMetaObject::invokeMethod(object, "resetAlias"); @@ -1511,7 +1572,7 @@ void tst_qqmlecmascript::aliasPropertyReset() // test that an alias to a bound property works correctly QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml")); object = c3.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false)); QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false)); @@ -1525,10 +1586,10 @@ void tst_qqmlecmascript::aliasPropertyReset() // whose aliased property's object has been deleted, does not crash. QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml")); object = c4.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); QObject *loader = object->findChild<QObject*>("loader"); - QVERIFY(loader != 0); + QVERIFY(loader != nullptr); delete loader; QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset. QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash. @@ -1540,16 +1601,16 @@ void tst_qqmlecmascript::aliasPropertyReset() // test that binding an alias property to an undefined value works correctly QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml")); object = c5.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value. delete object; // test that a manual write (of undefined) to a non-resettable property fails properly QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml"); - QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int"); + QString warning1 = url.toString() + QLatin1String(": Error: Cannot assign [undefined] to int"); QQmlComponent e1(&engine, url); object = e1.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("intAlias").value<int>(), 12); QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false)); QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); @@ -1612,6 +1673,7 @@ void tst_qqmlecmascript::componentCreation() QFETCH(QString, creationError); QFETCH(QString, createdParent); + QQmlEngine engine; QUrl testUrl(testFileUrl("componentCreation.qml")); if (!creationError.isEmpty()) { @@ -1621,7 +1683,7 @@ void tst_qqmlecmascript::componentCreation() QQmlComponent component(&engine, testUrl); MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, method.toUtf8()); QQmlComponent *created = object->componentProperty(); @@ -1633,7 +1695,7 @@ void tst_qqmlecmascript::componentCreation() if (createdParent == QLatin1String("obj")) { expectedParent = object; } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) { - expectedParent = 0; + expectedParent = nullptr; } QCOMPARE(created->parent(), expectedParent); } @@ -1658,9 +1720,10 @@ void tst_qqmlecmascript::dynamicCreation() QFETCH(QString, method); QFETCH(QString, createdName); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, method.toUtf8()); QObject *created = object->objectProperty(); @@ -1675,11 +1738,13 @@ void tst_qqmlecmascript::dynamicCreation() */ void tst_qqmlecmascript::dynamicDestruction() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml")); QPointer<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); - QPointer<QObject> createdQmlObject = 0; + QVERIFY(object != nullptr); + QPointer<QObject> createdQmlObject = nullptr; QMetaObject::invokeMethod(object, "create"); createdQmlObject = object->objectProperty(); @@ -1689,13 +1754,13 @@ void tst_qqmlecmascript::dynamicDestruction() QMetaObject::invokeMethod(object, "killOther"); QVERIFY(createdQmlObject); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(createdQmlObject); for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up if (createdQmlObject) { QTest::qWait(100); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } } @@ -1704,7 +1769,7 @@ void tst_qqmlecmascript::dynamicDestruction() QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership); QMetaObject::invokeMethod(object, "killMe"); QVERIFY(object); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(!object); } @@ -1712,7 +1777,7 @@ void tst_qqmlecmascript::dynamicDestruction() { QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty"))); @@ -1722,7 +1787,7 @@ void tst_qqmlecmascript::dynamicDestruction() QMetaObject::invokeMethod(o, "destroy"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty"))); @@ -1732,10 +1797,10 @@ void tst_qqmlecmascript::dynamicDestruction() { // QTBUG-23451 - QPointer<QObject> createdQmlObject = 0; + QPointer<QObject> createdQmlObject = nullptr; QQmlComponent component(&engine, testFileUrl("dynamicDeletion.3.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty"))); QMetaObject::invokeMethod(o, "create"); createdQmlObject = qvariant_cast<QObject*>(o->property("objectProperty")); @@ -1744,7 +1809,7 @@ void tst_qqmlecmascript::dynamicDestruction() QCOMPARE(qvariant_cast<bool>(o->property("test")), false); for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up QTest::qWait(100); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty"))); @@ -1758,9 +1823,10 @@ void tst_qqmlecmascript::dynamicDestruction() */ void tst_qqmlecmascript::objectToString() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qmlToString.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "testToString"); QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_")); QVERIFY(object->stringProperty().endsWith(", \"objName\")")); @@ -1778,9 +1844,10 @@ void tst_qqmlecmascript::objectHasOwnProperty() QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined"; QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined"; + QQmlEngine engine; QQmlComponent component(&engine, url); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // test QObjects in QML QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess"); @@ -1790,7 +1857,7 @@ void tst_qqmlecmascript::objectHasOwnProperty() // now test other types in QML QObject *child = object->findChild<QObject*>("typeObj"); - QVERIFY(child != 0); + QVERIFY(child != nullptr); QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess"); QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true); QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true); @@ -1824,10 +1891,12 @@ This test is best run under valgrind to ensure no invalid memory access occur. */ void tst_qqmlecmascript::selfDeletingBinding() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); object->setProperty("triggerDelete", true); delete object; } @@ -1835,7 +1904,7 @@ void tst_qqmlecmascript::selfDeletingBinding() { QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); object->setProperty("triggerDelete", true); delete object; } @@ -1850,9 +1919,10 @@ and no synthesiszed properties). */ void tst_qqmlecmascript::extendedObjectPropertyLookup() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -1861,9 +1931,10 @@ Test that extended object properties can be accessed correctly. */ void tst_qqmlecmascript::extendedObjectPropertyLookup2() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant returnValue; QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue))); @@ -1877,6 +1948,7 @@ Test failure when trying to create and uncreatable extended type object. */ void tst_qqmlecmascript::uncreatableExtendedObjectFailureCheck() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("uncreatableExtendedObjectFailureCheck.qml")); QObject *object = component.create(); @@ -1888,10 +1960,11 @@ Test that an subclass of an uncreatable extended object contains all the require */ void tst_qqmlecmascript::extendedObjectPropertyLookup3() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant returnValue; QVERIFY(QMetaObject::invokeMethod(object, "getAbstractProperty", Q_RETURN_ARG(QVariant, returnValue))); @@ -1908,6 +1981,7 @@ Test file/lineNumbers for binding/Script errors. */ void tst_qqmlecmascript::scriptErrors() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("scriptErrors.qml")); QString url = component.url().toString(); @@ -1916,7 +1990,7 @@ void tst_qqmlecmascript::scriptErrors() QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\""; QString warning4 = url + ":13: ReferenceError: a is not defined"; QString warning5 = url + ":11: ReferenceError: a is not defined"; - QString warning6 = url + ":10:21: Unable to assign [undefined] to int"; + QString warning6 = url + ":10:5: Unable to assign [undefined] to int"; QString warning7 = url + ":15: TypeError: Cannot assign to read-only property \"trueProperty\""; QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\""; @@ -1926,7 +2000,7 @@ void tst_qqmlecmascript::scriptErrors() QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData()); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData()); emit object->basicSignal(); @@ -1945,6 +2019,7 @@ Test file/lineNumbers for inline functions. */ void tst_qqmlecmascript::functionErrors() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("functionErrors.qml")); QString url = component.url().toString(); @@ -1953,14 +2028,14 @@ void tst_qqmlecmascript::functionErrors() QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; // test that if an exception occurs while invoking js function from cpp, it is reported as expected. QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml")); url = componentTwo.url().toString(); object = componentTwo.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QObject *resource = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function"); @@ -1975,12 +2050,13 @@ Test various errors that can occur when assigning a property from script */ void tst_qqmlecmascript::propertyAssignmentErrors() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml")); QString url = component.url().toString(); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); @@ -1994,9 +2070,10 @@ a signal script. */ void tst_qqmlecmascript::signalTriggeredBindings() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("base").toReal(), 50.); QCOMPARE(object->property("test1").toReal(), 50.); @@ -2022,9 +2099,10 @@ Test that list properties can be iterated from ECMAScript */ void tst_qqmlecmascript::listProperties() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("listProperties.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toInt(), 21); QCOMPARE(object->property("test2").toInt(), 2); @@ -2036,6 +2114,7 @@ void tst_qqmlecmascript::listProperties() void tst_qqmlecmascript::exceptionClearsOnReeval() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml")); QString url = component.url().toString(); @@ -2043,7 +2122,7 @@ void tst_qqmlecmascript::exceptionClearsOnReeval() QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), false); @@ -2059,6 +2138,7 @@ void tst_qqmlecmascript::exceptionClearsOnReeval() void tst_qqmlecmascript::exceptionSlotProducesWarning() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml")); QString url = component.url().toString(); @@ -2066,12 +2146,13 @@ void tst_qqmlecmascript::exceptionSlotProducesWarning() QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::exceptionBindingProducesWarning() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml")); QString url = component.url().toString(); @@ -2079,29 +2160,32 @@ void tst_qqmlecmascript::exceptionBindingProducesWarning() QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::compileInvalidBinding() { // QTBUG-23387: ensure that invalid bindings don't cause a crash. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("v8bindingException.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } // Check that transient binding errors are not displayed void tst_qqmlecmascript::transientErrors() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("transientErrors.qml")); QQmlTestMessageHandler messageHandler; QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); @@ -2115,7 +2199,7 @@ void tst_qqmlecmascript::transientErrors() QQmlTestMessageHandler messageHandler; QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); @@ -2126,9 +2210,10 @@ void tst_qqmlecmascript::transientErrors() // Check that errors during shutdown are minimized void tst_qqmlecmascript::shutdownErrors() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlTestMessageHandler messageHandler; @@ -2139,6 +2224,7 @@ void tst_qqmlecmascript::shutdownErrors() void tst_qqmlecmascript::compositePropertyType() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml")); QTest::ignoreMessage(QtDebugMsg, "hello world"); @@ -2149,9 +2235,10 @@ void tst_qqmlecmascript::compositePropertyType() // QTBUG-5759 void tst_qqmlecmascript::jsObject() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("jsObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 92); @@ -2160,10 +2247,12 @@ void tst_qqmlecmascript::jsObject() void tst_qqmlecmascript::undefinedResetsProperty() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("resettableProperty").toInt(), 92); @@ -2180,7 +2269,7 @@ void tst_qqmlecmascript::undefinedResetsProperty() { QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("resettableProperty").toInt(), 19); @@ -2195,9 +2284,10 @@ void tst_qqmlecmascript::undefinedResetsProperty() // Aliases to variant properties should work void tst_qqmlecmascript::qtbug_22464() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -2206,10 +2296,11 @@ void tst_qqmlecmascript::qtbug_22464() void tst_qqmlecmascript::qtbug_21580() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -2219,19 +2310,21 @@ void tst_qqmlecmascript::qtbug_21580() // Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } // QTBUG-6781 void tst_qqmlecmascript::bug1() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("bug.1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 14); @@ -2249,11 +2342,12 @@ void tst_qqmlecmascript::bug1() #ifndef QT_NO_WIDGETS void tst_qqmlecmascript::bug2() { + QQmlEngine engine; QQmlComponent component(&engine); component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -2262,9 +2356,10 @@ void tst_qqmlecmascript::bug2() // Don't crash in createObject when the component has errors. void tst_qqmlecmascript::dynamicCreationCrash() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); QMetaObject::invokeMethod(object, "dontCrash"); @@ -2285,9 +2380,9 @@ void tst_qqmlecmascript::dynamicCreationOwnership() QQmlEngine dcoEngine; QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo"); - QVERIFY(mdcdo != 0); + QVERIFY(mdcdo != nullptr); mdcdo->setDtorCount(&dtorCount); for (int i = 1; i < 105; ++i, ++expectedDtorCount) { @@ -2298,29 +2393,38 @@ void tst_qqmlecmascript::dynamicCreationOwnership() QMetaObject::invokeMethod(object, "performGc"); } if (i % 10 == 0) { - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } } delete object; } - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QCOMPARE(dtorCount, expectedDtorCount); } void tst_qqmlecmascript::regExpBug() { + QQmlEngine engine; + //QTBUG-9367 { QQmlComponent component(&engine, testFileUrl("regExp.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]")); delete object; } + { + QQmlComponent component(&engine, testFileUrl("regularExpression.qml")); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); + QVERIFY(!object.isNull()); + QCOMPARE(object->regularExpression().pattern(), QLatin1String("[a-zA-z]")); + } + //QTBUG-23068 { QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString()); @@ -2330,15 +2434,27 @@ void tst_qqmlecmascript::regExpBug() QVERIFY(!object); QCOMPARE(component.errorString(), err); } + + { + const QString err = QString::fromLatin1("%1:6 Invalid property assignment: " + "regular expression expected; " + "use /pattern/ syntax\n") + .arg(testFileUrl("regularExpression.2.qml").toString()); + QQmlComponent component(&engine, testFileUrl("regularExpression.2.qml")); + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(!object); + QCOMPARE(component.errorString(), err); + } } -static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const char *source) +static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source) { QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::Scope scope(QV8Engine::getV4(engine)); - QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), functionSource); + QV4::Scope scope(v4); + QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::ContextType::Eval, functionSource); program.inheritContext = true; QV4::ScopedFunctionObject function(scope, program.run()); @@ -2346,10 +2462,10 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const scope.engine->catchException(); return true; } - QV4::ScopedCallData d(scope, 1); - d->args[0] = o; - d->thisObject = engine->global(); - function->call(scope, d); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = o; + *jsCallData->thisObject = v4->global(); + function->call(jsCallData); if (scope.engine->hasException) { scope.engine->catchException(); return true; @@ -2357,14 +2473,14 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const return false; } -static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, +static inline bool evaluate_value(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source, const QV4::Value &result) { QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::Scope scope(QV8Engine::getV4(engine)); - QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), functionSource); + QV4::Scope scope(v4); + QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::ContextType::Eval, functionSource); program.inheritContext = true; QV4::ScopedFunctionObject function(scope, program.run()); @@ -2375,26 +2491,27 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, if (!function) return false; - QV4::ScopedCallData d(scope, 1); - d->args[0] = o; - d->thisObject = engine->global(); - function->call(scope, d); + QV4::ScopedValue value(scope); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = o; + *jsCallData->thisObject = v4->global(); + value = function->call(jsCallData); if (scope.engine->hasException) { scope.engine->catchException(); return false; } - return QV4::Runtime::method_strictEqual(scope.result, result); + return QV4::Runtime::StrictEqual::call(value, result); } -static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o, - const char *source) +static inline QV4::ReturnedValue evaluate(QV4::ExecutionEngine *v4, const QV4::Value &o, + const char *source) { QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::Scope scope(v4); - QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), functionSource); + QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::ContextType::Eval, functionSource); program.inheritContext = true; QV4::ScopedFunctionObject function(scope, program.run()); @@ -2404,15 +2521,15 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o } if (!function) return QV4::Encode::undefined(); - QV4::ScopedCallData d(scope, 1); - d->args[0] = o; - d->thisObject = engine->global(); - function->call(scope, d); + QV4::JSCallData jsCallData(scope, 1); + jsCallData->args[0] = o; + *jsCallData->thisObject = v4->global(); + QV4::ScopedValue result(scope, function->call(jsCallData)); if (scope.engine->hasException) { scope.engine->catchException(); return QV4::Encode::undefined(); } - return scope.result.asReturnedValue(); + return result->asReturnedValue(); } #define EVALUATE_ERROR(source) evaluate_error(engine, object, source) @@ -2426,12 +2543,11 @@ void tst_qqmlecmascript::callQtInvokables() MyInvokableObject *o = new MyInvokableObject(); QQmlEngine qmlengine; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine); - QV8Engine *engine = ep->v8engine(); - QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::ExecutionEngine *engine = qmlengine.handle(); + QV4::Scope scope(engine); - QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), o)); + QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o)); // Non-existent methods o->reset(); @@ -2714,28 +2830,28 @@ void tst_qqmlecmascript::callQtInvokables() QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0)); + QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0)); + QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0)); + QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue())); QCOMPARE(o->error(), false); QCOMPARE(o->invoked(), 13); QCOMPARE(o->actuals().count(), 1); - QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)0)); + QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr)); o->reset(); QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", QV4::Primitive::undefinedValue())); @@ -2999,9 +3115,8 @@ void tst_qqmlecmascript::resolveClashingProperties() { ClashingNames *o = new ClashingNames(); QQmlEngine qmlengine; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine); - QV4::ExecutionEngine *engine = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *engine = qmlengine.handle(); QV4::Scope scope(engine); QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o)); @@ -3019,7 +3134,7 @@ void tst_qqmlecmascript::resolveClashingProperties() QString key = name->toQStringNoThrow(); if (key == QLatin1String("clashes")) { value = v; - QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value)); + QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value)); QString type = typeString->toQStringNoThrow(); if (type == QLatin1String("boolean")) { QVERIFY(!seenProperty); @@ -3040,6 +3155,7 @@ void tst_qqmlecmascript::resolveClashingProperties() // QTBUG-13047 (check that you can pass registered object types as args) void tst_qqmlecmascript::invokableObjectArg() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml")); QObject *o = component.create(); @@ -3054,6 +3170,7 @@ void tst_qqmlecmascript::invokableObjectArg() // QTBUG-13047 (check that you can return registered object types from methods) void tst_qqmlecmascript::invokableObjectRet() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml")); QObject *o = component.create(); @@ -3064,6 +3181,7 @@ void tst_qqmlecmascript::invokableObjectRet() void tst_qqmlecmascript::invokableEnumRet() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("invokableEnumRet.qml")); QObject *o = component.create(); @@ -3075,6 +3193,7 @@ void tst_qqmlecmascript::invokableEnumRet() // QTBUG-5675 void tst_qqmlecmascript::listToVariant() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("listToVariant.qml")); MyQmlContainer container; @@ -3083,7 +3202,7 @@ void tst_qqmlecmascript::listToVariant() context.setContextObject(&container); QObject *object = component.create(&context); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant v = object->property("test"); QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>()); @@ -3095,6 +3214,7 @@ void tst_qqmlecmascript::listToVariant() // QTBUG-16316 void tst_qqmlecmascript::listAssignment() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("listAssignment.qml")); QObject *obj = component.create(); QCOMPARE(obj->property("list1length").toInt(), 2); @@ -3133,6 +3253,7 @@ void tst_qqmlecmascript::multiEngineObject() // Test that references to QObjects are cleanup when the object is destroyed void tst_qqmlecmascript::deletedObject() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deletedObject.qml")); QObject *object = component.create(); @@ -3147,14 +3268,15 @@ void tst_qqmlecmascript::deletedObject() void tst_qqmlecmascript::attachedPropertyScope() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MyQmlAttachedObject *attached = qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object)); - QVERIFY(attached != 0); + QVERIFY(attached != nullptr); QCOMPARE(object->property("value2").toInt(), 0); @@ -3167,11 +3289,13 @@ void tst_qqmlecmascript::attachedPropertyScope() void tst_qqmlecmascript::scriptConnect() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), false); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3184,7 +3308,7 @@ void tst_qqmlecmascript::scriptConnect() QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), false); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3197,7 +3321,7 @@ void tst_qqmlecmascript::scriptConnect() QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), false); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3210,7 +3334,7 @@ void tst_qqmlecmascript::scriptConnect() QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->methodCalled(), false); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3223,7 +3347,7 @@ void tst_qqmlecmascript::scriptConnect() QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->methodCalled(), false); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3236,7 +3360,7 @@ void tst_qqmlecmascript::scriptConnect() QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 0); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3248,11 +3372,13 @@ void tst_qqmlecmascript::scriptConnect() void tst_qqmlecmascript::scriptDisconnect() { + QQmlEngine engine; + { QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 0); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3271,7 +3397,7 @@ void tst_qqmlecmascript::scriptDisconnect() QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 0); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3290,7 +3416,7 @@ void tst_qqmlecmascript::scriptDisconnect() QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 0); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3308,7 +3434,7 @@ void tst_qqmlecmascript::scriptDisconnect() QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 0); emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); @@ -3338,6 +3464,7 @@ public slots: void tst_qqmlecmascript::ownership() { + QQmlEngine engine; OwnershipObject own; QQmlContext *context = new QQmlContext(engine.rootContext()); context->setContextObject(&own); @@ -3345,13 +3472,13 @@ void tst_qqmlecmascript::ownership() { QQmlComponent component(&engine, testFileUrl("ownership.qml")); - QVERIFY(own.object != 0); + QVERIFY(own.object != nullptr); QObject *object = component.create(context); engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(own.object.isNull()); @@ -3364,16 +3491,16 @@ void tst_qqmlecmascript::ownership() { QQmlComponent component(&engine, testFileUrl("ownership.qml")); - QVERIFY(own.object != 0); + QVERIFY(own.object != nullptr); QObject *object = component.create(context); engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); - QVERIFY(own.object != 0); + QVERIFY(own.object != nullptr); delete object; } @@ -3385,7 +3512,7 @@ class CppOwnershipReturnValue : public QObject { Q_OBJECT public: - CppOwnershipReturnValue() : value(0) {} + CppOwnershipReturnValue() : value(nullptr) {} ~CppOwnershipReturnValue() { delete value; } Q_INVOKABLE QObject *create() { @@ -3420,21 +3547,22 @@ void tst_qqmlecmascript::cppOwnershipReturnValue() QObject *object = component.create(); - QVERIFY(object != 0); - QVERIFY(source.value != 0); + QVERIFY(object != nullptr); + QVERIFY(source.value != nullptr); delete object; } - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); - QVERIFY(source.value != 0); + QVERIFY(source.value != nullptr); } // QTBUG-15697 void tst_qqmlecmascript::ownershipCustomReturnValue() { + QQmlEngine engine; CppOwnershipReturnValue source; { @@ -3448,14 +3576,14 @@ void tst_qqmlecmascript::ownershipCustomReturnValue() QObject *object = component.create(); - QVERIFY(object != 0); - QVERIFY(source.value != 0); + QVERIFY(object != nullptr); + QVERIFY(source.value != nullptr); delete object; } engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(source.value.isNull()); @@ -3467,7 +3595,7 @@ class OwnershipChangingObject : public QObject { Q_OBJECT public: - OwnershipChangingObject(): object(0) { } + OwnershipChangingObject(): object(nullptr) { } QPointer<QObject> object; @@ -3478,6 +3606,7 @@ public slots: void tst_qqmlecmascript::ownershipRootObject() { + QQmlEngine engine; OwnershipChangingObject own; QQmlContext *context = new QQmlContext(engine.rootContext()); context->setContextObject(&own); @@ -3488,10 +3617,10 @@ void tst_qqmlecmascript::ownershipRootObject() engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); - QVERIFY(own.object != 0); + QVERIFY(own.object != nullptr); delete context; delete object; @@ -3499,6 +3628,7 @@ void tst_qqmlecmascript::ownershipRootObject() void tst_qqmlecmascript::ownershipConsistency() { + QQmlEngine engine; OwnershipChangingObject own; QQmlContext *context = new QQmlContext(engine.rootContext()); context->setContextObject(&own); @@ -3518,10 +3648,10 @@ void tst_qqmlecmascript::ownershipConsistency() engine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); - QVERIFY(own.object != 0); + QVERIFY(own.object != nullptr); delete context; delete object; @@ -3529,6 +3659,7 @@ void tst_qqmlecmascript::ownershipConsistency() void tst_qqmlecmascript::ownershipQmlIncubated() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml")); QObject *object = component.create(); QVERIFY(object); @@ -3537,7 +3668,7 @@ void tst_qqmlecmascript::ownershipQmlIncubated() QMetaObject::invokeMethod(object, "deleteIncubatedItem"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(!object->property("incubatedItem").value<QObject*>()); @@ -3568,6 +3699,7 @@ private: // Tests that returning a QList<QObject*> from a method works void tst_qqmlecmascript::qlistqobjectMethods() { + QQmlEngine engine; QListQObjectMethodsObject obj; QQmlContext *context = new QQmlContext(engine.rootContext()); context->setContextObject(&obj); @@ -3586,10 +3718,11 @@ void tst_qqmlecmascript::qlistqobjectMethods() // QTBUG-9205 void tst_qqmlecmascript::strictlyEquals() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("strictlyEquals.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); @@ -3605,10 +3738,11 @@ void tst_qqmlecmascript::strictlyEquals() void tst_qqmlecmascript::compiled() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("compiled.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toReal(), qreal(15.7)); QCOMPARE(object->property("test2").toReal(), qreal(-6.7)); @@ -3645,10 +3779,11 @@ void tst_qqmlecmascript::compiled() // Test that numbers assigned in bindings as strings work consistently void tst_qqmlecmascript::numberAssignment() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("numberAssignment.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1"), QVariant((qreal)6.7)); QCOMPARE(object->property("test2"), QVariant((qreal)6.7)); @@ -3671,10 +3806,11 @@ void tst_qqmlecmascript::numberAssignment() void tst_qqmlecmascript::propertySplicing() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertySplicing.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -3684,10 +3820,11 @@ void tst_qqmlecmascript::propertySplicing() // QTBUG-16683 void tst_qqmlecmascript::signalWithUnknownTypes() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MyQmlObject::MyType type; type.value = 0x8971123; @@ -3737,9 +3874,10 @@ void tst_qqmlecmascript::signalWithJSValueInVariant() QFETCH(QString, expression); QFETCH(QString, compare); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml")); QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QJSValue value = engine.evaluate(expression); QVERIFY(!value.isError()); @@ -3761,9 +3899,10 @@ void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines() QFETCH(QString, expression); QFETCH(QString, compare); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml")); QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QJSEngine engine2; QJSValue value = engine2.evaluate(expression); @@ -3791,9 +3930,10 @@ void tst_qqmlecmascript::signalWithQJSValue() QFETCH(QString, expression); QFETCH(QString, compare); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml")); QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QJSValue value = engine.evaluate(expression); QVERIFY(!value.isError()); @@ -3959,7 +4099,7 @@ void tst_qqmlecmascript::singletonType() if (!errorMessage.isEmpty()) { QVERIFY(!object); } else { - QVERIFY(object != 0); + QVERIFY(object != nullptr); for (int i = 0; i < readProperties.size(); ++i) QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i)); for (int i = 0; i < writeProperties.size(); ++i) @@ -3994,7 +4134,7 @@ void tst_qqmlecmascript::singletonTypeCaching() QQmlEngine cleanEngine; QQmlComponent component(&cleanEngine, testfile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QList<QVariant> firstValues; QMetaObject::invokeMethod(object, "modifyValues"); for (int i = 0; i < readProperties.size(); ++i) @@ -4003,7 +4143,7 @@ void tst_qqmlecmascript::singletonTypeCaching() QQmlComponent component2(&cleanEngine, testfile); QObject *object2 = component2.create(); - QVERIFY(object2 != 0); + QVERIFY(object2 != nullptr); for (int i = 0; i < readProperties.size(); ++i) QCOMPARE(object2->property(readProperties.at(i).toLatin1().constData()), firstValues.at(i)); // cached, shouldn't have changed. delete object2; @@ -4011,6 +4151,7 @@ void tst_qqmlecmascript::singletonTypeCaching() void tst_qqmlecmascript::singletonTypeImportOrder() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeImportOrder.qml")); QObject *object = component.create(); QVERIFY(object); @@ -4020,6 +4161,7 @@ void tst_qqmlecmascript::singletonTypeImportOrder() void tst_qqmlecmascript::singletonTypeResolution() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("singletontype/singletonTypeResolution.qml")); QObject *object = component.create(); QVERIFY(object); @@ -4031,27 +4173,26 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { QQmlContextData *childCtxt = ctxt->childContexts; if (!ctxt->importedScripts.isNullOrUndefined()) { - QV8Engine *engine = QV8Engine::get(ctxt->engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::ExecutionEngine *v4 = ctxt->engine->handle(); QV4::Scope scope(v4); QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts.value()); QV4::Scoped<QV4::QQmlContextWrapper> qml(scope); for (quint32 i = 0; i < scripts->getLength(); ++i) { QQmlContextData *scriptContext, *newContext; - qml = scripts->getIndexed(i); + qml = scripts->get(i); - scriptContext = qml ? qml->getContext() : 0; + scriptContext = qml ? qml->getContext() : nullptr; qml = QV4::Encode::undefined(); { - QV4::Scope scope(QV8Engine::getV4((engine))); - QV4::ScopedContext temporaryScope(scope, QV4::QmlContext::create(scope.engine->rootContext(), scriptContext, 0)); + QV4::Scope scope(v4); + QV4::ScopedContext temporaryScope(scope, QV4::QmlContext::create(scope.engine->rootContext(), scriptContext, nullptr)); Q_UNUSED(temporaryScope) } ctxt->engine->collectGarbage(); - qml = scripts->getIndexed(i); - newContext = qml ? qml->getContext() : 0; + qml = scripts->get(i); + newContext = qml ? qml->getContext() : nullptr; QCOMPARE(scriptContext, newContext); } } @@ -4164,7 +4305,7 @@ void tst_qqmlecmascript::importScripts_data() << QString() << QStringList() << (QStringList() << QLatin1String("testValue")) - << (QVariantList() << QVariant(18)); + << (QVariantList() << QVariant(16)); QTest::newRow("import singleton type into js import") << testFileUrl("jsimport/testImportSingletonType.qml") @@ -4298,6 +4439,10 @@ void tst_qqmlecmascript::importScripts() ThreadedTestHTTPServer server(dataDirectory() + "/remote"); + QQmlEngine engine; + QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib")); + engine.addImportPath(dataDir); + QStringList importPathList = engine.importPathList(); QString remotePath(server.urlString("/")); @@ -4326,7 +4471,7 @@ void tst_qqmlecmascript::importScripts() if (!errorMessage.isEmpty()) { QVERIFY(!object); } else { - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlContextData *ctxt = QQmlContextData::get(engine.rootContext()); tst_qqmlecmascript::verifyContextLifetime(ctxt); @@ -4341,6 +4486,7 @@ void tst_qqmlecmascript::importScripts() void tst_qqmlecmascript::importCreationContext() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("jsimport/creationContext.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -4361,11 +4507,11 @@ void tst_qqmlecmascript::scarceResources_other() QPixmap origPixmap(100, 100); origPixmap.fill(Qt::blue); QString srp_name, expectedWarning; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - ScarceResourceObject *eo = 0; - QObject *srsc = 0; - QObject *object = 0; + QQmlEngine engine; + QV4::ExecutionEngine *v4 = engine.handle(); + ScarceResourceObject *eo = nullptr; + QObject *srsc = nullptr; + QObject *object = nullptr; /* property var semantics */ @@ -4394,7 +4540,7 @@ void tst_qqmlecmascript::scarceResources_other() // test that scarce resources are handled properly from js functions in qml files QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml")); object = varComponentEleven.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. @@ -4413,7 +4559,7 @@ void tst_qqmlecmascript::scarceResources_other() // test that if an exception occurs while invoking js function from cpp, that the resources are released. QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml")); object = varComponentTwelve.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. @@ -4431,7 +4577,7 @@ void tst_qqmlecmascript::scarceResources_other() // that the scarce resource is removed from the engine's list of scarce resources to clean up. QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml")); object = varComponentThirteen.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(!object->property("varProperty").isValid()); // not assigned yet QMetaObject::invokeMethod(object, "assignVarProperty"); QVERIFY(v4->scarceResources.isEmpty()); // the scarce resource is a VME property. @@ -4445,7 +4591,7 @@ void tst_qqmlecmascript::scarceResources_other() // test that scarce resources are handled properly in signal invocation QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml")); object = variantComponentTen.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); srsc = object->findChild<QObject*>("srsc"); QVERIFY(srsc); QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet. @@ -4468,7 +4614,7 @@ void tst_qqmlecmascript::scarceResources_other() // test that scarce resources are handled properly from js functions in qml files QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml")); object = variantComponentEleven.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. @@ -4487,7 +4633,7 @@ void tst_qqmlecmascript::scarceResources_other() // test that if an exception occurs while invoking js function from cpp, that the resources are released. QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml")); object = variantComponentTwelve.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. @@ -4733,14 +4879,14 @@ void tst_qqmlecmascript::scarceResources() QFETCH(QVariantList, expectedValues); QFETCH(QStringList, expectedErrors); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - ScarceResourceObject *eo = 0; - QObject *object = 0; + QQmlEngine engine; + QV4::ExecutionEngine *v4 = engine.handle(); + ScarceResourceObject *eo = nullptr; + QObject *object = nullptr; QQmlComponent c(&engine, qmlFile); object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); for (int i = 0; i < propertyNames.size(); ++i) { QString prop = propertyNames.at(i); bool validity = expectedValidity.at(i).toBool(); @@ -4766,9 +4912,10 @@ void tst_qqmlecmascript::scarceResources() void tst_qqmlecmascript::propertyChangeSlots() { // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; // ensure that invalid property names fail properly. @@ -4832,9 +4979,10 @@ void tst_qqmlecmascript::propertyVar() { QFETCH(QUrl, qmlFile); + QQmlEngine engine; QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -4871,9 +5019,10 @@ void tst_qqmlecmascript::propertyQJSValue() { QFETCH(QUrl, qmlFile); + QQmlEngine engine; QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -4883,14 +5032,15 @@ void tst_qqmlecmascript::propertyQJSValue() // Tests that we can write QVariant values to var properties from C++ void tst_qqmlecmascript::propertyVarCpp() { - QObject *object = 0; + QObject *object = nullptr; // ensure that writing to and reading from a var property from cpp works as required. // Literal values stored in var properties can be read and written as QVariants // of a specific type, whereas object values are read as QVariantMaps. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml")); object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // assign int to property var that currently has int assigned QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10))); QCOMPARE(object->property("varBound"), QVariant(15)); @@ -4909,11 +5059,13 @@ void tst_qqmlecmascript::propertyVarCpp() void tst_qqmlecmascript::propertyVarOwnership() { + QQmlEngine engine; + // Referenced JS objects are not collected { QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), false); QMetaObject::invokeMethod(object, "runTest"); QCOMPARE(object->property("test").toBool(), true); @@ -4923,7 +5075,7 @@ void tst_qqmlecmascript::propertyVarOwnership() { QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), false); QMetaObject::invokeMethod(object, "runTest"); QCOMPARE(object->property("test").toBool(), true); @@ -4933,7 +5085,7 @@ void tst_qqmlecmascript::propertyVarOwnership() { QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test2").toBool(), false); QCOMPARE(object->property("test2").toBool(), false); @@ -4957,7 +5109,7 @@ void tst_qqmlecmascript::propertyVarOwnership() { QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -4976,7 +5128,7 @@ void tst_qqmlecmascript::propertyVarOwnership() { QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.5.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "createComponent"); engine.collectGarbage(); QMetaObject::invokeMethod(object, "runTest"); @@ -4990,22 +5142,23 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership() // The childObject has a reference to a different QObject. We want to ensure // that the different item will not be cleaned up until required. IE, the childObject // has implicit ownership of the constructed QObject. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignCircular"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QObject *rootObject = object->property("vp").value<QObject*>(); - QVERIFY(rootObject != 0); + QVERIFY(rootObject != nullptr); QObject *childObject = rootObject->findChild<QObject*>("text"); - QVERIFY(childObject != 0); + QVERIFY(childObject != nullptr); QCOMPARE(rootObject->property("rectCanary").toInt(), 5); QCOMPARE(childObject->property("textCanary").toInt(), 10); QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject. QPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events. QVERIFY(!qobjectGuard.isNull()); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QVERIFY(!qobjectGuard.isNull()); QMetaObject::invokeMethod(object, "deassignCircular"); @@ -5017,11 +5170,12 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership() void tst_qqmlecmascript::propertyVarReparent() { // ensure that nothing breaks if we re-parent objects + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignVarProp"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QObject *rect = object->property("vp").value<QObject*>(); QObject *text = rect->findChild<QObject*>("textOne"); @@ -5059,11 +5213,12 @@ void tst_qqmlecmascript::propertyVarReparentNullContext() // sometimes reparenting can cause problems // (eg, if the ctxt is collected, varproperties are no longer available) // this test ensures that no crash occurs in that situation. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignVarProp"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QObject *rect = object->property("vp").value<QObject*>(); QObject *text = rect->findChild<QObject*>("textOne"); @@ -5095,9 +5250,10 @@ void tst_qqmlecmascript::propertyVarReparentNullContext() void tst_qqmlecmascript::propertyVarCircular() { // enforce behaviour regarding circular references - ensure qdvmemo deletion. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc { QCOMPARE(object->property("canaryInt"), QVariant(5)); @@ -5105,7 +5261,7 @@ void tst_qqmlecmascript::propertyVarCircular() QVERIFY(canaryResourceVariant.isValid()); } - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QCOMPARE(object->property("canaryInt"), QVariant(5)); QVariant canaryResourceVariant = object->property("canaryResource"); @@ -5127,16 +5283,17 @@ void tst_qqmlecmascript::propertyVarCircular2() { // track deletion of JS-owned parent item with Cpp-owned child // where the child has a var property referencing its parent. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignCircular"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QObject *rootObject = object->property("vp").value<QObject*>(); - QVERIFY(rootObject != 0); + QVERIFY(rootObject != nullptr); QObject *childObject = rootObject->findChild<QObject*>("text"); - QVERIFY(childObject != 0); + QVERIFY(childObject != nullptr); QPointer<QObject> rootObjectTracker(rootObject); QVERIFY(!rootObjectTracker.isNull()); QPointer<QObject> childObjectTracker(childObject); @@ -5155,11 +5312,12 @@ void tst_qqmlecmascript::propertyVarInheritance() { // enforce behaviour regarding element inheritance - ensure handle disposal. // The particular component under test here has a chain of references. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); // we want to be able to track when the varProperties array of the last metaobject is disposed QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>(); @@ -5174,9 +5332,9 @@ void tst_qqmlecmascript::propertyVarInheritance() // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only // public function which can return us a handle to something in the varProperties array. QV4::ReturnedValue tmp = icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")); - icoCanaryHandle.set(QQmlEnginePrivate::getV4Engine(&engine), tmp); + icoCanaryHandle.set(engine.handle(), tmp); tmp = ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")); - ccoCanaryHandle.set(QQmlEnginePrivate::getV4Engine(&engine), tmp); + ccoCanaryHandle.set(engine.handle(), tmp); tmp = QV4::Encode::null(); QVERIFY(!icoCanaryHandle.isUndefined()); QVERIFY(!ccoCanaryHandle.isUndefined()); @@ -5200,21 +5358,22 @@ void tst_qqmlecmascript::propertyVarInheritance2() { // The particular component under test here does NOT have a chain of references; the // only link between rootObject and childObject is that rootObject is the parent of childObject. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "assignCircular"); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. QCoreApplication::processEvents(); QObject *rootObject = object->property("vp").value<QObject*>(); - QVERIFY(rootObject != 0); + QVERIFY(rootObject != nullptr); QObject *childObject = rootObject->findChild<QObject*>("text"); - QVERIFY(childObject != 0); + QVERIFY(childObject != nullptr); QCOMPARE(rootObject->property("rectCanary").toInt(), 5); QCOMPARE(childObject->property("textCanary").toInt(), 10); QV4::WeakValue childObjectVarArrayValueHandle; { - childObjectVarArrayValueHandle.set(QQmlEnginePrivate::getV4Engine(&engine), + childObjectVarArrayValueHandle.set(engine.handle(), QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp"))); QVERIFY(!childObjectVarArrayValueHandle.isUndefined()); gc(engine); @@ -5233,10 +5392,11 @@ void tst_qqmlecmascript::propertyVarInheritance2() // Ensure that QObject type conversion works on binding assignment void tst_qqmlecmascript::elementAssign() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("elementAssign.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -5246,10 +5406,11 @@ void tst_qqmlecmascript::elementAssign() // QTBUG-12457 void tst_qqmlecmascript::objectPassThroughSignals() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -5259,10 +5420,11 @@ void tst_qqmlecmascript::objectPassThroughSignals() // QTBUG-21626 void tst_qqmlecmascript::objectConversion() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("objectConversion.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant retn; QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn)); QCOMPARE(retn.value<QJSValue>().property("test").toInt(), int(100)); @@ -5274,10 +5436,11 @@ void tst_qqmlecmascript::objectConversion() // QTBUG-20242 void tst_qqmlecmascript::booleanConversion() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("booleanConversion.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test_true1").toBool(), true); QCOMPARE(object->property("test_true2").toBool(), true); @@ -5300,16 +5463,15 @@ void tst_qqmlecmascript::handleReferenceManagement() QQmlEngine hrmEngine; QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); - cro->setEngine(&hrmEngine); cro->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "createReference"); gc(hrmEngine); QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference delete object; hrmEngine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QCOMPARE(dtorCount, 3); } @@ -5320,16 +5482,15 @@ void tst_qqmlecmascript::handleReferenceManagement() QQmlEngine hrmEngine; QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); - cro->setEngine(&hrmEngine); cro->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "circularReference"); gc(hrmEngine); QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive. delete object; hrmEngine.collectGarbage(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QCOMPARE(dtorCount, 3); } @@ -5339,7 +5500,7 @@ void tst_qqmlecmascript::handleReferenceManagement() QQmlEngine hrmEngine; QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "createReference"); gc(hrmEngine); QMetaObject::invokeMethod(object, "ensureReference"); @@ -5356,7 +5517,7 @@ void tst_qqmlecmascript::handleReferenceManagement() QQmlEngine hrmEngine; QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "createReference"); gc(hrmEngine); QMetaObject::invokeMethod(object, "ensureReference"); @@ -5373,7 +5534,7 @@ void tst_qqmlecmascript::handleReferenceManagement() QQmlEngine hrmEngine; QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.dynprop.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "createReference"); gc(hrmEngine); QMetaObject::invokeMethod(object, "ensureReference"); @@ -5388,9 +5549,10 @@ void tst_qqmlecmascript::handleReferenceManagement() void tst_qqmlecmascript::stringArg() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("stringArg.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "success"); QVERIFY(object->property("returnValue").toBool()); @@ -5404,10 +5566,11 @@ void tst_qqmlecmascript::stringArg() void tst_qqmlecmascript::readonlyDeclaration() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -5421,13 +5584,15 @@ Q_DECLARE_METATYPE(QList<QString>) Q_DECLARE_METATYPE(QList<QUrl>) void tst_qqmlecmascript::sequenceConversionRead() { + QQmlEngine engine; + { QUrl qmlFile = testFileUrl("sequenceConversion.read.qml"); QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); - QVERIFY(seq != 0); + QVERIFY(seq != nullptr); QMetaObject::invokeMethod(object, "readSequences"); QList<int> intList; intList << 1 << 2 << 3 << 4; @@ -5476,9 +5641,9 @@ void tst_qqmlecmascript::sequenceConversionRead() QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml"); QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); - QVERIFY(seq != 0); + QVERIFY(seq != nullptr); // we haven't registered QList<NonRegisteredType> as a sequence type. QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<NonRegisteredType>' for property 'MySequenceConversionObject::typeListProperty'"); @@ -5501,13 +5666,14 @@ void tst_qqmlecmascript::sequenceConversionRead() void tst_qqmlecmascript::sequenceConversionWrite() { + QQmlEngine engine; { QUrl qmlFile = testFileUrl("sequenceConversion.write.qml"); QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); - QVERIFY(seq != 0); + QVERIFY(seq != nullptr); QMetaObject::invokeMethod(object, "writeSequences"); QCOMPARE(object->property("success").toBool(), true); @@ -5528,9 +5694,9 @@ void tst_qqmlecmascript::sequenceConversionWrite() QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml"); QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); - QVERIFY(seq != 0); + QVERIFY(seq != nullptr); // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work. QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QJSValue to QList<QPoint>"); @@ -5549,18 +5715,20 @@ void tst_qqmlecmascript::sequenceConversionArray() { // ensure that in JS the returned sequences act just like normal JS Arrays. QUrl qmlFile = testFileUrl("sequenceConversion.array.qml"); + QQmlEngine engine; QQmlComponent component(&engine, qmlFile); - QObject *object = component.create(); - QVERIFY(object != 0); - QMetaObject::invokeMethod(object, "indexedAccess"); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QMetaObject::invokeMethod(object.data(), "indexedAccess"); QVERIFY(object->property("success").toBool()); - QMetaObject::invokeMethod(object, "arrayOperations"); + QMetaObject::invokeMethod(object.data(), "arrayOperations"); QVERIFY(object->property("success").toBool()); - QMetaObject::invokeMethod(object, "testEqualitySemantics"); + QMetaObject::invokeMethod(object.data(), "testEqualitySemantics"); QVERIFY(object->property("success").toBool()); - QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QMetaObject::invokeMethod(object.data(), "testReferenceDeletion"); QCOMPARE(object->property("referenceDeletion").toBool(), true); - delete object; + QMetaObject::invokeMethod(object.data(), "jsonConversion"); + QVERIFY(object->property("success").toBool()); } @@ -5569,9 +5737,10 @@ void tst_qqmlecmascript::sequenceConversionIndexes() // ensure that we gracefully fail if unsupported index values are specified. // Qt container classes only support non-negative, signed integer index values. QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml"); + QQmlEngine engine; QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set"); QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set"); QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get"); @@ -5590,9 +5759,10 @@ void tst_qqmlecmascript::sequenceConversionThreads() // ensure that sequence conversion operations work correctly in a worker thread // and that serialisation between the main and worker thread succeeds. QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml"); + QQmlEngine engine; QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "testIntSequence"); QTRY_VERIFY(object->property("finished").toBool()); @@ -5627,11 +5797,12 @@ void tst_qqmlecmascript::sequenceConversionThreads() void tst_qqmlecmascript::sequenceConversionBindings() { + QQmlEngine engine; { QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml"); QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QList<int> intList; intList << 1 << 2 << 3 << 12 << 7; QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList); QCOMPARE(object->property("boundElement").toInt(), intList.at(3)); @@ -5642,11 +5813,11 @@ void tst_qqmlecmascript::sequenceConversionBindings() { QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml"); - QString warning = QString(QLatin1String("%1:17:27: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString()); + QString warning = QString(QLatin1String("%1:17:9: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString()); QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } } @@ -5654,9 +5825,10 @@ void tst_qqmlecmascript::sequenceConversionBindings() void tst_qqmlecmascript::sequenceConversionCopy() { QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml"); + QQmlEngine engine; QQmlComponent component(&engine, qmlFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "testCopySequences"); QCOMPARE(object->property("success").toBool(), true); QMetaObject::invokeMethod(object, "readSequenceCopyElements"); @@ -5672,11 +5844,13 @@ void tst_qqmlecmascript::sequenceConversionCopy() void tst_qqmlecmascript::assignSequenceTypes() { + QQmlEngine engine; + // test binding array to sequence type property { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml")); MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2)); QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2 << 3)); QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true)); @@ -5690,7 +5864,7 @@ void tst_qqmlecmascript::assignSequenceTypes() { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml")); MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->intListProperty(), (QList<int>() << 1)); QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1)); QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); @@ -5704,7 +5878,7 @@ void tst_qqmlecmascript::assignSequenceTypes() { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml")); MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->intListProperty(), (QList<int>() << 1)); QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1)); QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); @@ -5716,7 +5890,7 @@ void tst_qqmlecmascript::assignSequenceTypes() { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml")); MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2)); QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2 << 3)); QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true)); @@ -5730,7 +5904,7 @@ void tst_qqmlecmascript::assignSequenceTypes() { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml")); MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->intListProperty(), (QList<int>() << 1)); QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1)); QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); @@ -5744,7 +5918,7 @@ void tst_qqmlecmascript::assignSequenceTypes() { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml")); MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->intListProperty(), (QList<int>() << 1)); QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1)); QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); @@ -5756,13 +5930,13 @@ void tst_qqmlecmascript::assignSequenceTypes() { QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1")); MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2")); MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3")); MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4")); MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5")); - QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0); + QVERIFY(msco1 != nullptr && msco2 != nullptr && msco3 != nullptr && msco4 != nullptr && msco5 != nullptr); QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")))); QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")))); QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); @@ -5770,24 +5944,36 @@ void tst_qqmlecmascript::assignSequenceTypes() QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); delete object; } + + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.8.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QVariant result; + QMetaObject::invokeMethod(object.data(), "tryWritingReadOnlySequence", Q_RETURN_ARG(QVariant, result)); + QVERIFY(result.type() == QVariant::Bool); + QVERIFY(result.toBool()); + } } // Test that assigning a null object works // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 void tst_qqmlecmascript::nullObjectBinding() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); - QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0)); + QVERIFY(object->property("test") == QVariant::fromValue((QObject *)nullptr)); delete object; } void tst_qqmlecmascript::nullObjectInitializer() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.qml")); QScopedPointer<QObject> obj(component.create()); @@ -5818,7 +6004,7 @@ void tst_qqmlecmascript::nullObjectInitializer() { const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty"); QVERIFY(propertyIndex > 0); - QVERIFY(ddata->hasBindingBit(propertyIndex)); + QVERIFY(!ddata->hasBindingBit(propertyIndex)); } QVERIFY(obj->property("success").toBool()); @@ -5827,6 +6013,13 @@ void tst_qqmlecmascript::nullObjectInitializer() QVERIFY(value.userType() == qMetaTypeId<QObject*>()); QVERIFY(!value.value<QObject*>()); } + + { + QQmlComponent component(&engine, testFileUrl("qmlVarNullBinding.qml")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVERIFY(!obj->property("signalSeen").toBool()); + } } // Test that bindings don't evaluate once the engine has been destroyed @@ -5836,7 +6029,7 @@ void tst_qqmlecmascript::deletedEngine() QQmlComponent component(engine, testFileUrl("deletedEngine.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 39); object->setProperty("b", QVariant(9)); @@ -5855,20 +6048,22 @@ void tst_qqmlecmascript::deletedEngine() // Test the crashing part of QTBUG-9705 void tst_qqmlecmascript::libraryScriptAssert() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::variantsAssignedUndefined() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toInt(), 10); QCOMPARE(object->property("test2").toInt(), 11); @@ -5884,10 +6079,11 @@ void tst_qqmlecmascript::variantsAssignedUndefined() void tst_qqmlecmascript::variants() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("variants.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid); QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::Nullptr)); @@ -5907,12 +6103,13 @@ void tst_qqmlecmascript::variants() void tst_qqmlecmascript::qtbug_9792() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml")); QQmlContext *context = new QQmlContext(engine.rootContext()); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context)); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtDebugMsg, "Hello world!"); object->basicSignal(); @@ -5931,13 +6128,14 @@ void tst_qqmlecmascript::qtbug_9792() // Verifies that QPointer<>s used in the vmemetaobject are cleaned correctly void tst_qqmlecmascript::qtcreatorbug_1289() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QObject *nested = qvariant_cast<QObject *>(o->property("object")); - QVERIFY(nested != 0); + QVERIFY(nested != nullptr); QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o); @@ -5952,6 +6150,7 @@ void tst_qqmlecmascript::qtcreatorbug_1289() // Test that we shut down without stupid warnings void tst_qqmlecmascript::noSpuriousWarningsAtShutdown() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml")); @@ -5980,13 +6179,14 @@ void tst_qqmlecmascript::noSpuriousWarningsAtShutdown() void tst_qqmlecmascript::canAssignNullToQObject() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml")); MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); - QVERIFY(o->objectProperty() != 0); + QVERIFY(o->objectProperty() != nullptr); o->setProperty("runTest", true); @@ -5999,7 +6199,7 @@ void tst_qqmlecmascript::canAssignNullToQObject() QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml")); MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(!o->objectProperty()); @@ -6009,20 +6209,21 @@ void tst_qqmlecmascript::canAssignNullToQObject() void tst_qqmlecmascript::functionAssignment_fromBinding() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml")); QString url = component.url().toString(); - QString w1 = url + ":4:25: Unable to assign a function to a property of any type other than var."; - QString w2 = url + ":5:25: Invalid use of Qt.binding() in a binding declaration."; - QString w3 = url + ":6:21: Invalid use of Qt.binding() in a binding declaration."; - QString w4 = url + ":7:15: Invalid use of Qt.binding() in a binding declaration."; + QString w1 = url + ":4:5: Unable to assign a function to a property of any type other than var."; + QString w2 = url + ":5:5: Invalid use of Qt.binding() in a binding declaration."; + QString w3 = url + ":6:5: Invalid use of Qt.binding() in a binding declaration."; + QString w4 = url + ":7:5: Invalid use of Qt.binding() in a binding declaration."; QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, w4.toLatin1().constData()); MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(!o->property("a").isValid()); @@ -6033,11 +6234,12 @@ void tst_qqmlecmascript::functionAssignment_fromJS() { QFETCH(QString, triggerProperty); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml")); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(!o->property("a").isValid()); o->setProperty("aNumber", QVariant(5)); @@ -6065,11 +6267,12 @@ void tst_qqmlecmascript::functionAssignment_fromJS_data() void tst_qqmlecmascript::functionAssignmentfromJS_invalid() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml")); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(!o->property("a").isValid()); o->setProperty("assignFuncWithoutReturn", true); @@ -6089,6 +6292,7 @@ void tst_qqmlecmascript::functionAssignmentfromJS_invalid() void tst_qqmlecmascript::functionAssignment_afterBinding() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml")); QString url = component.url().toString(); @@ -6096,7 +6300,7 @@ void tst_qqmlecmascript::functionAssignment_afterBinding() QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData()); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed @@ -6105,10 +6309,11 @@ void tst_qqmlecmascript::functionAssignment_afterBinding() void tst_qqmlecmascript::eval() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("eval.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); @@ -6121,10 +6326,11 @@ void tst_qqmlecmascript::eval() void tst_qqmlecmascript::function() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("function.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); @@ -6136,11 +6342,12 @@ void tst_qqmlecmascript::function() // Test the "Qt.include" method void tst_qqmlecmascript::include() { + QQmlEngine engine; // Non-library relative include { QQmlComponent component(&engine, testFileUrl("include.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test0").toInt(), 99); QCOMPARE(o->property("test1").toBool(), true); @@ -6156,7 +6363,7 @@ void tst_qqmlecmascript::include() { QQmlComponent component(&engine, testFileUrl("include_shared.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test0").toInt(), 99); QCOMPARE(o->property("test1").toBool(), true); @@ -6172,7 +6379,7 @@ void tst_qqmlecmascript::include() { QQmlComponent component(&engine, testFileUrl("include_callback.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); @@ -6188,12 +6395,20 @@ void tst_qqmlecmascript::include() { QQmlComponent component(&engine, testFileUrl("include_pragma.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toInt(), 100); delete o; } + // Including file with ".pragma library", shadowing a global var + { + QQmlComponent component(&engine, testFileUrl("include_pragma_shadow.qml")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("result").toBool(), true); + } + // Remote - error { TestHTTPServer server; @@ -6202,7 +6417,7 @@ void tst_qqmlecmascript::include() QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml")); QObject *o = component.beginCreate(engine.rootContext()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); o->setProperty("serverBaseUrl", server.baseUrl().toString()); component.completeCreate(); @@ -6219,7 +6434,7 @@ void tst_qqmlecmascript::include() { QQmlComponent component(&engine, QUrl("qrc:///data/include.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test0").toInt(), 99); QCOMPARE(o->property("test1").toBool(), true); @@ -6235,18 +6450,15 @@ void tst_qqmlecmascript::include() void tst_qqmlecmascript::includeRemoteSuccess() { -#if defined(Q_CC_MSVC) && _MSC_VER == 1700 - QSKIP("This test does not work reliably with MSVC2012 on Win8 64-bit in release mode."); -#endif - // Remote - success TestHTTPServer server; QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("include_remote.qml")); QObject *o = component.beginCreate(engine.rootContext()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); o->setProperty("serverBaseUrl", server.baseUrl().toString()); component.completeCreate(); @@ -6270,42 +6482,42 @@ void tst_qqmlecmascript::includeRemoteSuccess() void tst_qqmlecmascript::signalHandlers() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("signalHandlers.qml")); - QObject *o = component.create(); - QVERIFY(o != 0); - + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("count").toInt(), 0); - QMetaObject::invokeMethod(o, "testSignalCall"); + QMetaObject::invokeMethod(o.data(), "testSignalCall"); QCOMPARE(o->property("count").toInt(), 1); - QMetaObject::invokeMethod(o, "testSignalHandlerCall"); + QMetaObject::invokeMethod(o.data(), "testSignalHandlerCall"); QCOMPARE(o->property("count").toInt(), 1); QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function")); QCOMPARE(o->property("funcCount").toInt(), 0); - QMetaObject::invokeMethod(o, "testSignalConnection"); + QMetaObject::invokeMethod(o.data(), "testSignalConnection"); QCOMPARE(o->property("funcCount").toInt(), 1); - QMetaObject::invokeMethod(o, "testSignalHandlerConnection"); + QMetaObject::invokeMethod(o.data(), "testSignalHandlerConnection"); QCOMPARE(o->property("funcCount").toInt(), 2); - QMetaObject::invokeMethod(o, "testSignalDefined"); + QMetaObject::invokeMethod(o.data(), "testSignalDefined"); QCOMPARE(o->property("definedResult").toBool(), true); - QMetaObject::invokeMethod(o, "testSignalHandlerDefined"); + QMetaObject::invokeMethod(o.data(), "testSignalHandlerDefined"); QCOMPARE(o->property("definedHandlerResult").toBool(), true); QVariant result; - QMetaObject::invokeMethod(o, "testConnectionOnAlias", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(o.data(), "testConnectionOnAlias", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - QMetaObject::invokeMethod(o, "testAliasSignalHandler", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(o.data(), "testAliasSignalHandler", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - QMetaObject::invokeMethod(o, "testSignalWithClosureArgument", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(o.data(), "testSignalWithClosureArgument", Q_RETURN_ARG(QVariant, result)); + QCOMPARE(result.toBool(), true); + QMetaObject::invokeMethod(o.data(), "testThisInSignalHandler", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - - delete o; } void tst_qqmlecmascript::qtbug_37351() @@ -6331,46 +6543,51 @@ void tst_qqmlecmascript::qtbug_37351() void tst_qqmlecmascript::qtbug_10696() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); delete o; } void tst_qqmlecmascript::qtbug_11606() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); delete o; } void tst_qqmlecmascript::qtbug_11600() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); delete o; } void tst_qqmlecmascript::qtbug_21864() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); delete o; } void tst_qqmlecmascript::rewriteMultiLineStrings() { + QQmlEngine engine; { // QTBUG-23387 QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QTRY_COMPARE(o->property("test").toBool(), true); delete o; } @@ -6378,7 +6595,7 @@ void tst_qqmlecmascript::rewriteMultiLineStrings() { QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings_crlf.1.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); delete o; } } @@ -6386,13 +6603,14 @@ void tst_qqmlecmascript::rewriteMultiLineStrings() void tst_qqmlecmascript::qobjectConnectionListExceptionHandling() { // QTBUG-23375 + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml")); QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined"); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); delete o; } @@ -6400,9 +6618,10 @@ void tst_qqmlecmascript::qobjectConnectionListExceptionHandling() // Reading and writing non-scriptable properties should fail void tst_qqmlecmascript::nonscriptable() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("nonscriptable.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("readOk").toBool(), true); QCOMPARE(o->property("writeOk").toBool(), true); delete o; @@ -6411,9 +6630,10 @@ void tst_qqmlecmascript::nonscriptable() // deleteLater() should not be callable from QML void tst_qqmlecmascript::deleteLater() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deleteLater.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); delete o; } @@ -6421,9 +6641,10 @@ void tst_qqmlecmascript::deleteLater() // objectNameChanged() should be usable from QML void tst_qqmlecmascript::objectNameChangedSignal() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("objectNameChangedSignal.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), false); o->setObjectName("obj"); QCOMPARE(o->property("test").toBool(), true); @@ -6433,6 +6654,7 @@ void tst_qqmlecmascript::objectNameChangedSignal() // destroyed() should not be usable from QML void tst_qqmlecmascript::destroyedSignal() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("destroyedSignal.qml")); QVERIFY(component.isError()); @@ -6442,9 +6664,10 @@ void tst_qqmlecmascript::destroyedSignal() void tst_qqmlecmascript::in() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("in.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); delete o; @@ -6452,10 +6675,11 @@ void tst_qqmlecmascript::in() void tst_qqmlecmascript::typeOf() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("typeOf.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toString(), QLatin1String("undefined")); QCOMPARE(o->property("test2").toString(), QLatin1String("object")); @@ -6472,17 +6696,19 @@ void tst_qqmlecmascript::typeOf() void tst_qqmlecmascript::qtbug_24448() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_24448.qml")); QScopedPointer<QObject> o(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(o->property("test").toBool()); } void tst_qqmlecmascript::sharedAttachedObject() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); delete o; @@ -6491,9 +6717,10 @@ void tst_qqmlecmascript::sharedAttachedObject() // QTBUG-13999 void tst_qqmlecmascript::objectName() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("objectName.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toString(), QString("hello")); QCOMPARE(o->property("test2").toString(), QString("ell")); @@ -6508,9 +6735,10 @@ void tst_qqmlecmascript::objectName() void tst_qqmlecmascript::writeRemovesBinding() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6520,9 +6748,10 @@ void tst_qqmlecmascript::writeRemovesBinding() // Test bindings assigned to alias properties actually assign to the alias' target void tst_qqmlecmascript::aliasBindingsAssignCorrectly() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6532,10 +6761,11 @@ void tst_qqmlecmascript::aliasBindingsAssignCorrectly() // Test bindings assigned to alias properties override a binding on the target (QTBUG-13719) void tst_qqmlecmascript::aliasBindingsOverrideTarget() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6545,7 +6775,7 @@ void tst_qqmlecmascript::aliasBindingsOverrideTarget() { QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6555,7 +6785,7 @@ void tst_qqmlecmascript::aliasBindingsOverrideTarget() { QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6566,10 +6796,11 @@ void tst_qqmlecmascript::aliasBindingsOverrideTarget() // Test that writes to alias properties override bindings on the alias target (QTBUG-13719) void tst_qqmlecmascript::aliasWritesOverrideBindings() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6579,7 +6810,7 @@ void tst_qqmlecmascript::aliasWritesOverrideBindings() { QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6589,7 +6820,7 @@ void tst_qqmlecmascript::aliasWritesOverrideBindings() { QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml")); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); @@ -6601,29 +6832,32 @@ void tst_qqmlecmascript::aliasWritesOverrideBindings() // QTBUG-20200 void tst_qqmlecmascript::aliasToCompositeElement() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::qtbug_20344() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml")); QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::revisionErrors() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml")); QString url = component.url().toString(); @@ -6636,7 +6870,7 @@ void tst_qqmlecmascript::revisionErrors() QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } { @@ -6658,7 +6892,7 @@ void tst_qqmlecmascript::revisionErrors() QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData()); MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } { @@ -6674,19 +6908,20 @@ void tst_qqmlecmascript::revisionErrors() QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } } void tst_qqmlecmascript::revision() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml")); QString url = component.url().toString(); MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } { @@ -6694,7 +6929,7 @@ void tst_qqmlecmascript::revision() QString url = component.url().toString(); MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } { @@ -6702,7 +6937,7 @@ void tst_qqmlecmascript::revision() QString url = component.url().toString(); MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } // Test that non-root classes can resolve revisioned methods @@ -6710,7 +6945,7 @@ void tst_qqmlecmascript::revision() QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toReal(), 11.); delete object; } @@ -6719,7 +6954,7 @@ void tst_qqmlecmascript::revision() QQmlComponent component(&engine, testFileUrl("metaobjectRevision5.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toReal(), 11.); delete object; } @@ -6727,9 +6962,10 @@ void tst_qqmlecmascript::revision() void tst_qqmlecmascript::realToInt() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("realToInt.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "test1"); QCOMPARE(object->value(), int(4)); @@ -6739,10 +6975,11 @@ void tst_qqmlecmascript::realToInt() void tst_qqmlecmascript::urlProperty() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); object->setStringProperty("http://qt-project.org"); QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html")); QCOMPARE(object->intProperty(), 123); @@ -6753,10 +6990,11 @@ void tst_qqmlecmascript::urlProperty() void tst_qqmlecmascript::urlPropertyWithEncoding() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); object->setStringProperty("http://qt-project.org"); const QUrl encoded = QUrl::fromEncoded("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); QCOMPARE(object->urlProperty(), encoded); @@ -6767,15 +7005,16 @@ void tst_qqmlecmascript::urlPropertyWithEncoding() void tst_qqmlecmascript::urlListPropertyWithEncoding() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("urlListProperty.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1")); MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2")); MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3")); MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4")); - QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0); + QVERIFY(msco1 != nullptr && msco2 != nullptr && msco3 != nullptr && msco4 != nullptr); const QUrl encoded = QUrl::fromEncoded("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded)); QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded)); @@ -6787,50 +7026,57 @@ void tst_qqmlecmascript::urlListPropertyWithEncoding() void tst_qqmlecmascript::dynamicString() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("dynamicString.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("stringProperty").toString(), QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!")); } void tst_qqmlecmascript::deleteLaterObjectMethodCall() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlecmascript::automaticSemicolon() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlecmascript::compatibilitySemicolon() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("compatibilitySemicolon.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlecmascript::incrDecrSemicolon1() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlecmascript::incrDecrSemicolon2() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlecmascript::incrDecrSemicolon_error1() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon_error1.qml")); QObject *object = component.create(); QVERIFY(!object); @@ -6838,19 +7084,21 @@ void tst_qqmlecmascript::incrDecrSemicolon_error1() void tst_qqmlecmascript::unaryExpression() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("unaryExpression.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } // Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice void tst_qqmlecmascript::doubleEvaluate() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); WriteCounter *wc = qobject_cast<WriteCounter *>(object); - QVERIFY(wc != 0); + QVERIFY(wc != nullptr); QCOMPARE(wc->count(), 1); wc->setProperty("x", 9); @@ -6862,17 +7110,18 @@ void tst_qqmlecmascript::doubleEvaluate() void tst_qqmlecmascript::nonNotifyable() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml")); QQmlTestMessageHandler messageHandler; QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString expected1 = QLatin1String("QQmlExpression: Expression ") + component.url().toString() + - QLatin1String(":5:24 depends on non-NOTIFYable properties:"); + QLatin1String(":5:5 depends on non-NOTIFYable properties:"); QString expected2 = QLatin1String(" ") + QLatin1String(object->metaObject()->className()) + QLatin1String("::value"); @@ -6884,11 +7133,25 @@ void tst_qqmlecmascript::nonNotifyable() delete object; } +void tst_qqmlecmascript::nonNotifyableConstant() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("nonNotifyableConstant.qml")); + QQmlTestMessageHandler messageHandler; + + // Shouldn't produce an error message about non-NOTIFYable properties, + // as the property has the CONSTANT attribute. + QScopedPointer<QObject> object(component.create()); + QVERIFY(object); + QCOMPARE(messageHandler.messages().size(), 0); +} + void tst_qqmlecmascript::forInLoop() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("forInLoop.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QMetaObject::invokeMethod(object, "listProperty"); @@ -6906,9 +7169,10 @@ void tst_qqmlecmascript::forInLoop() // An object the binding depends on is deleted while the binding is still running void tst_qqmlecmascript::deleteWhileBindingRunning() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -6916,6 +7180,7 @@ void tst_qqmlecmascript::qtbug_22679() { MyQmlObject object; object.setStringProperty(QLatin1String("Please work correctly")); + QQmlEngine engine; engine.rootContext()->setContextProperty("contextProp", &object); QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml")); @@ -6923,7 +7188,7 @@ void tst_qqmlecmascript::qtbug_22679() QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>))); QObject *o = component.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(warningsSpy.count(), 0); delete o; } @@ -6945,6 +7210,7 @@ void tst_qqmlecmascript::qtbug_22843() fileName += QLatin1String(".library"); fileName += QLatin1String(".qml"); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(fileName)); QString url = component.url().toString(); @@ -6957,10 +7223,11 @@ void tst_qqmlecmascript::qtbug_22843() void tst_qqmlecmascript::switchStatement() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -6983,7 +7250,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7006,7 +7273,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7029,11 +7296,11 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml")); - QString warning = component.url().toString() + ":4:12: Unable to assign [undefined] to int"; + QString warning = component.url().toString() + ":4:5: Unable to assign [undefined] to int"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7057,7 +7324,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7080,7 +7347,7 @@ void tst_qqmlecmascript::switchStatement() { QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // `object->value()' is the number of executed statements @@ -7103,11 +7370,12 @@ void tst_qqmlecmascript::switchStatement() void tst_qqmlecmascript::withStatement() { + QQmlEngine engine; { QUrl url = testFileUrl("withStatement.1.qml"); QQmlComponent component(&engine, url); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->value(), 123); } @@ -7115,10 +7383,11 @@ void tst_qqmlecmascript::withStatement() void tst_qqmlecmascript::tryStatement() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->value(), 123); } @@ -7126,7 +7395,7 @@ void tst_qqmlecmascript::tryStatement() { QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->value(), 321); } @@ -7134,17 +7403,17 @@ void tst_qqmlecmascript::tryStatement() { QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); - QCOMPARE(object->value(), 1); + QVERIFY(object->qjsvalue().isUndefined()); } { QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); - QCOMPARE(object->value(), 1); + QVERIFY(object->qjsvalue().isUndefined()); } } @@ -7180,7 +7449,7 @@ void tst_qqmlecmascript::invokableWithQObjectDerived() QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("result").value<bool>()); delete object; @@ -7190,9 +7459,10 @@ void tst_qqmlecmascript::invokableWithQObjectDerived() void tst_qqmlecmascript::realTypePrecision() { // Properties and signal parameters of type real should have double precision. + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("realTypePrecision.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toDouble(), 1234567890.); QCOMPARE(object->property("test2").toDouble(), 1234567890.); QCOMPARE(object->property("test3").toDouble(), 1234567890.); @@ -7206,7 +7476,7 @@ void tst_qqmlecmascript::registeredFlagMethod() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("registeredFlagMethod.qml")); MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->buttons(), 0); emit object->basicSignal(); @@ -7221,7 +7491,18 @@ void tst_qqmlecmascript::replaceBinding() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("replaceBinding.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); + + QVERIFY(obj->property("success").toBool()); + delete obj; +} + +void tst_qqmlecmascript::bindingBoundFunctions() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindingBoundFunctions.qml")); + QObject *obj = c.create(); + QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); delete obj; @@ -7229,11 +7510,11 @@ void tst_qqmlecmascript::replaceBinding() void tst_qqmlecmascript::deleteRootObjectInCreation() { - { QQmlEngine engine; + { QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVERIFY(obj->property("rootIndestructible").toBool()); QVERIFY(!obj->property("childDestructible").toBool()); QTest::qWait(1); @@ -7244,7 +7525,7 @@ void tst_qqmlecmascript::deleteRootObjectInCreation() { QQmlComponent c(&engine, testFileUrl("deleteRootObjectInCreation.2.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("testConditionsMet").toBool()); delete object; } @@ -7258,9 +7539,9 @@ void tst_qqmlecmascript::onDestruction() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("onDestruction.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); delete obj; - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } { @@ -7271,8 +7552,8 @@ void tst_qqmlecmascript::onDestruction() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("onDestruction.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QVERIFY(obj != nullptr); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } } @@ -7281,8 +7562,8 @@ class WeakReferenceMutator : public QObject Q_OBJECT public: WeakReferenceMutator() - : resultPtr(Q_NULLPTR) - , weakRef(Q_NULLPTR) + : resultPtr(nullptr) + , weakRef(nullptr) {} void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr) @@ -7298,10 +7579,11 @@ public: private slots: void reviveFirstWeakReference() { - *resultPtr = weakRef->valueRef() && weakRef->isNullOrUndefined(); + // weakRef is not required to be undefined here. The gc can clear it later. + *resultPtr = weakRef->valueRef(); if (!*resultPtr) return; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(this)); + QV4::ExecutionEngine *v4 = qmlEngine(this)->handle(); weakRef->set(v4, v4->newObject()); *resultPtr = weakRef->valueRef() && !weakRef->isNullOrUndefined(); } @@ -7351,7 +7633,7 @@ void tst_qqmlecmascript::onDestructionViaGC() qmlRegisterType<WeakReferenceMutator>("Test", 1, 0, "WeakReferenceMutator"); QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + QV4::ExecutionEngine *v4 =engine.handle(); QQmlComponent component(&engine, testFileUrl("DestructionHelper.qml")); @@ -7369,7 +7651,7 @@ void tst_qqmlecmascript::onDestructionViaGC() QVERIFY2(!weakReferenceMutator.isNull(), qPrintable(component.errorString())); weakReferenceMutator->init(v4, weakRef.data(), &mutatorResult); - v4->memoryManager->allocObject<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult); + v4->memoryManager->allocate<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult); } gc(engine); @@ -7383,7 +7665,7 @@ struct EventProcessor : public QObject public: Q_INVOKABLE void process() { - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } }; @@ -7398,7 +7680,7 @@ void tst_qqmlecmascript::bindingSuppression() QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); delete obj; QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); @@ -7411,7 +7693,7 @@ void tst_qqmlecmascript::signalEmitted() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("signalEmitted.2.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QTRY_VERIFY(obj->property("success").toBool()); delete obj; } @@ -7421,7 +7703,7 @@ void tst_qqmlecmascript::signalEmitted() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("signalEmitted.3.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); gc(engine); // should collect c1. QTRY_VERIFY(obj->property("success").toBool()); delete obj; @@ -7432,7 +7714,7 @@ void tst_qqmlecmascript::signalEmitted() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("signalEmitted.4.qml")); QObject *obj = c.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); gc(engine); // should collect c1. QMetaObject::invokeMethod(obj, "destroyC2"); QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later). @@ -7443,38 +7725,36 @@ void tst_qqmlecmascript::signalEmitted() // QTBUG-25647 void tst_qqmlecmascript::threadSignal() { + QQmlEngine engine; { - QQmlComponent c(&engine, testFileUrl("threadSignal.qml")); - QObject *object = c.create(); - QVERIFY(object != 0); - QTRY_VERIFY(object->property("passed").toBool()); - delete object; + QQmlComponent c(&engine, testFileUrl("threadSignal.qml")); + QScopedPointer<QObject> object(c.create()); + QVERIFY(!object.isNull()); + QTRY_VERIFY(object->property("passed").toBool()); } { - QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml")); - QObject *object = c.create(); - QVERIFY(object != 0); - QSignalSpy doneSpy(object, SIGNAL(done(QString))); - QMetaObject::invokeMethod(object, "doIt"); - QTRY_VERIFY(object->property("passed").toBool()); - QCOMPARE(doneSpy.count(), 1); - delete object; + QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml")); + QScopedPointer<QObject> object(c.create()); + QVERIFY(!object.isNull()); + QMetaObject::invokeMethod(object.data(), "doIt"); + QTRY_VERIFY(object->property("passed").toBool()); } } // ensure that the qqmldata::destroyed() handler doesn't cause problems void tst_qqmlecmascript::qqmldataDestroyed() { + QQmlEngine engine; // gc cleans up a qobject, later the qqmldata destroyed handler will run. { QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // now gc causing the collection of the dynamically constructed object. engine.collectGarbage(); engine.collectGarbage(); // now process events to allow deletion (calling qqmldata::destroyed()) - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); // shouldn't crash. delete object; @@ -7485,7 +7765,7 @@ void tst_qqmlecmascript::qqmldataDestroyed() { QQmlComponent c(&engine, testFileUrl("qqmldataDestroyed.2.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("testConditionsMet").toBool()); // the gc() within the handler will have triggered the weak // qobject reference callback. If that incorrectly disposes @@ -7498,9 +7778,10 @@ void tst_qqmlecmascript::qqmldataDestroyed() void tst_qqmlecmascript::secondAlias() { + QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("secondAlias.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 200); delete object; } @@ -7508,9 +7789,10 @@ void tst_qqmlecmascript::secondAlias() // An alias to a var property works void tst_qqmlecmascript::varAlias() { + QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("varAlias.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 192); delete object; } @@ -7518,9 +7800,10 @@ void tst_qqmlecmascript::varAlias() // Used to trigger an assert in the lazy meta object creation stage void tst_qqmlecmascript::overrideDataAssert() { + QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("overrideDataAssert.qml")); QObject *object = c.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); object->metaObject(); delete object; } @@ -7543,18 +7826,20 @@ void tst_qqmlecmascript::fallbackBindings() { QFETCH(QString, source); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(source)); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); } void tst_qqmlecmascript::propertyOverride() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("propertyOverride.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); } @@ -7587,12 +7872,13 @@ void tst_qqmlecmascript::sequenceSort() QFETCH(QString, function); QFETCH(bool, useComparer); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("sequenceSort.qml")); QObject *object = component.create(); - if (object == 0) + if (object == nullptr) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant q; QMetaObject::invokeMethod(object, function.toLatin1().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer)); @@ -7603,12 +7889,13 @@ void tst_qqmlecmascript::sequenceSort() void tst_qqmlecmascript::dateParse() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("date.qml")); QObject *object = component.create(); - if (object == 0) + if (object == nullptr) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant q; QMetaObject::invokeMethod(object, "test_is_invalid_jsDateTime", Q_RETURN_ARG(QVariant, q)); @@ -7623,12 +7910,13 @@ void tst_qqmlecmascript::dateParse() void tst_qqmlecmascript::utcDate() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("utcdate.qml")); QObject *object = component.create(); - if (object == 0) + if (object == nullptr) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant q; QVariant val = QString::fromLatin1("2014-07-16T23:30:31"); @@ -7638,12 +7926,13 @@ void tst_qqmlecmascript::utcDate() void tst_qqmlecmascript::negativeYear() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("negativeyear.qml")); QObject *object = component.create(); - if (object == 0) + if (object == nullptr) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant q; QMetaObject::invokeMethod(object, "check_negative_tostring", Q_RETURN_ARG(QVariant, q)); @@ -7659,6 +7948,7 @@ void tst_qqmlecmascript::negativeYear() void tst_qqmlecmascript::concatenatedStringPropertyAccess() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("concatenatedStringPropertyAccess.qml")); QObject *object = component.create(); QVERIFY(object); @@ -7704,19 +7994,21 @@ void tst_qqmlecmascript::updateCall() // documented it can be called from within QML. Make sure // we don't crash when calling it. QString file("updateCall.qml"); + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(file)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlecmascript::numberParsing() { + QQmlEngine engine; for (int i = 1; i < 8; ++i) { QString file("numberParsing.%1.qml"); file = file.arg(i); QQmlComponent component(&engine, testFileUrl(file)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } for (int i = 1; i < 3; ++i) { QString file("numberParsing_error.%1.qml"); @@ -7728,6 +8020,7 @@ void tst_qqmlecmascript::numberParsing() void tst_qqmlecmascript::stringParsing() { + QQmlEngine engine; for (int i = 1; i < 7; ++i) { QString file("stringParsing_error.%1.qml"); file = file.arg(i); @@ -7751,10 +8044,11 @@ void tst_qqmlecmascript::push_and_shift() void tst_qqmlecmascript::qtbug_32801() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_32801.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); // do not crash when a QML signal is connected to a non-void slot connect(obj.data(), SIGNAL(testSignal(QString)), obj.data(), SLOT(slotWithReturnValue(QString))); @@ -7763,6 +8057,7 @@ void tst_qqmlecmascript::qtbug_32801() void tst_qqmlecmascript::thisObject() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("thisObject.qml")); QObject *object = component.create(); QVERIFY(object); @@ -7772,21 +8067,23 @@ void tst_qqmlecmascript::thisObject() void tst_qqmlecmascript::qtbug_33754() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_33754.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); } void tst_qqmlecmascript::qtbug_34493() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_34493.qml")); QScopedPointer<QObject> obj(component.create()); if (component.errors().size()) qDebug() << component.errors(); QVERIFY(component.errors().isEmpty()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVERIFY(QMetaObject::invokeMethod(obj.data(), "doIt")); QTRY_VERIFY(obj->property("prop").toString() == QLatin1String("Hello World!")); } @@ -7795,12 +8092,13 @@ void tst_qqmlecmascript::qtbug_34493() // as its type*, it's parent type* and as QObject* void tst_qqmlecmascript::singletonFromQMLToCpp() { + QQmlEngine engine; QQmlComponent component(&engine, testFile("singletonTest.qml")); QScopedPointer<QObject> obj(component.create()); if (component.errors().size()) qDebug() << component.errors(); QVERIFY(component.errors().isEmpty()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("qobjectTest"), QVariant(true)); QCOMPARE(obj->property("myQmlObjectTest"), QVariant(true)); @@ -7812,12 +8110,13 @@ void tst_qqmlecmascript::singletonFromQMLToCpp() // and correctly compares to itself void tst_qqmlecmascript::singletonFromQMLAndBackAndCompare() { + QQmlEngine engine; QQmlComponent component(&engine, testFile("singletonTest2.qml")); QScopedPointer<QObject> o(component.create()); if (component.errors().size()) qDebug() << component.errors(); QVERIFY(component.errors().isEmpty()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(o->property("myInheritedQmlObjectTest1"), QVariant(true)); QCOMPARE(o->property("myInheritedQmlObjectTest2"), QVariant(true)); @@ -7836,9 +8135,10 @@ void tst_qqmlecmascript::singletonFromQMLAndBackAndCompare() void tst_qqmlecmascript::setPropertyOnInvalid() { + QQmlEngine engine; { QQmlComponent component(&engine, testFileUrl("setPropertyOnNull.qml")); - QString warning = component.url().toString() + ":4: TypeError: Type error"; + QString warning = component.url().toString() + ":4: TypeError: Value is null and could not be converted to an object"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *object = component.create(); QVERIFY(object); @@ -7847,7 +8147,7 @@ void tst_qqmlecmascript::setPropertyOnInvalid() { QQmlComponent component(&engine, testFileUrl("setPropertyOnUndefined.qml")); - QString warning = component.url().toString() + ":4: TypeError: Type error"; + QString warning = component.url().toString() + ":4: TypeError: Value is undefined and could not be converted to an object"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); QObject *object = component.create(); QVERIFY(object); @@ -7857,12 +8157,13 @@ void tst_qqmlecmascript::setPropertyOnInvalid() void tst_qqmlecmascript::miscTypeTest() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("misctypetest.qml")); QObject *object = component.create(); - if (object == 0) + if (object == nullptr) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant q; QMetaObject::invokeMethod(object, "test_invalid_url_equal", Q_RETURN_ARG(QVariant, q)); @@ -7885,6 +8186,7 @@ void tst_qqmlecmascript::stackLimits() void tst_qqmlecmascript::idsAsLValues() { + QQmlEngine engine; QString err = QString(QLatin1String("%1:5 left-hand side of assignment operator is not an lvalue\n")).arg(testFileUrl("idAsLValue.qml").toString()); QQmlComponent component(&engine, testFileUrl("idAsLValue.qml")); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); @@ -7895,17 +8197,19 @@ void tst_qqmlecmascript::idsAsLValues() void tst_qqmlecmascript::qtbug_34792() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug34792.qml")); QObject *object = component.create(); - if (object == 0) + if (object == nullptr) qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } void tst_qqmlecmascript::noCaptureWhenWritingProperty() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("noCaptureWhenWritingProperty.qml")); QScopedPointer<QObject> obj(component.create()); QVERIFY(!obj.isNull()); @@ -7914,6 +8218,7 @@ void tst_qqmlecmascript::noCaptureWhenWritingProperty() void tst_qqmlecmascript::singletonWithEnum() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("singletontype/singletonWithEnum.qml")); QScopedPointer<QObject> obj(component.create()); if (obj.isNull()) @@ -7922,52 +8227,66 @@ void tst_qqmlecmascript::singletonWithEnum() QVariant prop = obj->property("testValue"); QCOMPARE(prop.type(), QVariant::Int); QCOMPARE(prop.toInt(), int(SingletonWithEnum::TestValue)); + + { + QQmlExpression expr(qmlContext(obj.data()), obj.data(), "SingletonWithEnum.TestValue_MinusOne"); + bool valueUndefined = false; + QVariant result = expr.evaluate(&valueUndefined); + QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString())); + QVERIFY(!valueUndefined); + QCOMPARE(result.toInt(), -1); + } } void tst_qqmlecmascript::lazyBindingEvaluation() { - QQmlComponent component(&engine, testFileUrl("lazyBindingEvaluation.qml")); - QScopedPointer<QObject> obj(component.create()); - if (obj.isNull()) + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("lazyBindingEvaluation.qml")); + QScopedPointer<QObject> obj(component.create()); + if (obj.isNull()) qDebug() << component.errors().first().toString(); - QVERIFY(!obj.isNull()); - QVariant prop = obj->property("arrayLength"); - QCOMPARE(prop.type(), QVariant::Int); - QCOMPARE(prop.toInt(), 2); + QVERIFY(!obj.isNull()); + QVariant prop = obj->property("arrayLength"); + QCOMPARE(prop.type(), QVariant::Int); + QCOMPARE(prop.toInt(), 2); } void tst_qqmlecmascript::varPropertyAccessOnObjectWithInvalidContext() { - QQmlComponent component(&engine, testFileUrl("varPropertyAccessOnObjectWithInvalidContext.qml")); - QScopedPointer<QObject> obj(component.create()); - if (obj.isNull()) + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("varPropertyAccessOnObjectWithInvalidContext.qml")); + QScopedPointer<QObject> obj(component.create()); + if (obj.isNull()) qDebug() << component.errors().first().toString(); - QVERIFY(!obj.isNull()); - QVERIFY(obj->property("success").toBool()); + QVERIFY(!obj.isNull()); + QVERIFY(obj->property("success").toBool()); } void tst_qqmlecmascript::importedScriptsAccessOnObjectWithInvalidContext() { - QQmlComponent component(&engine, testFileUrl("importedScriptsAccessOnObjectWithInvalidContext.qml")); - QScopedPointer<QObject> obj(component.create()); - if (obj.isNull()) + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("importedScriptsAccessOnObjectWithInvalidContext.qml")); + QScopedPointer<QObject> obj(component.create()); + if (obj.isNull()) qDebug() << component.errors().first().toString(); - QVERIFY(!obj.isNull()); - QTRY_VERIFY(obj->property("success").toBool()); + QVERIFY(!obj.isNull()); + QTRY_VERIFY(obj->property("success").toBool()); } void tst_qqmlecmascript::importedScriptsWithoutQmlMode() { - QQmlComponent component(&engine, testFileUrl("importScriptsWithoutQmlMode.qml")); - QScopedPointer<QObject> obj(component.create()); - if (obj.isNull()) + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("importScriptsWithoutQmlMode.qml")); + QScopedPointer<QObject> obj(component.create()); + if (obj.isNull()) qDebug() << component.errors().first().toString(); - QVERIFY(!obj.isNull()); - QTRY_VERIFY(obj->property("success").toBool()); + QVERIFY(!obj.isNull()); + QTRY_VERIFY(obj->property("success").toBool()); } void tst_qqmlecmascript::contextObjectOnLazyBindings() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("contextObjectOnLazyBindings.qml")); QScopedPointer<QObject> obj(component.create()); if (obj.isNull()) @@ -7980,6 +8299,7 @@ void tst_qqmlecmascript::contextObjectOnLazyBindings() void tst_qqmlecmascript::garbageCollectionDuringCreation() { + QQmlEngine engine; QQmlComponent component(&engine); component.setData("import Qt.test 1.0\n" "QObjectContainerWithGCOnAppend {\n" @@ -8007,6 +8327,7 @@ void tst_qqmlecmascript::garbageCollectionDuringCreation() void tst_qqmlecmascript::qtbug_39520() { + QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick 2.0\n" "Item {\n" @@ -8045,7 +8366,7 @@ class ObjectContainer : public QObject Q_PROPERTY(ContainedObject1 *object1 READ object1 WRITE setObject1) Q_PROPERTY(ContainedObject2 *object2 READ object2 WRITE setObject2) public: - explicit ObjectContainer(QObject *parent = 0) : + explicit ObjectContainer(QObject *parent = nullptr) : QObject(parent), mGetterCalled(false), mSetterCalled(false) @@ -8055,7 +8376,7 @@ public: ContainedObject1 *object1() { mGetterCalled = true; - return 0; + return nullptr; } void setObject1(ContainedObject1 *) @@ -8066,7 +8387,7 @@ public: ContainedObject2 *object2() { mGetterCalled = true; - return 0; + return nullptr; } void setObject2(ContainedObject2 *) @@ -8128,16 +8449,18 @@ void tst_qqmlecmascript::switchExpression() void tst_qqmlecmascript::qtbug_46022() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_46022.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("test1").toBool(), true); QCOMPARE(obj->property("test2").toBool(), true); } void tst_qqmlecmascript::qtbug_52340() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_52340.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -8149,10 +8472,11 @@ void tst_qqmlecmascript::qtbug_52340() void tst_qqmlecmascript::qtbug_54589() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_54589.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("result").toBool(), true); } @@ -8160,16 +8484,18 @@ void tst_qqmlecmascript::qtbug_54687() { QJSEngine e; // it's simple: this shouldn't crash. - engine.evaluate("12\n----12"); + e.evaluate("12\n----12"); } void tst_qqmlecmascript::stringify_qtbug_50592() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("stringify_qtbug_50592.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); - QCOMPARE(obj->property("source").toString(), QString::fromLatin1("http://example.org/some_nonexistant_image.png")); + QVERIFY(obj != nullptr); + QCOMPARE(obj->property("source").toString(), + QString::fromLatin1("\"http://example.org/some_nonexistant_image.png\"")); } // Tests for the JS-only instanceof. Tests for the QML extensions for @@ -8226,7 +8552,7 @@ void tst_qqmlecmascript::instanceof() QJSEngine engine; QJSValue ret = engine.evaluate(setupCode + ";\n" + QTest::currentDataTag()); - if (expectedValue.type() == QMetaType::Bool) { + if (expectedValue.type() == QVariant::Bool) { bool returnValue = ret.toBool(); QVERIFY2(!ret.isError(), qPrintable(ret.toString())); QCOMPARE(returnValue, expectedValue.toBool()); @@ -8352,10 +8678,11 @@ void tst_qqmlecmascript::freeze_empty_object() void tst_qqmlecmascript::singleBlockLoops() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug_59012.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVERIFY(!component.isError()); } @@ -8363,12 +8690,249 @@ void tst_qqmlecmascript::singleBlockLoops() // This fix ensures it looks up the right thing. void tst_qqmlecmascript::qtbug_60547() { + QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("qtbug60547/main.qml")); QScopedPointer<QObject> object(component.create()); QVERIFY2(!object.isNull(), qPrintable(component.errorString())); QCOMPARE(object->property("counter"), QVariant(int(1))); } +void tst_qqmlecmascript::anotherNaN() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("nans.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY2(!object.isNull(), qPrintable(component.errorString())); + object->setProperty("prop", std::numeric_limits<double>::quiet_NaN()); // don't crash + + std::uint64_t anotherNaN = 0xFFFFFF01000000F7ul; + double d; + std::memcpy(&d, &anotherNaN, sizeof(d)); + QVERIFY(std::isnan(d)); + object->setProperty("prop", d); // don't crash +} + +void tst_qqmlecmascript::delayLoadingArgs() +{ + QJSEngine engine; + QJSValue ret = engine.evaluate("(function(x){return x + (x+=2)})(20)"); + QCOMPARE(ret.toInt(), 42); // esp. not 44. +} + +void tst_qqmlecmascript::manyArguments() +{ + const char *testCase = + "function x() { var sum; for (var i = 0; i < arguments.length; ++i) sum += arguments[i][0]; }" + "x([0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9])"; + + QJSEngine engine; + engine.evaluate(testCase); +} + +void tst_qqmlecmascript::forInIterator() +{ + auto testCase = + "(function(){\n" + "var x = 'yoyo'\n" + "var i\n" + "for (i in x) {\n" + "}\n" + "return i\n" + "})()"; + QJSEngine engine; + QJSValue ret = engine.evaluate(testCase); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QStringLiteral("3")); +} + +void tst_qqmlecmascript::localForInIterator() +{ + auto testCase = + "(function(){\n" + "var x = 'yoyo'\n" + "for (var i in x) {\n" + "}\n" + "return i\n" + "})()"; + QJSEngine engine; + QJSValue ret = engine.evaluate(testCase); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QStringLiteral("3")); +} + +void tst_qqmlecmascript::shadowedFunctionName() +{ + // verify that arguments shadow the function name + QJSEngine engine; + QJSValue v = engine.evaluate(QString::fromLatin1( + "function f(f) { return f; }\n" + "f(true)\n" + )); + QVERIFY(!v.isError()); + QVERIFY(v.isBool()); + QCOMPARE(v.toBool(), true); +} + +void tst_qqmlecmascript::callPropertyOnUndefined() +{ + QJSEngine engine; + QJSValue v = engine.evaluate(QString::fromLatin1( + "function f() {\n" + " var base;\n" + " base.push(1);" + "}\n" + )); + QVERIFY(!v.isError()); // well, more importantly: this shouldn't fail on an assert. +} + +void tst_qqmlecmascript::jumpStrictNotEqualUndefined() +{ + QJSEngine engine; + QJSValue v = engine.evaluate(QString::fromLatin1( + "var ok = 0\n" + "var foo = 0\n" + "if (foo !== void 1)\n" + " ++ok;\n" + "else\n" + " --ok;\n" + "if (foo === void 1)\n" + " --ok;\n" + "else\n" + " ++ok;\n" + "ok\n" + )); + QVERIFY(!v.isError()); + QCOMPARE(v.toInt(), 2); +} + +void tst_qqmlecmascript::removeBindingsWithNoDependencies() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("removeBindingsWithNoDependencies.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QVariant rect = object->property("placement"); + QCOMPARE(rect.toRectF(), QRectF(0, 0, 100, 100)); + const QMetaObject *metaObject = object->metaObject(); + + { + const QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("placement")); + QVERIFY(prop.isValid()); + QVERIFY(!QQmlPropertyPrivate::binding(object.data(), QQmlPropertyIndex(prop.propertyIndex()))); + } + + { + const QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("partialPlacement")); + QVERIFY(prop.isValid()); + QQmlAbstractBinding *vtProxyBinding = QQmlPropertyPrivate::binding(object.data(), QQmlPropertyIndex(prop.propertyIndex())); + QVERIFY(vtProxyBinding); + QVERIFY(vtProxyBinding->isValueTypeProxy()); + + QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding*>(vtProxyBinding); + QVERIFY(!proxy->subBindings()); + } + +} + +void tst_qqmlecmascript::temporaryDeadZone() +{ + QJSEngine engine; + QJSValue v = engine.evaluate(QString::fromLatin1("a; let a;")); + QVERIFY(v.isError()); + v = engine.evaluate(QString::fromLatin1("a.name; let a;")); + QVERIFY(v.isError()); + v = engine.evaluate(QString::fromLatin1("var a = {}; a[b]; let b;")); + QVERIFY(v.isError()); + v = engine.evaluate(QString::fromLatin1("class C { constructor() { super[x]; let x; } }; new C()")); + QVERIFY(v.isError()); +} + +void tst_qqmlecmascript::importLexicalVariables_data() +{ + QTest::addColumn<QUrl>("testFile"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("script") + << testFileUrl("importLexicalVariables_script.qml") + << QStringLiteral("000 100 210"); + QTest::newRow("pragmaLibrary") + << testFileUrl("importLexicalVariables_pragmaLibrary.qml") + << QStringLiteral("000 100 210"); + QTest::newRow("module") + << testFileUrl("importLexicalVariables_module.qml") + << QStringLiteral("000 000 110"); +} + +void tst_qqmlecmascript::importLexicalVariables() +{ + QFETCH(QUrl, testFile); + QFETCH(QString, expected); + + QQmlEngine engine; + QQmlComponent component(&engine, testFile); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QVERIFY(!component.isError()); + + QVariant result; + QMetaObject::invokeMethod(object.data(), "runTest", Qt::DirectConnection, Q_RETURN_ARG(QVariant, result)); + QCOMPARE(result, QVariant(expected)); +} + +void tst_qqmlecmascript::hugeObject() +{ + // mainly check that this doesn't crash + QJSEngine engine; + QJSValue v = engine.evaluate(QString::fromLatin1( + "var known = {}, prefix = 'x'\n" + "for (var i = 0; i < 150000; i++) known[prefix + i] = true;" + )); + QVERIFY(!v.isError()); +} + +void tst_qqmlecmascript::templateStringTerminator() +{ + QJSEngine engine; + const QJSValue value = engine.evaluate("let a = 123; let b = `x${a}\ny^`; b;"); + QVERIFY(!value.isError()); + QCOMPARE(value.toString(), QLatin1String("x123\ny^")); +} + +void tst_qqmlecmascript::arrayAndException() +{ + QJSEngine engine; + const QJSValue value = engine.evaluate("[...[],[,,$]]"); + // Should not crash + QVERIFY(value.isError()); +} + +void tst_qqmlecmascript::numberToStringWithRadix() +{ + QJSEngine engine; + { + const QJSValue value = engine.evaluate(".5.toString(5)"); + QVERIFY(!value.isError()); + QVERIFY(value.toString().startsWith("0.2222222222")); + } + { + const QJSValue value = engine.evaluate(".05.toString(5)"); + QVERIFY(!value.isError()); + QVERIFY(value.toString().startsWith("0.01111111111")); + } +} + +void tst_qqmlecmascript::tailCallWithArguments() +{ + QJSEngine engine; + const QJSValue value = engine.evaluate( + "'use strict';\n" + "[[1, 2]].map(function (a) {\n" + " return (function() { return Math.min.apply(this, arguments); })(a[0], a[1]);\n" + "})[0];"); + QVERIFY(!value.isError()); + QCOMPARE(value.toInt(), 1); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml new file mode 100644 index 0000000000..1047926750 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml @@ -0,0 +1,10 @@ +import QtQuick 2.8 + +Item { + property alias textEdit: textEdit + + TextEdit { + id: textEdit + + } +} diff --git a/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml new file mode 100644 index 0000000000..552c6c3791 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +Item { + property alias textEdit: textEdit + + TextEdit { + id: textEdit + + } +} diff --git a/tests/auto/qml/qqmlengine/data/qrcurls.js b/tests/auto/qml/qqmlengine/data/qrcurls.js new file mode 100644 index 0000000000..15a4d5a70c --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/qrcurls.js @@ -0,0 +1 @@ +function someFunction() {} diff --git a/tests/auto/qml/qqmlengine/data/qrcurls.qml b/tests/auto/qml/qqmlengine/data/qrcurls.qml new file mode 100644 index 0000000000..e879577e10 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/qrcurls.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 + +QtObject { +} diff --git a/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml new file mode 100644 index 0000000000..39bd01fe40 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml @@ -0,0 +1,7 @@ +import QtQuick 2.8 + +Item { + GroupedPropertiesRevisionComponent1 { + textEdit.onEditingFinished: console.log("test") + } +} diff --git a/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml new file mode 100644 index 0000000000..bb9ba79b01 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml @@ -0,0 +1,7 @@ +import QtQuick 2.8 + +Item { + GroupedPropertiesRevisionComponent2 { + textEdit.onEditingFinished: console.log("test") + } +} diff --git a/tests/auto/qml/qqmlengine/qqmlengine.pro b/tests/auto/qml/qqmlengine/qqmlengine.pro index e7952d8e3a..d2eb92bfd5 100644 --- a/tests/auto/qml/qqmlengine/qqmlengine.pro +++ b/tests/auto/qml/qqmlengine/qqmlengine.pro @@ -7,3 +7,12 @@ include (../../shared/util.pri) SOURCES += tst_qqmlengine.cpp QT += core-private gui-private qml-private network testlib + +boot2qt: { + # GC corruption test is too heavy for qemu-arm + DEFINES += SKIP_GCCORRUPTION_TEST +} + +RESOURCES += \ + data/qrcurls.qml \ + data/qrcurls.js diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 07569efc72..f58ae38264 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -41,6 +41,7 @@ #include <QQmlNetworkAccessManagerFactory> #include <QQmlExpression> #include <QQmlIncubationController> +#include <QTemporaryDir> #include <private/qqmlengine_p.h> #include <QQmlAbstractUrlInterceptor> @@ -51,6 +52,7 @@ public: tst_qqmlengine() {} private slots: + void initTestCase() override; void rootContext(); void networkAccessManager(); void synchronousNetworkAccessManager(); @@ -73,6 +75,11 @@ private slots: void urlInterceptor(); void qmlContextProperties(); void testGCCorruption(); + void testGroupedPropertyRevisions(); + void componentFromEval(); + void qrcUrls(); + void cppSignalAndEval(); + void singletonInstance(); public slots: QObject *createAQObjectForOwnershipTest () @@ -80,8 +87,17 @@ public slots: static QObject *ptr = new QObject(); return ptr; } + +private: + QTemporaryDir m_tempDir; }; +void tst_qqmlengine::initTestCase() +{ + QVERIFY2(m_tempDir.isValid(), qPrintable(m_tempDir.errorString())); + QQmlDataTest::initTestCase(); +} + void tst_qqmlengine::rootContext() { QQmlEngine engine; @@ -95,7 +111,7 @@ void tst_qqmlengine::rootContext() class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { public: - NetworkAccessManagerFactory() : manager(0) {} + NetworkAccessManagerFactory() : manager(nullptr) {} QNetworkAccessManager *create(QObject *parent) { manager = new QNetworkAccessManager(parent); @@ -111,7 +127,7 @@ void tst_qqmlengine::networkAccessManager() // Test QQmlEngine created manager QPointer<QNetworkAccessManager> manager = engine->networkAccessManager(); - QVERIFY(manager != 0); + QVERIFY(manager != nullptr); delete engine; // Test factory created manager @@ -143,10 +159,10 @@ class ImmediateManager : public QNetworkAccessManager { Q_OBJECT public: - ImmediateManager(QObject *parent = 0) : QNetworkAccessManager(parent) { + ImmediateManager(QObject *parent = nullptr) : QNetworkAccessManager(parent) { } - QNetworkReply *createRequest(Operation, const QNetworkRequest & , QIODevice * outgoingData = 0) { + QNetworkReply *createRequest(Operation, const QNetworkRequest & , QIODevice * outgoingData = nullptr) { Q_UNUSED(outgoingData); return new ImmediateReply; } @@ -200,17 +216,17 @@ void tst_qqmlengine::contextForObject() QQmlEngine *engine = new QQmlEngine; // Test null-object - QVERIFY(!QQmlEngine::contextForObject(0)); + QVERIFY(!QQmlEngine::contextForObject(nullptr)); // Test an object with no context QObject object; QVERIFY(!QQmlEngine::contextForObject(&object)); // Test setting null-object - QQmlEngine::setContextForObject(0, engine->rootContext()); + QQmlEngine::setContextForObject(nullptr, engine->rootContext()); // Test setting null-context - QQmlEngine::setContextForObject(&object, 0); + QQmlEngine::setContextForObject(&object, nullptr); // Test setting context QQmlEngine::setContextForObject(&object, engine->rootContext()); @@ -224,7 +240,7 @@ void tst_qqmlengine::contextForObject() QCOMPARE(QQmlEngine::contextForObject(&object), engine->rootContext()); // Delete context - delete engine; engine = 0; + delete engine; engine = nullptr; QVERIFY(!QQmlEngine::contextForObject(&object)); } @@ -286,9 +302,12 @@ void tst_qqmlengine::clearComponentCache() { QQmlEngine engine; + const QString fileName = m_tempDir.filePath(QStringLiteral("temp.qml")); + const QUrl fileUrl = QUrl::fromLocalFile(fileName); + // Create original qml file { - QFile file("temp.qml"); + QFile file(fileName); QVERIFY(file.open(QIODevice::WriteOnly)); file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 10\n}\n"); file.close(); @@ -296,9 +315,9 @@ void tst_qqmlengine::clearComponentCache() // Test "test" property { - QQmlComponent component(&engine, "temp.qml"); + QQmlComponent component(&engine, fileUrl); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("test").toInt(), 10); delete obj; } @@ -311,7 +330,7 @@ void tst_qqmlengine::clearComponentCache() // Similar effects of lacking precision have been observed on some Linux systems. QThread::sleep(1); - QFile file("temp.qml"); + QFile file(fileName); QVERIFY(file.open(QIODevice::WriteOnly)); file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 11\n}\n"); file.close(); @@ -319,9 +338,9 @@ void tst_qqmlengine::clearComponentCache() // Test cache hit { - QQmlComponent component(&engine, "temp.qml"); + QQmlComponent component(&engine, fileUrl); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("test").toInt(), 10); delete obj; } @@ -331,12 +350,18 @@ void tst_qqmlengine::clearComponentCache() // Test cache refresh { - QQmlComponent component(&engine, "temp.qml"); + QQmlComponent component(&engine, fileUrl); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("test").toInt(), 11); delete obj; } + + // Regular Synchronous loading will leave us with an event posted + // to the gui thread and an extra refcount that will only be dropped after the + // event delivery. Call sendPostedEvents() to get rid of it so that + // the temporary directory can be removed. + QCoreApplication::sendPostedEvents(); } struct ComponentCacheFunctions : public QObject, public QQmlIncubationController @@ -350,7 +375,7 @@ public: Q_INVOKABLE void trim() { // Wait for any pending deletions to occur - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); // There might be JS function objects around that hold a last ref to the compilation unit that's @@ -402,7 +427,7 @@ void tst_qqmlengine::trimComponentCache() QQmlComponent component(&engine, testFileUrl(file)); QVERIFY(component.isReady()); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); } @@ -471,7 +496,7 @@ void tst_qqmlengine::repeatedCompilation() QQmlComponent component(&engine, testFileUrl("repeatedCompilation.qml")); QVERIFY(component.isReady()); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); } } @@ -515,11 +540,11 @@ void tst_qqmlengine::outputWarningsToStandardError() QObject *o = c.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); delete o; QCOMPARE(messageHandler.messages().count(), 1); - QCOMPARE(messageHandler.messages().at(0), QLatin1String("<Unknown File>:1:48: Unable to assign [undefined] to int")); + QCOMPARE(messageHandler.messages().at(0), QLatin1String("<Unknown File>:1:32: Unable to assign [undefined] to int")); messageHandler.clear(); engine.setOutputWarningsToStandardError(false); @@ -527,7 +552,7 @@ void tst_qqmlengine::outputWarningsToStandardError() o = c.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); delete o; QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); @@ -536,9 +561,9 @@ void tst_qqmlengine::outputWarningsToStandardError() void tst_qqmlengine::objectOwnership() { { - QCOMPARE(QQmlEngine::objectOwnership(0), QQmlEngine::CppOwnership); - QQmlEngine::setObjectOwnership(0, QQmlEngine::JavaScriptOwnership); - QCOMPARE(QQmlEngine::objectOwnership(0), QQmlEngine::CppOwnership); + QCOMPARE(QQmlEngine::objectOwnership(nullptr), QQmlEngine::CppOwnership); + QQmlEngine::setObjectOwnership(nullptr, QQmlEngine::JavaScriptOwnership); + QCOMPARE(QQmlEngine::objectOwnership(nullptr), QQmlEngine::CppOwnership); } { @@ -558,7 +583,7 @@ void tst_qqmlengine::objectOwnership() c.setData("import QtQuick 2.0; QtObject { property QtObject object: QtObject {} }", QUrl()); QObject *o = c.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QCOMPARE(QQmlEngine::objectOwnership(o), QQmlEngine::CppOwnership); @@ -578,7 +603,7 @@ void tst_qqmlengine::objectOwnership() c.setData("import QtQuick 2.0; Item { property int data: test.createAQObjectForOwnershipTest() ? 0 : 1 }", QUrl()); QVERIFY(c.isReady()); QObject *o = c.create(); - QVERIFY(o != 0); + QVERIFY(o != nullptr); } QTRY_VERIFY(spy.count()); } @@ -593,8 +618,8 @@ void tst_qqmlengine::objectOwnership() c.setData("import QtQuick 2.0; QtObject { property var object: { var i = test; test ? 0 : 1 } }", QUrl()); QVERIFY(c.isReady()); QObject *o = c.create(); - QVERIFY(o != 0); - engine.rootContext()->setContextProperty("test", 0); + QVERIFY(o != nullptr); + engine.rootContext()->setContextProperty("test", nullptr); } QTRY_VERIFY(spy.count()); } @@ -613,8 +638,8 @@ void tst_qqmlengine::multipleEngines() engine1.rootContext()->setContextProperty("object", &o); engine2.rootContext()->setContextProperty("object", &o); - QQmlExpression expr1(engine1.rootContext(), 0, QString("object.objectName")); - QQmlExpression expr2(engine2.rootContext(), 0, QString("object.objectName")); + QQmlExpression expr1(engine1.rootContext(), nullptr, QString("object.objectName")); + QQmlExpression expr2(engine2.rootContext(), nullptr, QString("object.objectName")); QCOMPARE(expr1.evaluate().toString(), QString("TestName")); QCOMPARE(expr2.evaluate().toString(), QString("TestName")); @@ -624,13 +649,13 @@ void tst_qqmlengine::multipleEngines() { QQmlEngine engine1; engine1.rootContext()->setContextProperty("object", &o); - QQmlExpression expr1(engine1.rootContext(), 0, QString("object.objectName")); + QQmlExpression expr1(engine1.rootContext(), nullptr, QString("object.objectName")); QCOMPARE(expr1.evaluate().toString(), QString("TestName")); } { QQmlEngine engine1; engine1.rootContext()->setContextProperty("object", &o); - QQmlExpression expr1(engine1.rootContext(), 0, QString("object.objectName")); + QQmlExpression expr1(engine1.rootContext(), nullptr, QString("object.objectName")); QCOMPARE(expr1.evaluate().toString(), QString("TestName")); } } @@ -721,6 +746,9 @@ public: if (url.path().endsWith("Test.2/qmldir"))//Special case return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir"); + // Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now + if (url.path().endsWith("intercepted/qmldir")) + return url; QString alteredPath = url.path(); int a = alteredPath.lastIndexOf('/'); @@ -839,6 +867,10 @@ void tst_qqmlengine::qmlContextProperties() void tst_qqmlengine::testGCCorruption() { +#ifdef SKIP_GCCORRUPTION_TEST + QSKIP("Test too heavy for qemu"); +#endif + QQmlEngine e; QQmlComponent c(&e, testFileUrl("testGCCorruption.qml")); @@ -846,6 +878,171 @@ void tst_qqmlengine::testGCCorruption() QVERIFY2(o, qPrintable(c.errorString())); } +void tst_qqmlengine::testGroupedPropertyRevisions() +{ + QQmlEngine e; + + QQmlComponent c(&e, testFileUrl("testGroupedPropertiesRevision.1.qml")); + QScopedPointer<QObject> object(c.create()); + QVERIFY2(object.data(), qPrintable(c.errorString())); + QQmlComponent c2(&e, testFileUrl("testGroupedPropertiesRevision.2.qml")); + QVERIFY(!c2.errorString().isEmpty()); +} + +void tst_qqmlengine::componentFromEval() +{ + QQmlEngine engine; + const QUrl testUrl = testFileUrl("EmptyComponent.qml"); + QJSValue result = engine.evaluate("Qt.createComponent(\"" + testUrl.toString() + "\");"); + QPointer<QQmlComponent> component(qobject_cast<QQmlComponent*>(result.toQObject())); + QVERIFY(!component.isNull()); + QScopedPointer<QObject> item(component->create()); + QVERIFY(!item.isNull()); +} + +void tst_qqmlengine::qrcUrls() +{ + QQmlEngine engine; + QQmlEnginePrivate *pEngine = QQmlEnginePrivate::get(&engine); + + { + QQmlRefPointer<QQmlTypeData> oneQml(pEngine->typeLoader.getType(QUrl("qrc:/qrcurls.qml"))); + QVERIFY(oneQml.data() != nullptr); + QQmlRefPointer<QQmlTypeData> twoQml(pEngine->typeLoader.getType(QUrl("qrc:///qrcurls.qml"))); + QVERIFY(twoQml.data() != nullptr); + QCOMPARE(oneQml.data(), twoQml.data()); + } + + { + QQmlRefPointer<QQmlTypeData> oneJS(pEngine->typeLoader.getType(QUrl("qrc:/qrcurls.js"))); + QVERIFY(oneJS.data() != nullptr); + QQmlRefPointer<QQmlTypeData> twoJS(pEngine->typeLoader.getType(QUrl("qrc:///qrcurls.js"))); + QVERIFY(twoJS.data() != nullptr); + QCOMPARE(oneJS.data(), twoJS.data()); + } +} + +class ObjectCaller : public QObject +{ + Q_OBJECT +signals: + void doubleReply(const double a); +}; + +void tst_qqmlengine::cppSignalAndEval() +{ + ObjectCaller objectCaller; + QQmlEngine engine; + engine.rootContext()->setContextProperty(QLatin1Literal("CallerCpp"), &objectCaller); + QQmlComponent c(&engine); + c.setData("import QtQuick 2.9\n" + "Item {\n" + " property var r: 0\n" + " Connections {\n" + " target: CallerCpp;\n" + " onDoubleReply: {\n" + " eval('var z = 1');\n" + " r = a;\n" + " }\n" + " }\n" + "}", + QUrl(QStringLiteral("qrc:/main.qml"))); + QScopedPointer<QObject> object(c.create()); + QVERIFY(!object.isNull()); + emit objectCaller.doubleReply(1.1234); + QCOMPARE(object->property("r"), 1.1234); +} + +class CppSingleton : public QObject { + Q_OBJECT +public: + CppSingleton() {} + + static QObject *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) + { + Q_UNUSED(qmlEngine); + Q_UNUSED(jsEngine); + return new CppSingleton(); + } +}; + +class JsSingleton : public QObject { + Q_OBJECT +public: + JsSingleton() {} + + static QJSValue create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) + { + Q_UNUSED(qmlEngine); + QJSValue value = jsEngine->newQObject(new JsSingleton()); + return value; + } +}; + +class SomeQObjectClass : public QObject { + Q_OBJECT +public: + SomeQObjectClass() : QObject(nullptr){} +}; + +void tst_qqmlengine::singletonInstance() +{ + QQmlEngine engine; + + int cppSingletonTypeId = qmlRegisterSingletonType<CppSingleton>("Test", 1, 0, "CppSingleton", &CppSingleton::create); + int jsValueSingletonTypeId = qmlRegisterSingletonType("Test", 1, 0, "JsSingleton", &JsSingleton::create); + + { + // Cpp QObject singleton type + QJSValue value = engine.singletonInstance<QJSValue>(cppSingletonTypeId); + QVERIFY(!value.isUndefined()); + QVERIFY(value.isQObject()); + QObject *instance = value.toQObject(); + QVERIFY(instance); + QCOMPARE(instance->metaObject()->className(), "CppSingleton"); + } + + { + // QJSValue QObject singleton type + QJSValue value = engine.singletonInstance<QJSValue>(jsValueSingletonTypeId); + QVERIFY(!value.isUndefined()); + QVERIFY(value.isQObject()); + QObject *instance = value.toQObject(); + QVERIFY(instance); + QCOMPARE(instance->metaObject()->className(), "JsSingleton"); + } + + { + // Invalid types + QJSValue value; + value = engine.singletonInstance<QJSValue>(-4711); + QVERIFY(value.isUndefined()); + value = engine.singletonInstance<QJSValue>(1701); + QVERIFY(value.isUndefined()); + } + + { + // Valid, but non-singleton type + int typeId = qmlRegisterType<CppSingleton>("Test", 1, 0, "NotASingleton"); + QJSValue value = engine.singletonInstance<QJSValue>(typeId); + QVERIFY(value.isUndefined()); + } + + { + // Cpp QObject singleton type + CppSingleton *instance = engine.singletonInstance<CppSingleton*>(cppSingletonTypeId); + QVERIFY(instance); + QCOMPARE(instance->metaObject()->className(), "CppSingleton"); + QCOMPARE(instance, engine.singletonInstance<QJSValue>(cppSingletonTypeId).toQObject()); + } + + { + // Wrong destination type + SomeQObjectClass * instance = engine.singletonInstance<SomeQObjectClass*>(cppSingletonTypeId); + QVERIFY(!instance); + } +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" diff --git a/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro b/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro index 5bcec9f5b4..90508609a8 100644 --- a/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro +++ b/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro @@ -6,4 +6,4 @@ include (../../shared/util.pri) SOURCES += tst_qqmlenginecleanup.cpp -QT += testlib qml +QT += testlib qml qml-private diff --git a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp index d0a8b6401f..b9cede6d13 100644 --- a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp +++ b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp @@ -31,6 +31,8 @@ #include <QtQml/qqml.h> #include <QtQml/QQmlEngine> #include <QtQml/QQmlComponent> +#include <private/qhashedstring_p.h> +#include <private/qqmlmetatype_p.h> //Separate test, because if engine cleanup attempts fail they can easily break unrelated tests class tst_qqmlenginecleanup : public QQmlDataTest @@ -44,41 +46,82 @@ private slots: void test_valueTypeProviderModule(); // QTBUG-43004 }; +// A wrapper around QQmlComponent to ensure the temporary reference counts +// on the type data as a result of the main thread <> loader thread communication +// are dropped. Regular Synchronous loading will leave us with an event posted +// to the gui thread and an extra refcount that will only be dropped after the +// event delivery. A plain sendPostedEvents() however is insufficient because +// we can't be sure that the event is posted after the constructor finished. +class CleanlyLoadingComponent : public QQmlComponent +{ +public: + CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url) + : QQmlComponent(engine, url, QQmlComponent::Asynchronous) + { waitForLoad(); } + CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName) + : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous) + { waitForLoad(); } + + void waitForLoad() + { + QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + } +}; + void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations() { //Test for preventing memory leaks is in tests/manual/qmltypememory QQmlEngine* engine; - QQmlComponent* component; + CleanlyLoadingComponent* component; QUrl testFile = testFileUrl("types.qml"); + const auto qmlTypeForTestType = []() { + return QQmlMetaType::qmlType(QStringLiteral("TestTypeCpp"), QStringLiteral("Test"), 2, 0); + }; + + QVERIFY(!qmlTypeForTestType().isValid()); qmlRegisterType<QObject>("Test", 2, 0, "TestTypeCpp"); + QVERIFY(qmlTypeForTestType().isValid()); + engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isReady()); - delete engine; delete component; - qmlClearTypeRegistrations(); + delete engine; + + { + auto cppType = qmlTypeForTestType(); + + qmlClearTypeRegistrations(); + QVERIFY(!qmlTypeForTestType().isValid()); + + // cppType should hold the last ref, qmlClearTypeRegistration should have wiped + // all internal references. + QCOMPARE(QQmlType::refCount(cppType.priv()), 1); + } //2nd run verifies that types can reload after a qmlClearTypeRegistrations qmlRegisterType<QObject>("Test", 2, 0, "TestTypeCpp"); + QVERIFY(qmlTypeForTestType().isValid()); engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isReady()); - delete engine; delete component; + delete engine; qmlClearTypeRegistrations(); + QVERIFY(!qmlTypeForTestType().isValid()); //3nd run verifies that TestTypeCpp is no longer registered engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isError()); QCOMPARE(component->errorString(), testFile.toString() +":33 module \"Test\" is not installed\n"); - delete engine; delete component; + delete engine; } static void cleanState(QQmlEngine **e) @@ -86,7 +129,7 @@ static void cleanState(QQmlEngine **e) delete *e; qmlClearTypeRegistrations(); *e = new QQmlEngine; - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } @@ -96,7 +139,7 @@ void tst_qqmlenginecleanup::test_valueTypeProviderModule() // provider can be reinitialized after multiple calls to // qmlClearTypeRegistrations() without causing cycles in the // value type provider list. - QQmlEngine *e = 0; + QQmlEngine *e = nullptr; QUrl testFile1 = testFileUrl("testFile1.qml"); QUrl testFile2 = testFileUrl("testFile2.qml"); bool noCycles = false; @@ -106,7 +149,7 @@ void tst_qqmlenginecleanup::test_valueTypeProviderModule() c.loadUrl(i % 2 == 0 ? testFile1 : testFile2); // this will hang if cycles exist. } delete e; - e = 0; + e = nullptr; noCycles = true; QVERIFY(noCycles); diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp index aa201f2644..1decc04ad2 100644 --- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp +++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp @@ -52,7 +52,7 @@ class TestObject : public QObject Q_PROPERTY(QQmlScriptString scriptString READ scriptString WRITE setScriptString) Q_PROPERTY(QQmlScriptString scriptStringError READ scriptStringError WRITE setScriptStringError) public: - TestObject(QObject *parent = 0) : QObject(parent) {} + TestObject(QObject *parent = nullptr) : QObject(parent) {} QQmlScriptString scriptString() const { return m_scriptString; } void setScriptString(QQmlScriptString scriptString) { m_scriptString = scriptString; } @@ -74,7 +74,7 @@ void tst_qqmlexpression::scriptString() QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("scriptString.qml")); TestObject *testObj = qobject_cast<TestObject*>(c.create()); - QVERIFY(testObj != 0); + QVERIFY(testObj != nullptr); QQmlScriptString script = testObj->scriptString(); QVERIFY(!script.isEmpty()); @@ -100,15 +100,19 @@ void tst_qqmlexpression::scriptString() void tst_qqmlexpression::syntaxError() { QQmlEngine engine; - QQmlExpression expression(engine.rootContext(), 0, "asd asd"); - QVariant v = expression.evaluate(); + QQmlExpression expression(engine.rootContext(), nullptr, "asd asd"); + bool isUndefined = false; + QVariant v = expression.evaluate(&isUndefined); QCOMPARE(v, QVariant()); + QVERIFY(expression.hasError()); + QCOMPARE(expression.error().description(), "SyntaxError: Expected token `;'"); + QVERIFY(isUndefined); } void tst_qqmlexpression::exception() { QQmlEngine engine; - QQmlExpression expression(engine.rootContext(), 0, "abc=123"); + QQmlExpression expression(engine.rootContext(), nullptr, "abc=123"); QVariant v = expression.evaluate(); QCOMPARE(v, QVariant()); QVERIFY(expression.hasError()); diff --git a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp index 268010ead8..341a49bf09 100644 --- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp +++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp @@ -105,11 +105,14 @@ void tst_qqmlextensionplugin::iidCheck() QPluginLoader loader(filePath); QVERIFY2(loader.load(), qPrintable(loader.errorString())); - QVERIFY(loader.instance() != Q_NULLPTR); + QVERIFY(loader.instance() != nullptr); if (qobject_cast<QQmlExtensionPlugin *>(loader.instance())) { QString iid = loader.metaData().value(QStringLiteral("IID")).toString(); - QCOMPARE(iid, QLatin1String(QQmlExtensionInterface_iid)); + if (iid == QLatin1String(QQmlExtensionInterface_iid_old)) + qWarning() << "Old extension plugin found. Update the IID" << loader.metaData(); + else + QCOMPARE(iid, QLatin1String(QQmlExtensionInterface_iid)); } } diff --git a/tests/auto/qml/qqmlfile/qqmlfile.pro b/tests/auto/qml/qqmlfile/qqmlfile.pro new file mode 100644 index 0000000000..ab66792445 --- /dev/null +++ b/tests/auto/qml/qqmlfile/qqmlfile.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qqmlfile +SOURCES += tst_qqmlfile.cpp +macos:CONFIG -= app_bundle +QT += qml testlib diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp index fe01507412..a1c8daddcf 100644 --- a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp +++ b/tests/auto/qml/qqmlfile/tst_qqmlfile.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -25,32 +25,34 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <QStringList> -#include <QtQml/qqmlextensionplugin.h> -#include <QtQml/qqml.h> -#include <QDebug> -class MyPluginType : public QObject +#include <QtCore> +#include <QtTest> +#include <QQmlFile> + +class tst_qqmlfile : public QObject { Q_OBJECT + public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + tst_qqmlfile() {} + +private Q_SLOTS: + void urlToLocalFileOrQrcOverloads(); }; -class MyPlugin : public QQmlExtensionPlugin +void tst_qqmlfile::urlToLocalFileOrQrcOverloads() { - Q_OBJECT - Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + const QString urlString = QStringLiteral("qrc:///example.qml"); + const QUrl url(urlString); + const QString pathForUrlString = QQmlFile::urlToLocalFileOrQrc(urlString); + const QString pathForUrl = QQmlFile::urlToLocalFileOrQrc(url); -public: - MyPlugin() {} + QCOMPARE(pathForUrlString, pathForUrl); + QCOMPARE(pathForUrlString, QStringLiteral(":/example.qml")); +} - void registerTypes(const char *uri) - { - Q_ASSERT(QLatin1String(uri) == "org.qtproject.InvalidStrictModule"); - qmlRegisterType<MyPluginType>("org.qtproject.SomeOtherModule", 1, 0, "MyPluginType"); - } -}; +QTEST_GUILESS_MAIN(tst_qqmlfile) -#include "plugin.moc" +#include "tst_qqmlfile.moc" diff --git a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp index 648e4490ee..2d618170d4 100644 --- a/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp +++ b/tests/auto/qml/qqmlfileselector/tst_qqmlfileselector.cpp @@ -33,6 +33,7 @@ #include <QQmlApplicationEngine> #include <QFileSelector> #include <QQmlContext> +#include <QLoggingCategory> #include <qqmlinfo.h> #include "../../shared/util.h" @@ -44,6 +45,7 @@ public: private slots: void basicTest(); + void basicTestCached(); void applicationEngineTest(); }; @@ -56,22 +58,41 @@ void tst_qqmlfileselector::basicTest() QQmlComponent component(&engine, testFileUrl("basicTest.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toString(), QString("selected")); delete object; } +void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) +{ + if (type == QtDebugMsg + && QByteArray(context.category) == QByteArray("qt.qml.diskcache") + && message.contains("QML source file has moved to a different location.")) { + QFAIL(message.toUtf8()); + } +} + +void tst_qqmlfileselector::basicTestCached() +{ + basicTest(); // Seed the cache, in case basicTestCached() is run on its own + QtMessageHandler defaultHandler = qInstallMessageHandler(&messageHandler); + QLoggingCategory::setFilterRules("qt.qml.diskcache.debug=true"); + basicTest(); // Run again and check that the file is in the cache now + QLoggingCategory::setFilterRules(QString()); + qInstallMessageHandler(defaultHandler); +} + void tst_qqmlfileselector::applicationEngineTest() { QQmlApplicationEngine engine; QQmlFileSelector* selector = QQmlFileSelector::get(&engine); - QVERIFY(selector != 0); + QVERIFY(selector != nullptr); selector->setExtraSelectors(QStringList() << "basic"); QQmlComponent component(&engine, testFileUrl("basicTest.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toString(), QString("selected")); delete object; diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp index 68739886c4..70aaa9678e 100644 --- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp +++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp @@ -54,7 +54,7 @@ void tst_QQmlImport::cleanup() void tst_QQmlImport::testDesignerSupported() { QQuickView *window = new QQuickView(); - window->engine()->addImportPath(QT_TESTCASE_BUILDDIR); + window->engine()->addImportPath(directory()); window->setSource(testFileUrl("testfile_supported.qml")); QVERIFY(window->errors().isEmpty()); @@ -68,7 +68,7 @@ void tst_QQmlImport::testDesignerSupported() delete window; window = new QQuickView(); - window->engine()->addImportPath(QT_TESTCASE_BUILDDIR); + window->engine()->addImportPath(directory()); window->engine()->clearComponentCache(); window->setSource(testFileUrl("testfile_supported.qml")); @@ -91,7 +91,7 @@ void tst_QQmlImport::uiFormatLoading() int size = 0; QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("TestForm.ui.qml")); - test->addImportPath(QT_TESTCASE_BUILDDIR); + test->addImportPath(directory()); QCOMPARE(test->rootObjects().size(), ++size); QVERIFY(test->rootObjects()[size -1]); QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); diff --git a/tests/auto/qml/qqmlincubator/data/garbageCollection.qml b/tests/auto/qml/qqmlincubator/data/garbageCollection.qml new file mode 100644 index 0000000000..6866a02a00 --- /dev/null +++ b/tests/auto/qml/qqmlincubator/data/garbageCollection.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property var incubator + + function getAndClearIncubator() { + var result = incubator + incubator = null + return result + } + + Component.onCompleted: { + var c = Qt.createComponent("statusChanged.qml"); // use existing simple type for convenience + var incubator = c.incubateObject(root); + incubator.onStatusChanged = function(status) { if (status === 1) root.incubator = incubator } + } +} diff --git a/tests/auto/qml/qqmlincubator/data/objectDeleted.errors.txt b/tests/auto/qml/qqmlincubator/data/objectDeleted.errors.txt index eeda289d35..9f972e3e06 100644 --- a/tests/auto/qml/qqmlincubator/data/objectDeleted.errors.txt +++ b/tests/auto/qml/qqmlincubator/data/objectDeleted.errors.txt @@ -1 +1 @@ --1:-1:Object destroyed during incubation +-1:-1:Object or context destroyed during incubation diff --git a/tests/auto/qml/qqmlincubator/testtypes.cpp b/tests/auto/qml/qqmlincubator/testtypes.cpp index 3fcd3ba299..06d5904bbd 100644 --- a/tests/auto/qml/qqmlincubator/testtypes.cpp +++ b/tests/auto/qml/qqmlincubator/testtypes.cpp @@ -28,7 +28,7 @@ #include "testtypes.h" #include <QtQml/qqml.h> -SelfRegisteringType *SelfRegisteringType::m_me = 0; +SelfRegisteringType *SelfRegisteringType::m_me = nullptr; SelfRegisteringType::SelfRegisteringType() : m_v(0) { @@ -42,13 +42,13 @@ SelfRegisteringType *SelfRegisteringType::me() void SelfRegisteringType::clearMe() { - m_me = 0; + m_me = nullptr; } -SelfRegisteringOuterType *SelfRegisteringOuterType::m_me = 0; +SelfRegisteringOuterType *SelfRegisteringOuterType::m_me = nullptr; bool SelfRegisteringOuterType::beenDeleted = false; SelfRegisteringOuterType::SelfRegisteringOuterType() -: m_v(0) +: m_v(nullptr) { m_me = this; beenDeleted = false; @@ -64,7 +64,7 @@ SelfRegisteringOuterType *SelfRegisteringOuterType::me() return m_me; } -CompletionRegisteringType *CompletionRegisteringType::m_me = 0; +CompletionRegisteringType *CompletionRegisteringType::m_me = nullptr; CompletionRegisteringType::CompletionRegisteringType() { } @@ -85,11 +85,11 @@ CompletionRegisteringType *CompletionRegisteringType::me() void CompletionRegisteringType::clearMe() { - m_me = 0; + m_me = nullptr; } -CallbackRegisteringType::callback CallbackRegisteringType::m_callback = 0; -void *CallbackRegisteringType::m_data = 0; +CallbackRegisteringType::callback CallbackRegisteringType::m_callback = nullptr; +void *CallbackRegisteringType::m_data = nullptr; CallbackRegisteringType::CallbackRegisteringType() : m_v(0) { @@ -97,8 +97,8 @@ CallbackRegisteringType::CallbackRegisteringType() void CallbackRegisteringType::clearCallback() { - m_callback = 0; - m_data = 0; + m_callback = nullptr; + m_data = nullptr; } void CallbackRegisteringType::registerCallback(callback c, void *d) @@ -107,8 +107,8 @@ void CallbackRegisteringType::registerCallback(callback c, void *d) m_data = d; } -CompletionCallbackType::callback CompletionCallbackType::m_callback = 0; -void *CompletionCallbackType::m_data = 0; +CompletionCallbackType::callback CompletionCallbackType::m_callback = nullptr; +void *CompletionCallbackType::m_data = nullptr; CompletionCallbackType::CompletionCallbackType() { } @@ -124,8 +124,8 @@ void CompletionCallbackType::componentComplete() void CompletionCallbackType::clearCallback() { - m_callback = 0; - m_data = 0; + m_callback = nullptr; + m_data = nullptr; } void CompletionCallbackType::registerCallback(callback c, void *d) diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp index 863b6aaa29..8e25079703 100644 --- a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp +++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp @@ -39,6 +39,7 @@ #include <QQmlComponent> #include <QQmlIncubator> #include "../../shared/util.h" +#include <private/qjsvalue_p.h> #include <private/qqmlincubator_p.h> #include <private/qqmlobjectcreator_p.h> @@ -68,6 +69,7 @@ private slots: void chainedAsynchronousClear(); void selfDelete(); void contextDelete(); + void garbageCollection(); private: QQmlIncubationController controller; @@ -145,15 +147,15 @@ void tst_qqmlincubator::objectDeleted() QCOMPARE(incubator.status(), QQmlIncubator::Loading); QVERIFY(!SelfRegisteringType::me()); - while (SelfRegisteringOuterType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringOuterType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringOuterType::me() != 0); + QVERIFY(SelfRegisteringOuterType::me() != nullptr); QVERIFY(incubator.isLoading()); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } @@ -201,13 +203,13 @@ void tst_qqmlincubator::clear() QQmlIncubator incubator; component.create(incubator); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } QVERIFY(incubator.isLoading()); - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QPointer<SelfRegisteringType> srt = SelfRegisteringType::me(); incubator.clear(); @@ -226,7 +228,7 @@ void tst_qqmlincubator::clear() } QVERIFY(incubator.isReady()); - QVERIFY(incubator.object() != 0); + QVERIFY(incubator.object() != nullptr); QPointer<QObject> obj = incubator.object(); incubator.clear(); @@ -299,7 +301,7 @@ void tst_qqmlincubator::forceCompletion() incubator.forceCompletion(); QVERIFY(incubator.isReady()); - QVERIFY(incubator.object() != 0); + QVERIFY(incubator.object() != nullptr); QCOMPARE(incubator.object()->property("testValue").toInt(), 3499); delete incubator.object(); @@ -314,18 +316,18 @@ void tst_qqmlincubator::forceCompletion() component.create(incubator); QVERIFY(incubator.isLoading()); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator.isLoading()); incubator.forceCompletion(); QVERIFY(incubator.isReady()); - QVERIFY(incubator.object() != 0); + QVERIFY(incubator.object() != nullptr); QCOMPARE(incubator.object()->property("testValue").toInt(), 3499); delete incubator.object(); @@ -341,13 +343,13 @@ void tst_qqmlincubator::forceCompletion() incubator.forceCompletion(); QVERIFY(incubator.isReady()); - QVERIFY(incubator.object() != 0); + QVERIFY(incubator.object() != nullptr); QCOMPARE(incubator.object()->property("testValue").toInt(), 3499); incubator.forceCompletion(); QVERIFY(incubator.isReady()); - QVERIFY(incubator.object() != 0); + QVERIFY(incubator.object() != nullptr); QCOMPARE(incubator.object()->property("testValue").toInt(), 3499); delete incubator.object(); @@ -410,19 +412,19 @@ void tst_qqmlincubator::clearDuringCompletion() QCOMPARE(incubator.status(), QQmlIncubator::Loading); QVERIFY(!CompletionRegisteringType::me()); - while (CompletionRegisteringType::me() == 0 && incubator.isLoading()) { + while (CompletionRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(CompletionRegisteringType::me() != 0); - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(CompletionRegisteringType::me() != nullptr); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator.isLoading()); QPointer<QObject> srt = SelfRegisteringType::me(); incubator.clear(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(incubator.isNull()); QVERIFY(srt.isNull()); @@ -436,7 +438,7 @@ void tst_qqmlincubator::objectDeletionAfterInit() struct MyIncubator : public QQmlIncubator { MyIncubator(QQmlIncubator::IncubationMode mode) - : QQmlIncubator(mode), obj(0) {} + : QQmlIncubator(mode), obj(nullptr) {} virtual void setInitialState(QObject *o) { obj = o; @@ -455,12 +457,12 @@ void tst_qqmlincubator::objectDeletionAfterInit() } QVERIFY(incubator.isLoading()); - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); delete incubator.obj; incubator.clear(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QVERIFY(incubator.isNull()); } @@ -592,11 +594,11 @@ void tst_qqmlincubator::asynchronousIfNested() QVERIFY(component.isReady()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 10); QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); - component.create(incubator, 0, qmlContext(object)); + component.create(incubator, nullptr, qmlContext(object)); QVERIFY(incubator.isReady()); QVERIFY(incubator.object()); @@ -618,16 +620,16 @@ void tst_qqmlincubator::asynchronousIfNested() QVERIFY(incubator.isLoading()); QVERIFY(!SelfRegisteringType::me()); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator.isLoading()); QQmlIncubator nested(QQmlIncubator::AsynchronousIfNested); - component.create(nested, 0, qmlContext(SelfRegisteringType::me())); + component.create(nested, nullptr, qmlContext(SelfRegisteringType::me())); QVERIFY(nested.isLoading()); while (nested.isLoading()) { @@ -669,7 +671,7 @@ void tst_qqmlincubator::asynchronousIfNested() if (!c.isReady()) return; QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); - c.create(incubator, 0, qmlContext(o)); + c.create(incubator, nullptr, qmlContext(o)); if (!incubator.isReady()) return; @@ -738,12 +740,12 @@ void tst_qqmlincubator::chainedAsynchronousIfNested() QVERIFY(incubator.isLoading()); QVERIFY(!SelfRegisteringType::me()); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator.isLoading()); struct MyIncubator : public QQmlIncubator { @@ -753,7 +755,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNested() protected: virtual void statusChanged(Status s) { if (s == Ready && next) - component->create(*next, 0, ctxt); + component->create(*next, nullptr, ctxt); } private: @@ -762,10 +764,10 @@ void tst_qqmlincubator::chainedAsynchronousIfNested() QQmlContext *ctxt; }; - MyIncubator incubator2(0, &component, 0); + MyIncubator incubator2(nullptr, &component, nullptr); MyIncubator incubator1(&incubator2, &component, qmlContext(SelfRegisteringType::me())); - component.create(incubator1, 0, qmlContext(SelfRegisteringType::me())); + component.create(incubator1, nullptr, qmlContext(SelfRegisteringType::me())); QVERIFY(incubator.isLoading()); QVERIFY(incubator1.isLoading()); @@ -824,7 +826,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() protected: virtual void statusChanged(Status s) { if (s == Ready && next) { - component->create(*next, 0, ctxt); + component->create(*next, nullptr, ctxt); } } @@ -842,7 +844,7 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QQmlContext *ctxt; static void callback(CompletionCallbackType *, void *data) { CallbackData *d = (CallbackData *)data; - d->component->create(*d->incubator, 0, d->ctxt); + d->component->create(*d->incubator, nullptr, d->ctxt); } }; @@ -852,15 +854,15 @@ void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() QVERIFY(incubator.isLoading()); QVERIFY(!SelfRegisteringType::me()); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator.isLoading()); - MyIncubator incubator3(0, &c1, qmlContext(SelfRegisteringType::me())); + MyIncubator incubator3(nullptr, &c1, qmlContext(SelfRegisteringType::me())); MyIncubator incubator2(&incubator3, &c1, qmlContext(SelfRegisteringType::me())); MyIncubator incubator1(&incubator2, &c1, qmlContext(SelfRegisteringType::me())); @@ -952,7 +954,7 @@ void tst_qqmlincubator::chainedAsynchronousClear() protected: virtual void statusChanged(Status s) { if (s == Ready && next) { - component->create(*next, 0, ctxt); + component->create(*next, nullptr, ctxt); } } @@ -970,7 +972,7 @@ void tst_qqmlincubator::chainedAsynchronousClear() QQmlContext *ctxt; static void callback(CompletionCallbackType *, void *data) { CallbackData *d = (CallbackData *)data; - d->component->create(*d->incubator, 0, d->ctxt); + d->component->create(*d->incubator, nullptr, d->ctxt); } }; @@ -980,15 +982,15 @@ void tst_qqmlincubator::chainedAsynchronousClear() QVERIFY(incubator.isLoading()); QVERIFY(!SelfRegisteringType::me()); - while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator.isLoading()); - MyIncubator incubator3(0, &c1, qmlContext(SelfRegisteringType::me())); + MyIncubator incubator3(nullptr, &c1, qmlContext(SelfRegisteringType::me())); MyIncubator incubator2(&incubator3, &c1, qmlContext(SelfRegisteringType::me())); MyIncubator incubator1(&incubator2, &c1, qmlContext(SelfRegisteringType::me())); @@ -1103,12 +1105,12 @@ void tst_qqmlincubator::selfDelete() QCOMPARE(incubator->QQmlIncubator::status(), QQmlIncubator::Loading); QVERIFY(!SelfRegisteringType::me()); - while (SelfRegisteringType::me() == 0 && incubator->isLoading()) { + while (SelfRegisteringType::me() == nullptr && incubator->isLoading()) { bool b = false; controller.incubateWhile(&b); } - QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(SelfRegisteringType::me() != nullptr); QVERIFY(incubator->isLoading()); // We have to cheat and manually remove it from the creator->allCreatedObjects @@ -1144,6 +1146,34 @@ void tst_qqmlincubator::contextDelete() } } +// QTBUG-53111 +void tst_qqmlincubator::garbageCollection() +{ + QQmlComponent component(&engine, testFileUrl("garbageCollection.qml")); + QScopedPointer<QObject> obj(component.create()); + + engine.collectGarbage(); + + bool b = true; + controller.incubateWhile(&b); + + // verify incubation completed (the incubator was not prematurely collected) + QVariant incubatorVariant; + QMetaObject::invokeMethod(obj.data(), "getAndClearIncubator", Q_RETURN_ARG(QVariant, incubatorVariant)); + QJSValue strongRef = incubatorVariant.value<QJSValue>(); + QVERIFY(!strongRef.isNull() && !strongRef.isUndefined()); + + // turn the last strong reference to the incubator into a weak one and collect + QV4::WeakValue weakIncubatorRef; + weakIncubatorRef.set(QQmlEnginePrivate::getV4Engine(&engine), *QJSValuePrivate::getValue(&strongRef)); + strongRef = QJSValue(); + incubatorVariant.clear(); + + // verify incubator is correctly collected now that incubation is complete and all references are gone + engine.collectGarbage(); + QVERIFY(weakIncubatorRef.isNullOrUndefined()); +} + QTEST_MAIN(tst_qqmlincubator) #include "tst_qqmlincubator.moc" diff --git a/tests/auto/qml/qqmlinfo/data/Component.qml b/tests/auto/qml/qqmlinfo/data/Component.qml new file mode 100644 index 0000000000..fefbbfae76 --- /dev/null +++ b/tests/auto/qml/qqmlinfo/data/Component.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + property Component delegate: Component { + QtObject { + } + } +} diff --git a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp index 3f6c200027..5ff72de0a0 100644 --- a/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp +++ b/tests/auto/qml/qqmlinfo/tst_qqmlinfo.cpp @@ -50,6 +50,7 @@ private slots: void types(); void chaining(); void messageTypes(); + void component(); private: QQmlEngine engine; @@ -60,14 +61,14 @@ void tst_qqmlinfo::qmlObject() QQmlComponent component(&engine, testFileUrl("qmlObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString message = component.url().toString() + ":3:1: QML QtObject: Test Message"; QTest::ignoreMessage(QtInfoMsg, qPrintable(message)); qmlInfo(object) << "Test Message"; QObject *nested = qvariant_cast<QObject *>(object->property("nested")); - QVERIFY(nested != 0); + QVERIFY(nested != nullptr); message = component.url().toString() + ":6:13: QML QtObject: Second Test Message"; QTest::ignoreMessage(QtInfoMsg, qPrintable(message)); @@ -79,12 +80,12 @@ void tst_qqmlinfo::nestedQmlObject() QQmlComponent component(&engine, testFileUrl("nestedQmlObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QObject *nested = qvariant_cast<QObject *>(object->property("nested")); - QVERIFY(nested != 0); + QVERIFY(nested != nullptr); QObject *nested2 = qvariant_cast<QObject *>(object->property("nested2")); - QVERIFY(nested2 != 0); + QVERIFY(nested2 != nullptr); QString message = component.url().toString() + ":5:13: QML NestedObject: Outer Object"; QTest::ignoreMessage(QtInfoMsg, qPrintable(message)); @@ -100,12 +101,12 @@ void tst_qqmlinfo::nestedComponent() QQmlComponent component(&engine, testFileUrl("NestedComponent.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QObject *nested = qvariant_cast<QObject *>(object->property("nested")); - QVERIFY(nested != 0); + QVERIFY(nested != nullptr); QObject *nested2 = qvariant_cast<QObject *>(object->property("nested2")); - QVERIFY(nested2 != 0); + QVERIFY(nested2 != nullptr); QString message = component.url().toString() + ":10:9: QML NestedObject: Complex Object"; QTest::ignoreMessage(QtInfoMsg, qPrintable(message)); @@ -130,7 +131,7 @@ void tst_qqmlinfo::nonQmlObject() void tst_qqmlinfo::nullObject() { QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: Null Object Test Message"); - qmlInfo(0) << "Null Object Test Message"; + qmlInfo(nullptr) << "Null Object Test Message"; } void tst_qqmlinfo::nonQmlContextedObject() @@ -145,44 +146,44 @@ void tst_qqmlinfo::nonQmlContextedObject() void tst_qqmlinfo::types() { QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: false"); - qmlInfo(0) << false; + qmlInfo(nullptr) << false; QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: 1.1"); - qmlInfo(0) << 1.1; + qmlInfo(nullptr) << 1.1; QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: 1.2"); - qmlInfo(0) << 1.2f; + qmlInfo(nullptr) << 1.2f; QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: 15"); - qmlInfo(0) << 15; + qmlInfo(nullptr) << 15; QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: 'b'"); - qmlInfo(0) << QChar('b'); + qmlInfo(nullptr) << QChar('b'); QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: \"Qt\""); - qmlInfo(0) << QByteArray("Qt"); + qmlInfo(nullptr) << QByteArray("Qt"); QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: true"); - qmlInfo(0) << bool(true); + qmlInfo(nullptr) << bool(true); //### do we actually want QUrl to show up in the output? //### why the extra space at the end? QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: QUrl(\"http://www.qt-project.org\") "); - qmlInfo(0) << QUrl("http://www.qt-project.org"); + qmlInfo(nullptr) << QUrl("http://www.qt-project.org"); //### should this be quoted? QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: hello"); - qmlInfo(0) << QLatin1String("hello"); + qmlInfo(nullptr) << QLatin1String("hello"); //### should this be quoted? QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: World"); QString str("Hello World"); QStringRef ref(&str, 6, 5); - qmlInfo(0) << ref; + qmlInfo(nullptr) << ref; //### should this be quoted? QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: Quick"); - qmlInfo(0) << QString ("Quick"); + qmlInfo(nullptr) << QString ("Quick"); } void tst_qqmlinfo::chaining() @@ -190,7 +191,7 @@ void tst_qqmlinfo::chaining() QString str("Hello World"); QStringRef ref(&str, 6, 5); QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: false 1.1 1.2 15 hello 'b' World \"Qt\" true Quick QUrl(\"http://www.qt-project.org\") "); - qmlInfo(0) << false << ' ' + qmlInfo(nullptr) << false << ' ' << 1.1 << ' ' << 1.2f << ' ' << 15 << ' ' @@ -207,13 +208,26 @@ void tst_qqmlinfo::chaining() void tst_qqmlinfo::messageTypes() { QTest::ignoreMessage(QtDebugMsg, "<Unknown File>: debug"); - qmlDebug(0) << QLatin1String("debug"); + qmlDebug(nullptr) << QLatin1String("debug"); QTest::ignoreMessage(QtInfoMsg, "<Unknown File>: info"); - qmlInfo(0) << QLatin1String("info"); + qmlInfo(nullptr) << QLatin1String("info"); QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: warning"); - qmlWarning(0) << QLatin1String("warning"); + qmlWarning(nullptr) << QLatin1String("warning"); +} + +void tst_qqmlinfo::component() +{ + QQmlComponent component(&engine, testFileUrl("Component.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QQmlComponent *delegate = qobject_cast<QQmlComponent*>(object->property("delegate").value<QObject*>()); + QVERIFY(delegate); + + QString message = component.url().toString() + ":4:34: QML Component: Delegate error"; + QTest::ignoreMessage(QtInfoMsg, qPrintable(message)); + qmlInfo(delegate) << "Delegate error"; } QTEST_MAIN(tst_qqmlinfo) diff --git a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro index 719fd6c350..542ec44736 100644 --- a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro +++ b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro @@ -2,7 +2,6 @@ CONFIG += testcase TARGET = tst_qqmlinstantiator macx:CONFIG -= app_bundle -INCLUDEPATH += ../../shared/ SOURCES += tst_qqmlinstantiator.cpp HEADERS += stringmodel.h diff --git a/tests/auto/qml/qqmlinstantiator/stringmodel.h b/tests/auto/qml/qqmlinstantiator/stringmodel.h index 0bd4ada55e..6ae3cbb6ee 100644 --- a/tests/auto/qml/qqmlinstantiator/stringmodel.h +++ b/tests/auto/qml/qqmlinstantiator/stringmodel.h @@ -60,27 +60,27 @@ public: endInsertRows(); } - int rowCount(const QModelIndex &) const + int rowCount(const QModelIndex &) const override { return items.count(); } - virtual QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE + QHash<int, QByteArray> roleNames() const override { return roles; } - virtual int columnCount(const QModelIndex &) const + int columnCount(const QModelIndex &) const override { return 1; } - virtual bool hasChildren(const QModelIndex &) const Q_DECL_OVERRIDE + bool hasChildren(const QModelIndex &) const override { return rowCount(QModelIndex()) > 0; } - virtual QModelIndex index(int row, int column, const QModelIndex &parent) const + QModelIndex index(int row, int column, const QModelIndex &parent) const override { Q_UNUSED(column); if (row>=0 && row<rowCount(parent)) @@ -89,12 +89,12 @@ public: return QModelIndex(); } - virtual QModelIndex parent(const QModelIndex &) const + QModelIndex parent(const QModelIndex &) const override { return QModelIndex(); } - QVariant data (const QModelIndex & index, int role) const + QVariant data (const QModelIndex & index, int role) const override { int row = index.row(); if ((row<0) || (row>=items.count())) diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp index 90b719d000..a66f13e6bb 100644 --- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp +++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp @@ -59,7 +59,7 @@ void tst_qqmlinstantiator::createNone() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("createNone.qml")); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create()); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QCOMPARE(instantiator->isActive(), true); QCOMPARE(instantiator->count(), 0); QCOMPARE(instantiator->property("success").toBool(), true); @@ -71,7 +71,7 @@ void tst_qqmlinstantiator::createSingle() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("createSingle.qml")); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create()); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QCOMPARE(instantiator->isActive(), true); QCOMPARE(instantiator->count(), 1); QVERIFY(instantiator->delegate()->isReady()); @@ -88,7 +88,7 @@ void tst_qqmlinstantiator::createMultiple() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("createMultiple.qml")); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create()); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QCOMPARE(instantiator->isActive(), true); QCOMPARE(instantiator->count(), 10); @@ -106,7 +106,7 @@ void tst_qqmlinstantiator::stringModel() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("stringModel.qml")); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create()); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QCOMPARE(instantiator->isActive(), true); QCOMPARE(instantiator->count(), 4); @@ -123,7 +123,7 @@ void tst_qqmlinstantiator::activeProperty() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("inactive.qml")); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create()); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QSignalSpy activeSpy(instantiator, SIGNAL(activeChanged())); QSignalSpy countSpy(instantiator, SIGNAL(countChanged())); QSignalSpy objectSpy(instantiator, SIGNAL(objectChanged())); @@ -158,7 +158,7 @@ void tst_qqmlinstantiator::intModelChange() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("createMultiple.qml")); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create()); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QSignalSpy activeSpy(instantiator, SIGNAL(activeChanged())); QSignalSpy countSpy(instantiator, SIGNAL(countChanged())); QSignalSpy objectSpy(instantiator, SIGNAL(objectChanged())); @@ -194,11 +194,11 @@ void tst_qqmlinstantiator::createAndRemove() StringModel *model = new StringModel("model1"); engine.rootContext()->setContextProperty("model1", model); QObject *rootObject = component.create(); - QVERIFY(rootObject != 0); + QVERIFY(rootObject != nullptr); QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(rootObject->findChild<QObject*>("instantiator1")); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); model->drop(1); QVector<QString> names; names << "Beta" << "Gamma" << "Delta"; @@ -234,7 +234,7 @@ void tst_qqmlinstantiator::asynchronous() QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator *>(incubator.object()); while (incubationController.incubatingObjectCount() > 0) incubationController.incubateFor(10); - QVERIFY(instantiator != 0); + QVERIFY(instantiator != nullptr); QCOMPARE(instantiator->isActive(), true); QCOMPARE(instantiator->count(), 10); diff --git a/tests/auto/qml/qqmlitemmodels/qtestmodel.h b/tests/auto/qml/qqmlitemmodels/qtestmodel.h index 8724ef927f..6a022b3135 100644 --- a/tests/auto/qml/qqmlitemmodels/qtestmodel.h +++ b/tests/auto/qml/qqmlitemmodels/qtestmodel.h @@ -91,8 +91,6 @@ public: } int rowCount(const QModelIndex& parent = QModelIndex()) const { - if (!fetched) - qFatal("%s: rowCount should not be called before fetching", Q_FUNC_INFO); if ((parent.column() > 0) || (level(parent) > levels) || (alternateChildlessRows && parent.row() > 0 && (parent.row() & 1))) return 0; diff --git a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp index fca72ab021..cbb7ebb0ff 100644 --- a/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp +++ b/tests/auto/qml/qqmlitemmodels/tst_qqmlitemmodels.cpp @@ -182,7 +182,7 @@ void tst_qqmlitemmodels::itemSelection() QCOMPARE(object->property("count").toInt(), 5); QCOMPARE(object->property("contains").toBool(), true); - const char *propNames[] = { "itemSelectionRead", "itemSelectionBinding", 0 }; + const char *propNames[] = { "itemSelectionRead", "itemSelectionBinding", nullptr }; for (const char **name = propNames; *name; name++) { QVariant isVariant = object->property(*name); QCOMPARE(isVariant.userType(), qMetaTypeId<QItemSelection>()); @@ -217,7 +217,7 @@ void tst_qqmlitemmodels::modelIndexList() QModelIndexList someMIL = object->someModelIndexList(); QCOMPARE(cppMILVariant.value<QModelIndexList>(), someMIL); - const char *propNames[] = { "modelIndexListRead", "modelIndexListBinding", 0 }; + const char *propNames[] = { "modelIndexListRead", "modelIndexListBinding", nullptr }; for (const char **name = propNames; *name; name++) { QVariant milVariant = object->property(*name); QCOMPARE(milVariant.userType(), qMetaTypeId<QModelIndexList>()); diff --git a/tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml b/tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml new file mode 100644 index 0000000000..67e92e5a05 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml @@ -0,0 +1,19 @@ +import QtQml 2.0 +import Test 1.0 +DeferredProperties { + groupProperty: QtObject { + objectName: "innerobj" + property bool wasCompleted: false + Component.onCompleted: wasCompleted = true + } + QtObject { + objectName: "innerlist1" + property bool wasCompleted: false + Component.onCompleted: wasCompleted = true + } + QtObject { + objectName: "innerlist2" + property bool wasCompleted: false + Component.onCompleted: wasCompleted = true + } +} diff --git a/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml b/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml new file mode 100644 index 0000000000..f311f6b602 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import Test 1.0 +LazyDeferredSubObject { + subObject: QtObject { objectName: 'default' } + objectName: subObject.objectName +} diff --git a/tests/auto/qml/qqmllanguage/data/TypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/TypeWithEnum.qml new file mode 100644 index 0000000000..c6788f787a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/TypeWithEnum.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + EnumValue2, + EnumValue3 + } + + enum MyOtherEnum { + OtherEnumValue1 = 24, + OtherEnumValue2, + OtherEnumValue3 = 24, + OtherEnumValue4, + OtherEnumValue5 = 1 + } + + property int enumValue: TypeWithEnum.EnumValue2 + property int enumValue2 + property int scopedEnumValue: TypeWithEnum.MyEnum.EnumValue2 + Component.onCompleted: enumValue2 = TypeWithEnum.EnumValue3 + + property int otherEnumValue1: TypeWithEnum.OtherEnumValue1 + property int otherEnumValue2: TypeWithEnum.OtherEnumValue2 + property int otherEnumValue3: TypeWithEnum.OtherEnumValue3 + property int otherEnumValue4: TypeWithEnum.OtherEnumValue4 + property int otherEnumValue5: TypeWithEnum.OtherEnumValue5 +} diff --git a/tests/auto/qml/qqmllanguage/data/accessDeletedObject.qml b/tests/auto/qml/qqmllanguage/data/accessDeletedObject.qml new file mode 100644 index 0000000000..e5151096e5 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/accessDeletedObject.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + id: root + + Component.onCompleted: { + var createdObject = objectCreator.create() + createdObject.del() + // Shouldn't crash. + var test = "index" in createdObject + } +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.15.qml b/tests/auto/qml/qqmllanguage/data/alias.15.qml new file mode 100644 index 0000000000..5f3de9c83e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.15.qml @@ -0,0 +1,12 @@ +import QtQuick 2.6 + +Item { + id: root + + property alias symbol: symbol + symbol.y: 1 + + Item { + id: symbol + } +} diff --git a/tests/auto/qml/qqmllanguage/data/alias.16.qml b/tests/auto/qml/qqmllanguage/data/alias.16.qml new file mode 100644 index 0000000000..4637aec58f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.16.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 + +Window { + visible: true + + property alias list: repeater.model + + list: ["A", "B", "C"] + + Repeater { + id: repeater + } +} + diff --git a/tests/auto/qml/qqmllanguage/data/assignComponentToWrongType.errors.txt b/tests/auto/qml/qqmllanguage/data/assignComponentToWrongType.errors.txt new file mode 100644 index 0000000000..af103cf8bf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/assignComponentToWrongType.errors.txt @@ -0,0 +1 @@ +4:27:Cannot assign object of type "Component" to property of type "QQmlTimer*" as the former is neither the same as the latter nor a sub-class of it. diff --git a/tests/auto/qml/qqmllanguage/data/assignComponentToWrongType.qml b/tests/auto/qml/qqmllanguage/data/assignComponentToWrongType.qml new file mode 100644 index 0000000000..2159bf8116 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/assignComponentToWrongType.qml @@ -0,0 +1,9 @@ +import QtQml 2.9 + +QtObject { + property Timer stuff: Component { + QtObject { + objectName: "wrong" + } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml b/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml index fce248a381..69b0096a5e 100644 --- a/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml +++ b/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml @@ -30,6 +30,7 @@ QtObject { MyQmlObject { id: testObj22; objectName: "test22"; qjsvalue: null }, MyQmlObject { id: testObj1Bound; objectName: "test1Bound"; qjsvalue: testObj1.qjsvalue + 4 }, // 1 + 4 + 4 = 9 MyQmlObject { id: testObj20Bound; objectName: "test20Bound"; qjsvalue: testObj20.qjsvalue(testObj1Bound.qjsvalue) }, // 9 * 3 = 27 + MyQmlObject { id: testObj23; objectName: "test23"; qjsvalue: QtObject { objectName: "blah" } }, QtObject { id: varProperties objectName: "varProperties" diff --git a/tests/auto/qml/qqmllanguage/data/circularSingleton.qml b/tests/auto/qml/qqmllanguage/data/circularSingleton.qml new file mode 100644 index 0000000000..e569111956 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/circularSingleton.qml @@ -0,0 +1,6 @@ +import QtQuick 2.10 +import "singleton/circular" + +QtObject { + property int value: MySingleton.value +} diff --git a/tests/auto/qml/qqmllanguage/data/deferredProperties.qml b/tests/auto/qml/qqmllanguage/data/deferredProperties.qml new file mode 100644 index 0000000000..07b146967c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/deferredProperties.qml @@ -0,0 +1,19 @@ +import QtQml 2.0 +import Test 1.0 +MyDeferredProperties { + groupProperty: QtObject { + objectName: "outerobj" + property bool wasCompleted: false + Component.onCompleted: wasCompleted = true + } + QtObject { + objectName: "outerlist1" + property bool wasCompleted: false + Component.onCompleted: wasCompleted = true + } + QtObject { + objectName: "outerlist2" + property bool wasCompleted: false + Component.onCompleted: wasCompleted = true + } +} diff --git a/tests/auto/qml/qqmllanguage/data/dynamicMeta.5.errors.txt b/tests/auto/qml/qqmllanguage/data/dynamicMeta.5.errors.txt index 015d55b03b..30b5193cd5 100644 --- a/tests/auto/qml/qqmllanguage/data/dynamicMeta.5.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/dynamicMeta.5.errors.txt @@ -1 +1 @@ -3:1:UnknownType is not a type +4:5:UnknownType is not a type diff --git a/tests/auto/qml/qqmllanguage/data/empty.errors.txt b/tests/auto/qml/qqmllanguage/data/empty.errors.txt index 620db2bbba..ba685d78ae 100644 --- a/tests/auto/qml/qqmllanguage/data/empty.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/empty.errors.txt @@ -1,2 +1 @@ -1:1:Expected token `numeric literal' -1:1:Expected a qualified name id +-1:-1:File is empty diff --git a/tests/auto/qml/qqmllanguage/data/groupPropertyInPropertyValueSource.qml b/tests/auto/qml/qqmllanguage/data/groupPropertyInPropertyValueSource.qml new file mode 100644 index 0000000000..579086fa1c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/groupPropertyInPropertyValueSource.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Rectangle { + ColorAnimation on color { + id: animation + from: "red" + to: "darkgray" + duration: 250 + easing.type: Easing.InOutQuad + } +} diff --git a/tests/auto/qml/qqmllanguage/data/importJsModule.1.mjs b/tests/auto/qml/qqmllanguage/data/importJsModule.1.mjs new file mode 100644 index 0000000000..c17a351216 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/importJsModule.1.mjs @@ -0,0 +1,4 @@ + +export function ok() { + return true; +} diff --git a/tests/auto/qml/qqmllanguage/data/importJsModule.1.qml b/tests/auto/qml/qqmllanguage/data/importJsModule.1.qml new file mode 100644 index 0000000000..cc988a6114 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/importJsModule.1.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "importJsModule.1.mjs" as JSModule + +Item { + property bool test: JSModule.ok() +} diff --git a/tests/auto/qml/qqmllanguage/data/importJsModule.2.qml b/tests/auto/qml/qqmllanguage/data/importJsModule.2.qml new file mode 100644 index 0000000000..0799585622 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/importJsModule.2.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import org.qtproject.PureESJsModule 1.0 + +Item { + property bool test: ModuleAPI.ok() +} diff --git a/tests/auto/qml/qqmllanguage/data/importJsModule.3.indirect.mjs b/tests/auto/qml/qqmllanguage/data/importJsModule.3.indirect.mjs new file mode 100644 index 0000000000..41d2f391bf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/importJsModule.3.indirect.mjs @@ -0,0 +1,4 @@ + +import { indirectOk } from "./importJsModule.3.mjs"; + +export function ok() { return indirectOk(); } diff --git a/tests/auto/qml/qqmllanguage/data/importJsModule.3.mjs b/tests/auto/qml/qqmllanguage/data/importJsModule.3.mjs new file mode 100644 index 0000000000..3623dc6f4f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/importJsModule.3.mjs @@ -0,0 +1,4 @@ +export function indirectOk() { return true; } + +export * from "./importJsModule.3.indirect.mjs"; + diff --git a/tests/auto/qml/qqmllanguage/data/importJsModule.3.qml b/tests/auto/qml/qqmllanguage/data/importJsModule.3.qml new file mode 100644 index 0000000000..3b187205c7 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/importJsModule.3.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "importJsModule.3.mjs" as JSModule + +Item { + property bool test: JSModule.ok() +} diff --git a/tests/auto/qml/qqmllanguage/data/incorrectCase.errors.insensitive.txt b/tests/auto/qml/qqmllanguage/data/incorrectCase.errors.insensitive.txt index 3813680562..4f3f758b7e 100644 --- a/tests/auto/qml/qqmllanguage/data/incorrectCase.errors.insensitive.txt +++ b/tests/auto/qml/qqmllanguage/data/incorrectCase.errors.insensitive.txt @@ -1,2 +1,2 @@ -3:1:Type IncorrectCaseType unavailable +3:1:IncorrectCaseType is not a type -1:-1:File name case mismatch diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.12.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidAlias.12.errors.txt new file mode 100644 index 0000000000..8b94763860 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.12.errors.txt @@ -0,0 +1 @@ +4:28:Invalid alias target location: source diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.12.qml b/tests/auto/qml/qqmllanguage/data/invalidAlias.12.qml new file mode 100644 index 0000000000..71063ae320 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.12.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +QtObject { + property alias source: previewImage.source + previewImage { id: previewImage } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt new file mode 100644 index 0000000000..234753ad59 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.errors.txt @@ -0,0 +1 @@ +6:5:Invalid alias target diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml new file mode 100644 index 0000000000..4050c0a7ad --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.13.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 + +QtObject { + property alias dataValue: dataVal + + invalidAliasComponent { + id: dataVal + strValue: "value2" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml b/tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml new file mode 100644 index 0000000000..a45b1806a3 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidAliasComponent.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string strValue: "value1" +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt index 810fd31b41..d76f18ba89 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.1.errors.txt @@ -1 +1 @@ -5:5:Invalid grouped property access +5:5:Invalid grouped property access: Property "o" with type "QVariant", which is not a value type diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt index f6d6f29fbf..9a0422753f 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.3.errors.txt @@ -1 +1 @@ -4:5:Invalid grouped property access +4:5:Invalid grouped property access: Property "customType" with type "MyCustomVariantType", which is not a value type diff --git a/tests/auto/qml/qqmllanguage/data/invalidID.10.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidID.10.errors.txt new file mode 100644 index 0000000000..d9dcb32f8e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidID.10.errors.txt @@ -0,0 +1 @@ +4:9:Invalid use of id property with a value type diff --git a/tests/auto/qml/qqmllanguage/data/invalidID.10.qml b/tests/auto/qml/qqmllanguage/data/invalidID.10.qml new file mode 100644 index 0000000000..4f1817dba4 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidID.10.qml @@ -0,0 +1,7 @@ +import Test 1.0 +MyQmlObject { + rect { + id: notPossible + width: 100 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.errors.txt new file mode 100644 index 0000000000..a96fe376aa --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.errors.txt @@ -0,0 +1 @@ +6:22:Expected token `numeric literal' diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml new file mode 100644 index 0000000000..fef23ecbef --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.1.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + EnumValue2 = "hello", + EnumValue3 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.errors.txt new file mode 100644 index 0000000000..a96fe376aa --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.errors.txt @@ -0,0 +1 @@ +6:22:Expected token `numeric literal' diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml new file mode 100644 index 0000000000..9892fcd19c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.2.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + EnumValue2 = hello, + EnumValue3 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.errors.txt new file mode 100644 index 0000000000..43465c60ec --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.errors.txt @@ -0,0 +1 @@ +7:22:Enum value out of range diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml new file mode 100644 index 0000000000..654bc0e626 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.3.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + EnumValue2, + EnumValue3 = 2147483648 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.4.errors.txt new file mode 100644 index 0000000000..12756dc593 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.4.errors.txt @@ -0,0 +1 @@ +7:22:Enum value must be an integer diff --git a/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.4.qml b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.4.qml new file mode 100644 index 0000000000..4a0aafba5e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/invalidQmlEnumValue.4.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + EnumValue2, + EnumValue3 = 17.5 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml b/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml new file mode 100644 index 0000000000..2465a18320 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +import Test 1.0 +MyLazyDeferredSubObject { + subObject.objectName: 'custom' +} diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/NonSingletonType.qml b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/NonSingletonType.qml new file mode 100644 index 0000000000..ec7c76c055 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/NonSingletonType.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 +import org.qtproject.MixedModule 1.0 + +Item { +} diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml new file mode 100644 index 0000000000..bf5a77576b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 +pragma Singleton + +Item { + enum EnumInSingleton { + EnumValue = 42, + AnotherEnumValue + } + + enum AnotherEnumInSingleton { + AnotherEnumValue = 2 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/qmldir b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/qmldir new file mode 100644 index 0000000000..cd03a5f941 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/qmldir @@ -0,0 +1,4 @@ +module org.qtproject.MixedModule +singleton SingletonType 1.0 SingletonType.qml +NonSingletonType 1.0 NonSingletonType.qml +Test 1.0 test.js diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/test.js b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/test.js new file mode 100644 index 0000000000..6a53b53b02 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/test.js @@ -0,0 +1 @@ +var foo = 1 diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/PureESJsModule/API.mjs b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/PureESJsModule/API.mjs new file mode 100644 index 0000000000..c17a351216 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/PureESJsModule/API.mjs @@ -0,0 +1,4 @@ + +export function ok() { + return true; +} diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/PureESJsModule/qmldir b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/PureESJsModule/qmldir new file mode 100644 index 0000000000..59b4740f75 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/PureESJsModule/qmldir @@ -0,0 +1 @@ +ModuleAPI 1.0 API.mjs diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.errors.txt b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.errors.txt new file mode 100644 index 0000000000..d1bd2bcff4 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.errors.txt @@ -0,0 +1 @@ +6:9:Enum names must begin with an upper case letter diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.qml b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.qml new file mode 100644 index 0000000000..0b50820128 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.1.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum MyEnum { + EnumValue1, + enumValue2, + EnumValue3 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.errors.txt b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.errors.txt new file mode 100644 index 0000000000..3e051c416e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.errors.txt @@ -0,0 +1 @@ +4:5:Scoped enum names must begin with an upper case letter diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.qml b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.qml new file mode 100644 index 0000000000..bb7aea6aa4 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseQmlEnum.2.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + enum myEnum { + EnumValue1, + EnumValue2, + EnumValue3 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/mixedModuleWithSelfImport.qml b/tests/auto/qml/qqmllanguage/data/mixedModuleWithSelfImport.qml new file mode 100644 index 0000000000..7768a6aedf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/mixedModuleWithSelfImport.qml @@ -0,0 +1,3 @@ +import org.qtproject.MixedModule 1.0 + +NonSingletonType {} diff --git a/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyInternalType.qml b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyInternalType.qml new file mode 100644 index 0000000000..0e69012662 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyInternalType.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +QtObject {} diff --git a/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyPublicType.qml b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyPublicType.qml new file mode 100644 index 0000000000..c6b38d51a9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyPublicType.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property InternalType myInternalType: InternalType {} +} diff --git a/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyPublicTypeWithExplicitImport.qml b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyPublicTypeWithExplicitImport.qml new file mode 100644 index 0000000000..9b488f92da --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/MyPublicTypeWithExplicitImport.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +import modulewithinternaltypes 1.0 +QtObject { + property InternalType myInternalType: InternalType {} +} diff --git a/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/qmldir b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/qmldir new file mode 100644 index 0000000000..3593845329 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/modulewithinternaltypes/qmldir @@ -0,0 +1,3 @@ +PublicType 1.0 MyPublicType.qml +PublicTypeWithExplicitImport 1.0 MyPublicTypeWithExplicitImport.qml +internal InternalType MyInternalType.qml diff --git a/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml new file mode 100644 index 0000000000..ff2f0f5a2c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +QtObject { + property int secondVar: { + stats.increaseEvaluationCounter() + return 1 + } + property int firstVar: secondVar + 1 +} diff --git a/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml new file mode 100644 index 0000000000..0eb5e03642 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +QtObject { + property int firstVar: secondVar + 1 + property int secondVar: { + stats.increaseEvaluationCounter() + return 1 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt index db7d9c0f60..cefd62f9d4 100644 --- a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt @@ -1 +1 @@ -4:18:Unexpected object assignment +4:18:Unexpected object assignment for property "x" diff --git a/tests/auto/qml/qqmllanguage/data/polymorphicFunctionLookup.qml b/tests/auto/qml/qqmllanguage/data/polymorphicFunctionLookup.qml new file mode 100644 index 0000000000..4a3cc52793 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/polymorphicFunctionLookup.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 +QtObject { + id: root + property bool testFunc; + property bool ok: false + property QtObject subObject: QtObject { + function testFunc() + { + root.ok = true + } + + Component.onCompleted: testFunc() + } +} diff --git a/tests/auto/qml/qqmllanguage/data/property.4.errors.txt b/tests/auto/qml/qqmllanguage/data/property.4.errors.txt index b447186849..2807384ec4 100644 --- a/tests/auto/qml/qqmllanguage/data/property.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/property.4.errors.txt @@ -1 +1 @@ -5:1:Syntax error +5:1:Expected token `:' diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml new file mode 100644 index 0000000000..4f8174a52d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithNameClash.qml @@ -0,0 +1,10 @@ +import QtQml 2.0 +import ScopedEnumsWithNameClashTest 1.0 + +QtObject { + property bool success: false + + Component.onCompleted: { + success = (ScopedEnum.ScopedEnum.OtherScopedEnum === 3) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml new file mode 100644 index 0000000000..84efa6859b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/scopedEnumsWithResolvedNameClash.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import ScopedEnumsWithResolvedNameClashTest 1.0 + +QtObject { + property bool success: false + + Component.onCompleted: { + success = (ScopedEnum.ScopedEnum.OtherScopedEnum === 3) + && (ScopedEnum.OtherScopedEnum.ScopedVal2 === 1) + } +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml b/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml index d5a4e2ccf6..65d351a975 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml +++ b/tests/auto/qml/qqmllanguage/data/singleton/RegisteredCompositeSingletonType.qml @@ -1,12 +1,22 @@ /**************************************************************************** ** ** Copyright (C) 2013 BlackBerry Limited. All rights reserved. -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are diff --git a/tests/auto/qml/qqmllanguage/data/singleton/circular/MySingleton.qml b/tests/auto/qml/qqmllanguage/data/singleton/circular/MySingleton.qml new file mode 100644 index 0000000000..1253018789 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singleton/circular/MySingleton.qml @@ -0,0 +1,12 @@ +pragma Singleton +import QtQuick 2.10 + +QtObject { + enum MyEnum { + Value0, + Value1, + Value2 + } + + property int value: MySingleton.Value2 +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/circular/qmldir b/tests/auto/qml/qqmllanguage/data/singleton/circular/qmldir new file mode 100644 index 0000000000..3bc50738dd --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singleton/circular/qmldir @@ -0,0 +1 @@ +singleton MySingleton MySingleton.qml diff --git a/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js b/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js index a3dcfd398f..5932178ee3 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js +++ b/tests/auto/qml/qqmllanguage/data/singleton/js/jspragma.js @@ -1,12 +1,22 @@ /**************************************************************************** ** ** Copyright (C) 2013 BlackBerry Limited. All rights reserved. -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest17.qml b/tests/auto/qml/qqmllanguage/data/singletonTest17.qml index 192099845b..f8bebd9752 100644 --- a/tests/auto/qml/qqmllanguage/data/singletonTest17.qml +++ b/tests/auto/qml/qqmllanguage/data/singletonTest17.qml @@ -1,12 +1,22 @@ /**************************************************************************** ** ** Copyright (C) 2013 BlackBerry Limited. All rights reserved. -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest18.qml b/tests/auto/qml/qqmllanguage/data/singletonTest18.qml new file mode 100644 index 0000000000..7616c23531 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singletonTest18.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import "singleton" +import Test 1.0 + +Item { + property var qmlSingleton: SingletonType + property var jsSingleton: MyQJSValueQObjectSingleton + property var cppSingleton: MyTypeObjectSingleton +} diff --git a/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml b/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml new file mode 100644 index 0000000000..e3c99e70e1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml @@ -0,0 +1,10 @@ +import QtQml 2.2 +QtObject { + property int x: 42 + property int y: 0 + function g(){ + y = this.x; + } + property var f: g + Component.onCompleted: f() +} diff --git a/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml new file mode 100644 index 0000000000..79e8347330 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import org.qtproject.MixedModule 1.0 + +QtObject { + property int enumValue: TypeWithEnum.EnumValue2 + property int enumValue2: -1 + property int scopedEnumValue: TypeWithEnum.MyEnum.EnumValue3 + property int enumValueFromSingleton: { var x = SingletonType.EnumValue; return x; } + Component.onCompleted: enumValue2 = TypeWithEnum.EnumValue1 + + property int duplicatedEnumValueFromSingleton: SingletonType.AnotherEnumValue + property int scopedEnumValueFromSingleton1: SingletonType.EnumInSingleton.AnotherEnumValue + property int scopedEnumValueFromSingleton2: SingletonType.AnotherEnumInSingleton.AnotherEnumValue + property int scopedEnumValueFromSingleton3: { var x = SingletonType.AnotherEnumInSingleton.AnotherEnumValue; return x; } +} diff --git a/tests/auto/qml/qqmllanguage/data/wrongType.16.errors.txt b/tests/auto/qml/qqmllanguage/data/wrongType.16.errors.txt index 77cf210918..cedae349d2 100644 --- a/tests/auto/qml/qqmllanguage/data/wrongType.16.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/wrongType.16.errors.txt @@ -1 +1 @@ -4:24:Cannot assign object to property +4:24:Cannot assign object of type "QtObject" to property of type "MyQmlObject*" as the former is neither the same as the latter nor a sub-class of it. diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.pro b/tests/auto/qml/qqmllanguage/qqmllanguage.pro index 99c0c3e823..3e88f3f0db 100644 --- a/tests/auto/qml/qqmllanguage/qqmllanguage.pro +++ b/tests/auto/qml/qqmllanguage/qqmllanguage.pro @@ -6,7 +6,6 @@ SOURCES += tst_qqmllanguage.cpp \ testtypes.cpp HEADERS += testtypes.h -INCLUDEPATH += ../../shared/ HEADERS += ../../shared/testhttpserver.h SOURCES += ../../shared/testhttpserver.cpp diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index bdcdaa8137..f9a6ee8e5a 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -35,6 +35,14 @@ static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngin return new MyTypeObject(); } +static QJSValue myQJSValueQObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + + QJSValue value = scriptEngine->newQObject(new MyTypeObject()); + return value; +} + void registerTypes() { qmlRegisterInterface<MyInterface>("MyInterface"); @@ -101,8 +109,12 @@ void registerTypes() qmlRegisterType<MyCompositeBaseType>("Test", 1, 0, "MyCompositeBaseType"); qmlRegisterSingletonType<MyTypeObjectSingleton>("Test", 1, 0, "MyTypeObjectSingleton", myTypeObjectSingleton); + qmlRegisterSingletonType("Test", 1, 0, "MyQJSValueQObjectSingleton", myQJSValueQObjectSingleton); qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass"); + + qmlRegisterType<LazyDeferredSubObject>("Test", 1, 0, "LazyDeferredSubObject"); + qmlRegisterType<DeferredProperties>("Test", 1, 0, "DeferredProperties"); } QVariant myCustomVariantTypeConverter(const QString &data) @@ -113,7 +125,7 @@ QVariant myCustomVariantTypeConverter(const QString &data) } -void CustomBindingParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { CustomBinding *customBinding = qobject_cast<CustomBinding*>(object); Q_ASSERT(customBinding); @@ -126,14 +138,14 @@ void CustomBinding::componentComplete() Q_ASSERT(m_target); foreach (const QV4::CompiledData::Binding *binding, bindings) { - QString name = compilationUnit->data->stringAt(binding->propertyNameIndex); + QString name = compilationUnit->stringAt(binding->propertyNameIndex); int bindingId = binding->value.compiledScriptIndex; QQmlContextData *context = QQmlContextData::get(qmlContext(this)); QQmlProperty property(m_target, name, qmlContext(this)); - QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); + QV4::Scope scope(qmlEngine(this)->handle()); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(scope.engine->rootContext(), context, m_target)); QQmlBinding *qmlBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, compilationUnit->runtimeFunctions[bindingId], m_target, context, qmlContext); @@ -142,7 +154,7 @@ void CustomBinding::componentComplete() } } -void EnumSupportingCustomParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { if (bindings.count() != 1) { error(bindings.first(), QStringLiteral("Custom parser invoked incorrectly for unit test")); @@ -150,7 +162,7 @@ void EnumSupportingCustomParser::verifyBindings(const QV4::CompiledData::Unit *q } const QV4::CompiledData::Binding *binding = bindings.first(); - if (qmlUnit->stringAt(binding->propertyNameIndex) != QStringLiteral("foo")) { + if (compilationUnit->stringAt(binding->propertyNameIndex) != QStringLiteral("foo")) { error(binding, QStringLiteral("Custom parser invoked with the wrong property name")); return; } @@ -159,7 +171,7 @@ void EnumSupportingCustomParser::verifyBindings(const QV4::CompiledData::Unit *q error(binding, QStringLiteral("Custom parser invoked with the wrong property value. Expected script that evaluates to enum")); return; } - QByteArray script = qmlUnit->stringAt(binding->stringIndex).toUtf8(); + QByteArray script = compilationUnit->stringAt(binding->stringIndex).toUtf8(); bool ok; int v = evaluateEnum(script, &ok); if (!ok) { @@ -172,7 +184,7 @@ void EnumSupportingCustomParser::verifyBindings(const QV4::CompiledData::Unit *q } } -void SimpleObjectCustomParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &bindings) +void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings) { SimpleObjectWithCustomParser *o = qobject_cast<SimpleObjectWithCustomParser*>(object); Q_ASSERT(o); @@ -182,8 +194,8 @@ void SimpleObjectCustomParser::applyBindings(QObject *object, QV4::CompiledData: MyQmlObject::MyQmlObject() : m_value(-1) - , m_interface(0) - , m_qmlobject(0) + , m_interface(nullptr) + , m_qmlobject(nullptr) , m_childAddedEventCount(0) { qRegisterMetaType<MyCustomVariantType>("MyCustomVariantType"); diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index e4a76b4324..bb6e9582c2 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -781,15 +781,15 @@ class MyCustomParserType : public QObject class MyCustomParserTypeParser : public QQmlCustomParser { public: - virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} }; class EnumSupportingCustomParser : public QQmlCustomParser { public: - virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &); - virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} }; class MyParserStatus : public QObject, public QQmlParserStatus @@ -1282,8 +1282,8 @@ public: class CustomBindingParser : public QQmlCustomParser { - virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &); + virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); }; class SimpleObjectWithCustomParser : public QObject @@ -1328,8 +1328,8 @@ private: class SimpleObjectCustomParser : public QQmlCustomParser { - virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &); + virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &); }; class RootObjectInCreationTester : public QObject @@ -1349,6 +1349,65 @@ private: QObject *obj; }; +class LazyDeferredSubObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject *subObject READ subObject WRITE setSubObject NOTIFY subObjectChanged FINAL) + Q_CLASSINFO("DeferredPropertyNames", "subObject"); +public: + LazyDeferredSubObject() + : obj(0) + {} + + QObject *subObject() const { if (!obj) qmlExecuteDeferred(const_cast<LazyDeferredSubObject *>(this)); return obj; } + void setSubObject(QObject *o) { if (obj == o) return; obj = o; emit subObjectChanged(); } + +signals: + void subObjectChanged(); + +private: + QObject *obj; +}; + +class DeferredProperties : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject *groupProperty MEMBER m_group) + Q_PROPERTY(QQmlListProperty<QObject> listProperty READ listProperty) + Q_CLASSINFO("DeferredPropertyNames", "groupProperty,listProperty") + Q_CLASSINFO("DefaultProperty", "listProperty") +public: + QQmlListProperty<QObject> listProperty() { return QQmlListProperty<QObject>(this, m_list); } + +private: + QObject *m_group = 0; + QObjectList m_list; +}; + +class ScopedEnumsWithNameClash +{ + Q_GADGET + Q_ENUMS(ScopedEnum) + Q_ENUMS(OtherScopedEnum) + +public: + enum class ScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3, OtherScopedEnum }; + enum class OtherScopedEnum : int { ScopedVal1 = 10, ScopedVal2 = 11, ScopedVal3 = 12 }; +}; + +class ScopedEnumsWithResolvedNameClash +{ + Q_GADGET + Q_ENUMS(ScopedEnum) + Q_ENUMS(OtherScopedEnum) + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + +public: + enum class ScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3, OtherScopedEnum }; + enum class OtherScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 }; +}; + + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 15f19d550b..32bab2954d 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -34,16 +34,20 @@ #include <QtCore/qdebug.h> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> +#include <QtCore/qscopeguard.h> #include <QSignalSpy> #include <QFont> #include <QQmlFileSelector> #include <QFileSelector> +#include <QEasingCurve> +#include <QScopeGuard> #include <private/qqmlproperty_p.h> #include <private/qqmlmetatype_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmlscriptstring_p.h> #include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlcomponent_p.h> #include "testtypes.h" #include "testhttpserver.h" @@ -123,6 +127,8 @@ private slots: void dynamicObjectProperties(); void dynamicSignalsAndSlots(); void simpleBindings(); + void noDoubleEvaluationForFlushedBindings_data(); + void noDoubleEvaluationForFlushedBindings(); void autoComponentCreation(); void autoComponentCreationInGroupProperty(); void propertyValueSource(); @@ -178,6 +184,10 @@ private slots: void importIncorrectCase(); void importJs_data(); void importJs(); + void importJsModule_data(); + void importJsModule(); + void explicitSelfImport(); + void importInternalType(); void qmlAttachedPropertiesObjectMethod(); void customOnProperty(); @@ -209,6 +219,9 @@ private slots: void lowercaseEnumCompileTime_data(); void lowercaseEnumCompileTime(); void scopedEnum(); + void scopedEnumsWithNameClash(); + void scopedEnumsWithResolvedNameClash(); + void qmlEnums(); void literals_data(); void literals(); @@ -237,6 +250,9 @@ private slots: void compositeSingletonJavaScriptPragma(); void compositeSingletonSelectors(); void compositeSingletonRegistered(); + void compositeSingletonCircular(); + + void singletonsHaveContextAndEngine(); void customParserBindingScopes(); void customParserEvaluateEnum(); @@ -248,6 +264,9 @@ private slots: void propertyCacheInSync(); void rootObjectInCreationNotForSubObjects(); + void lazyDeferredSubObject(); + void deferredProperties(); + void executeDeferredPropertiesOnce(); void noChildEvents(); @@ -269,6 +288,18 @@ private slots: void concurrentLoadQmlDir(); + void accessDeletedObject(); + + void lowercaseTypeNames(); + + void thisInQmlScope(); + + void valueTypeGroupPropertiesInBehavior(); + + void retrieveQmlTypeId(); + + void polymorphicFunctionLookup(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -355,9 +386,11 @@ void tst_qqmllanguage::insertedSemicolon() QQmlComponent component(&engine, testFileUrl(file)); + QScopedPointer<QObject> object; + if(create) { - QObject *object = component.create(); - QVERIFY(!object); + object.reset(component.create()); + QVERIFY(object.isNull()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); @@ -416,6 +449,7 @@ void tst_qqmllanguage::errors_data() QTest::newRow("invalidID.7") << "invalidID.7.qml" << "invalidID.7.errors.txt" << false; QTest::newRow("invalidID.8") << "invalidID.8.qml" << "invalidID.8.errors.txt" << false; QTest::newRow("invalidID.9") << "invalidID.9.qml" << "invalidID.9.errors.txt" << false; + QTest::newRow("invalidID.10") << "invalidID.10.qml" << "invalidID.10.errors.txt" << false; QTest::newRow("scriptString.1") << "scriptString.1.qml" << "scriptString.1.errors.txt" << false; QTest::newRow("scriptString.2") << "scriptString.2.qml" << "scriptString.2.errors.txt" << false; @@ -510,6 +544,8 @@ void tst_qqmllanguage::errors_data() QTest::newRow("invalidAlias.9") << "invalidAlias.9.qml" << "invalidAlias.9.errors.txt" << false; QTest::newRow("invalidAlias.10") << "invalidAlias.10.qml" << "invalidAlias.10.errors.txt" << false; QTest::newRow("invalidAlias.11") << "invalidAlias.11.qml" << "invalidAlias.11.errors.txt" << false; + QTest::newRow("invalidAlias.12") << "invalidAlias.12.qml" << "invalidAlias.12.errors.txt" << false; + QTest::newRow("invalidAlias.13") << "invalidAlias.13.qml" << "invalidAlias.13.errors.txt" << false; QTest::newRow("invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml" << "invalidAttachedProperty.1.errors.txt" << false; QTest::newRow("invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml" << "invalidAttachedProperty.2.errors.txt" << false; @@ -545,6 +581,12 @@ void tst_qqmllanguage::errors_data() QTest::newRow("singularProperty.2") << "singularProperty.2.qml" << "singularProperty.2.errors.txt" << false; QTest::newRow("scopedEnumList") << "scopedEnumList.qml" << "scopedEnumList.errors.txt" << false; + QTest::newRow("lowercase enum value") << "lowercaseQmlEnum.1.qml" << "lowercaseQmlEnum.1.errors.txt" << false; + QTest::newRow("lowercase enum type") << "lowercaseQmlEnum.2.qml" << "lowercaseQmlEnum.2.errors.txt" << false; + QTest::newRow("string enum value") << "invalidQmlEnumValue.1.qml" << "invalidQmlEnumValue.1.errors.txt" << false; + QTest::newRow("identifier enum type") << "invalidQmlEnumValue.2.qml" << "invalidQmlEnumValue.2.errors.txt" << false; + QTest::newRow("enum value too large") << "invalidQmlEnumValue.3.qml" << "invalidQmlEnumValue.3.errors.txt" << false; + QTest::newRow("non-integer enum value") << "invalidQmlEnumValue.4.qml" << "invalidQmlEnumValue.4.errors.txt" << false; const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ? QStringLiteral("incorrectCase.errors.sensitive.txt") : @@ -569,6 +611,8 @@ void tst_qqmllanguage::errors_data() QTest::newRow("badCompositeRegistration.1") << "badCompositeRegistration.1.qml" << "badCompositeRegistration.1.errors.txt" << false; QTest::newRow("badCompositeRegistration.2") << "badCompositeRegistration.2.qml" << "badCompositeRegistration.2.errors.txt" << false; + + QTest::newRow("assignComponentToWrongType") << "assignComponentToWrongType.qml" << "assignComponentToWrongType.errors.txt" << false; } @@ -580,9 +624,11 @@ void tst_qqmllanguage::errors() QQmlComponent component(&engine, testFileUrl(file)); + QScopedPointer<QObject> object; + if (create) { - QObject *object = component.create(); - QVERIFY(!object); + object.reset(component.create()); + QVERIFY(object.isNull()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); @@ -592,16 +638,16 @@ void tst_qqmllanguage::simpleObject() { QQmlComponent component(&engine, testFileUrl("simpleObject.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); } void tst_qqmllanguage::simpleContainer() { QQmlComponent component(&engine, testFileUrl("simpleContainer.qml")); VERIFY_ERRORS(0); - MyContainer *container= qobject_cast<MyContainer*>(component.create()); - QVERIFY(container != 0); + QScopedPointer<MyContainer> container(qobject_cast<MyContainer*>(component.create())); + QVERIFY(container != nullptr); QCOMPARE(container->getChildren()->count(),2); } @@ -609,8 +655,8 @@ void tst_qqmllanguage::interfaceProperty() { QQmlComponent component(&engine, testFileUrl("interfaceProperty.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); + QVERIFY(object != nullptr); QVERIFY(object->interface()); QCOMPARE(object->interface()->id, 913); } @@ -619,8 +665,8 @@ void tst_qqmllanguage::interfaceQList() { QQmlComponent component(&engine, testFileUrl("interfaceQList.qml")); VERIFY_ERRORS(0); - MyContainer *container= qobject_cast<MyContainer*>(component.create()); - QVERIFY(container != 0); + QScopedPointer<MyContainer> container(qobject_cast<MyContainer*>(component.create())); + QVERIFY(container != nullptr); QCOMPARE(container->getQListInterfaces()->count(), 2); for(int ii = 0; ii < 2; ++ii) QCOMPARE(container->getQListInterfaces()->at(ii)->id, 913); @@ -630,8 +676,8 @@ void tst_qqmllanguage::assignObjectToSignal() { QQmlComponent component(&engine, testFileUrl("assignObjectToSignal.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); } @@ -640,8 +686,8 @@ void tst_qqmllanguage::assignObjectToVariant() { QQmlComponent component(&engine, testFileUrl("assignObjectToVariant.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVariant v = object->property("a"); QVERIFY(v.userType() == qMetaTypeId<QObject *>()); } @@ -650,8 +696,8 @@ void tst_qqmllanguage::assignLiteralSignalProperty() { QQmlComponent component(&engine, testFileUrl("assignLiteralSignalProperty.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->onLiteralSignal(), 10); } @@ -660,8 +706,8 @@ void tst_qqmllanguage::assignQmlComponent() { QQmlComponent component(&engine, testFileUrl("assignQmlComponent.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast<MyContainer *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 1); QObject *child = object->getChildren()->at(0); QCOMPARE(child->property("x"), QVariant(10)); @@ -673,8 +719,8 @@ void tst_qqmllanguage::assignBasicTypes() { QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); QCOMPARE(object->qtEnumProperty(), Qt::RichText); @@ -703,9 +749,9 @@ void tst_qqmllanguage::assignBasicTypes() QCOMPARE(object->vector4Property(), QVector4D(10, 1, 2.2f, 2.3f)); const QUrl encoded = QUrl::fromEncoded("main.qml?with%3cencoded%3edata", QUrl::TolerantMode); QCOMPARE(object->urlProperty(), component.url().resolved(encoded)); - QVERIFY(object->objectProperty() != 0); + QVERIFY(object->objectProperty() != nullptr); MyTypeObject *child = qobject_cast<MyTypeObject *>(object->objectProperty()); - QVERIFY(child != 0); + QVERIFY(child != nullptr); QCOMPARE(child->intProperty(), 8); //these used to go via script. Ensure they no longer do @@ -718,8 +764,8 @@ void tst_qqmllanguage::assignTypeExtremes() { QQmlComponent component(&engine, testFileUrl("assignTypeExtremes.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->uintProperty(), 0xEE6B2800); QCOMPARE(object->intProperty(), -0x77359400); } @@ -729,8 +775,8 @@ void tst_qqmllanguage::assignCompositeToType() { QQmlComponent component(&engine, testFileUrl("assignCompositeToType.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); } // Test that literals are stored correctly in variant properties @@ -738,8 +784,8 @@ void tst_qqmllanguage::assignLiteralToVariant() { QQmlComponent component(&engine, testFileUrl("assignLiteralToVariant.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVERIFY(isJSNumberType(object->property("test1").userType())); QVERIFY(isJSNumberType(object->property("test2").userType())); @@ -766,8 +812,6 @@ void tst_qqmllanguage::assignLiteralToVariant() QCOMPARE(object->property("test10"), QVariant(bool(true))); QCOMPARE(object->property("test11"), QVariant(bool(false))); QVERIFY(object->property("test12") == QVariant(QVector4D(100, 100, 100, 100))); - - delete object; } // Test that literals are stored correctly in "var" properties @@ -777,8 +821,8 @@ void tst_qqmllanguage::assignLiteralToVar() { QQmlComponent component(&engine, testFileUrl("assignLiteralToVar.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVERIFY(isJSNumberType(object->property("test1").userType())); QCOMPARE(object->property("test2").userType(), (int)QMetaType::Double); @@ -817,16 +861,14 @@ void tst_qqmllanguage::assignLiteralToVar() QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); QCOMPARE(object->property("variantTest1Bound"), QVariant(9)); QCOMPARE(object->property("test1Bound"), QVariant(11)); - - delete object; } void tst_qqmllanguage::assignLiteralToJSValue() { QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); - QVERIFY(root != 0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root != nullptr); { MyQmlObject *object = root->findChild<MyQmlObject *>("test1"); @@ -906,6 +948,11 @@ void tst_qqmllanguage::assignLiteralToJSValue() QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(27)); + } { + MyQmlObject *object = root->findChild<MyQmlObject *>("test23"); + QJSValue value = object->qjsvalue(); + QVERIFY(value.isQObject()); + QCOMPARE(value.toQObject()->objectName(), "blah"); } } @@ -913,11 +960,11 @@ void tst_qqmllanguage::assignNullStrings() { QQmlComponent component(&engine, testFileUrl("assignNullStrings.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); QVERIFY(object->stringProperty().isNull()); QVERIFY(object->byteArrayProperty().isNull()); - QMetaObject::invokeMethod(object, "assignNullStringsFromJs", Qt::DirectConnection); + QMetaObject::invokeMethod(object.data(), "assignNullStringsFromJs", Qt::DirectConnection); QVERIFY(object->stringProperty().isNull()); QVERIFY(object->byteArrayProperty().isNull()); } @@ -927,8 +974,8 @@ void tst_qqmllanguage::bindJSValueToVar() QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); - QVERIFY(root != 0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root != nullptr); QObject *object = root->findChild<QObject *>("varProperties"); @@ -976,8 +1023,8 @@ void tst_qqmllanguage::bindJSValueToVariant() QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); - QVERIFY(root != 0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root != nullptr); QObject *object = root->findChild<QObject *>("variantProperties"); @@ -1025,8 +1072,8 @@ void tst_qqmllanguage::bindJSValueToType() QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); - QVERIFY(root != 0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root != nullptr); { MyTypeObject *object = root->findChild<MyTypeObject *>("typedProperties"); @@ -1060,8 +1107,8 @@ void tst_qqmllanguage::bindTypeToJSValue() QQmlComponent component(&engine, testFileUrl("bindTypeToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); - QVERIFY(root != 0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root != nullptr); { MyQmlObject *object = root->findChild<MyQmlObject *>("flagProperty"); @@ -1199,8 +1246,8 @@ void tst_qqmllanguage::customParserTypes() { QQmlComponent component(&engine, testFileUrl("customParserTypes.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("count"), QVariant(2)); } @@ -1209,8 +1256,8 @@ void tst_qqmllanguage::rootAsQmlComponent() { QQmlComponent component(&engine, testFileUrl("rootAsQmlComponent.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast<MyContainer *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->property("x"), QVariant(11)); QCOMPARE(object->getChildren()->count(), 2); } @@ -1233,13 +1280,13 @@ void tst_qqmllanguage::inlineQmlComponents() { QQmlComponent component(&engine, testFileUrl("inlineQmlComponents.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast<MyContainer *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 1); QQmlComponent *comp = qobject_cast<QQmlComponent *>(object->getChildren()->at(0)); - QVERIFY(comp != 0); - MyQmlObject *compObject = qobject_cast<MyQmlObject *>(comp->create()); - QVERIFY(compObject != 0); + QVERIFY(comp != nullptr); + QScopedPointer<MyQmlObject> compObject(qobject_cast<MyQmlObject *>(comp->create())); + QVERIFY(compObject != nullptr); QCOMPARE(compObject->value(), 11); } @@ -1249,18 +1296,18 @@ void tst_qqmllanguage::idProperty() { QQmlComponent component(&engine, testFileUrl("idProperty.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast<MyContainer *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyContainer> object(qobject_cast<MyContainer *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 2); MyTypeObject *child = qobject_cast<MyTypeObject *>(object->getChildren()->at(0)); - QVERIFY(child != 0); + QVERIFY(child != nullptr); QCOMPARE(child->id(), QString("myObjectId")); QCOMPARE(object->property("object"), QVariant::fromValue((QObject *)child)); child = qobject_cast<MyTypeObject *>(object->getChildren()->at(1)); - QVERIFY(child != 0); + QVERIFY(child != nullptr); QCOMPARE(child->id(), QString("name.with.dots")); } { @@ -1280,14 +1327,14 @@ void tst_qqmllanguage::autoNotifyConnection() { QQmlComponent component(&engine, testFileUrl("autoNotifyConnection.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != nullptr); QMetaProperty prop = object->metaObject()->property(object->metaObject()->indexOfProperty("receivedNotify")); QVERIFY(prop.isValid()); - QCOMPARE(prop.read(object), QVariant::fromValue(false)); + QCOMPARE(prop.read(object.data()), QVariant::fromValue(false)); object->setPropertyWithNotify(1); - QCOMPARE(prop.read(object), QVariant::fromValue(true)); + QCOMPARE(prop.read(object.data()), QVariant::fromValue(true)); } // Tests that signals can be assigned to @@ -1295,8 +1342,8 @@ void tst_qqmllanguage::assignSignal() { QQmlComponent component(&engine, testFileUrl("assignSignal.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlotWithArgs(9)"); @@ -1307,8 +1354,8 @@ void tst_qqmllanguage::assignSignalFunctionExpression() { QQmlComponent component(&engine, testFileUrl("assignSignalFunctionExpression.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlotWithArgs(9)"); @@ -1336,10 +1383,9 @@ void tst_qqmllanguage::overrideSignal() QQmlComponent component(&engine, testFileUrl(file)); if (errorFile.isEmpty()) { VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); - delete object; } else { VERIFY_ERRORS(errorFile.toLatin1().constData()); } @@ -1350,8 +1396,7 @@ void tst_qqmllanguage::dynamicProperties() { QQmlComponent component(&engine, testFileUrl("dynamicProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); QCOMPARE(object->property("intProperty"), QVariant(10)); QCOMPARE(object->property("boolProperty"), QVariant(false)); QCOMPARE(object->property("doubleProperty"), QVariant(-10.1)); @@ -1368,15 +1413,13 @@ void tst_qqmllanguage::dynamicPropertiesNested() { QQmlComponent component(&engine, testFileUrl("dynamicPropertiesNested.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("super_a").toInt(), 11); // Overridden QCOMPARE(object->property("super_c").toInt(), 14); // Inherited QCOMPARE(object->property("a").toInt(), 13); // New QCOMPARE(object->property("b").toInt(), 12); // New - - delete object; } // Tests the creation and assignment to dynamic list properties @@ -1384,8 +1427,8 @@ void tst_qqmllanguage::listProperties() { QQmlComponent component(&engine, testFileUrl("listProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 2); } @@ -1404,19 +1447,19 @@ void tst_qqmllanguage::dynamicObjectProperties() { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); - QCOMPARE(object->property("objectProperty"), qVariantFromValue((QObject*)0)); - QVERIFY(object->property("objectProperty2") != qVariantFromValue((QObject*)0)); + QCOMPARE(object->property("objectProperty"), qVariantFromValue((QObject*)nullptr)); + QVERIFY(object->property("objectProperty2") != qVariantFromValue((QObject*)nullptr)); } { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); - QVERIFY(object->property("objectProperty") != qVariantFromValue((QObject*)0)); + QVERIFY(object->property("objectProperty") != qVariantFromValue((QObject*)nullptr)); } } @@ -1427,15 +1470,15 @@ void tst_qqmllanguage::dynamicSignalsAndSlots() QQmlComponent component(&engine, testFileUrl("dynamicSignalsAndSlots.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVERIFY(object->metaObject()->indexOfMethod("signal1()") != -1); QVERIFY(object->metaObject()->indexOfMethod("signal2()") != -1); QVERIFY(object->metaObject()->indexOfMethod("slot1()") != -1); QVERIFY(object->metaObject()->indexOfMethod("slot2()") != -1); QCOMPARE(object->property("test").toInt(), 0); - QMetaObject::invokeMethod(object, "slot3", Qt::DirectConnection, Q_ARG(QVariant, QVariant(10))); + QMetaObject::invokeMethod(object.data(), "slot3", Qt::DirectConnection, Q_ARG(QVariant, QVariant(10))); QCOMPARE(object->property("test").toInt(), 10); } @@ -1443,13 +1486,44 @@ void tst_qqmllanguage::simpleBindings() { QQmlComponent component(&engine, testFileUrl("simpleBindings.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("value1"), QVariant(10)); QCOMPARE(object->property("value2"), QVariant(10)); QCOMPARE(object->property("value3"), QVariant(21)); QCOMPARE(object->property("value4"), QVariant(10)); - QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object)); + QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object.data())); +} + +class EvaluationCounter : public QObject +{ + Q_OBJECT +public: + int counter = 0; + Q_INVOKABLE void increaseEvaluationCounter() { ++counter; } +}; + +void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings_data() +{ + QTest::addColumn<QString>("fileName"); + QTest::newRow("order1") << QString("noDoubleEvaluationForFlushedBindings.qml"); + QTest::newRow("order2") << QString("noDoubleEvaluationForFlushedBindings.2.qml"); +} + +void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings() +{ + QFETCH(QString, fileName); + QQmlEngine engine; + + EvaluationCounter stats; + engine.rootContext()->setContextProperty("stats", &stats); + + QQmlComponent component(&engine, testFileUrl(fileName)); + VERIFY_ERRORS(0); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QCOMPARE(stats.counter, 1); } void tst_qqmllanguage::autoComponentCreation() @@ -1457,21 +1531,21 @@ void tst_qqmllanguage::autoComponentCreation() { QQmlComponent component(&engine, testFileUrl("autoComponentCreation.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); - QVERIFY(object->componentProperty() != 0); - MyTypeObject *child = qobject_cast<MyTypeObject *>(object->componentProperty()->create()); - QVERIFY(child != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); + QVERIFY(object->componentProperty() != nullptr); + QScopedPointer<MyTypeObject> child(qobject_cast<MyTypeObject *>(object->componentProperty()->create())); + QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } { QQmlComponent component(&engine, testFileUrl("autoComponentCreation.2.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); - QVERIFY(object->componentProperty() != 0); - MyTypeObject *child = qobject_cast<MyTypeObject *>(object->componentProperty()->create()); - QVERIFY(child != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); + QVERIFY(object->componentProperty() != nullptr); + QScopedPointer<MyTypeObject> child(qobject_cast<MyTypeObject *>(object->componentProperty()->create())); + QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } } @@ -1480,11 +1554,11 @@ void tst_qqmllanguage::autoComponentCreationInGroupProperty() { QQmlComponent component(&engine, testFileUrl("autoComponentCreationInGroupProperties.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); - QVERIFY(object->componentProperty() != 0); - MyTypeObject *child = qobject_cast<MyTypeObject *>(object->componentProperty()->create()); - QVERIFY(child != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); + QVERIFY(object->componentProperty() != nullptr); + QScopedPointer<MyTypeObject> child(qobject_cast<MyTypeObject *>(object->componentProperty()->create())); + QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } @@ -1493,8 +1567,8 @@ void tst_qqmllanguage::propertyValueSource() { QQmlComponent component(&engine, testFileUrl("propertyValueSource.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); QList<QObject *> valueSources; QObjectList allChildren = object->findChildren<QObject*>(); @@ -1506,16 +1580,16 @@ void tst_qqmllanguage::propertyValueSource() QCOMPARE(valueSources.count(), 1); MyPropertyValueSource *valueSource = qobject_cast<MyPropertyValueSource *>(valueSources.at(0)); - QVERIFY(valueSource != 0); - QCOMPARE(valueSource->prop.object(), qobject_cast<QObject*>(object)); + QVERIFY(valueSource != nullptr); + QCOMPARE(valueSource->prop.object(), qobject_cast<QObject*>(object.data())); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); } { QQmlComponent component(&engine, testFileUrl("propertyValueSource.2.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); QList<QObject *> valueSources; QObjectList allChildren = object->findChildren<QObject*>(); @@ -1527,8 +1601,8 @@ void tst_qqmllanguage::propertyValueSource() QCOMPARE(valueSources.count(), 1); MyPropertyValueSource *valueSource = qobject_cast<MyPropertyValueSource *>(valueSources.at(0)); - QVERIFY(valueSource != 0); - QCOMPARE(valueSource->prop.object(), qobject_cast<QObject*>(object)); + QVERIFY(valueSource != nullptr); + QCOMPARE(valueSource->prop.object(), qobject_cast<QObject*>(object.data())); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); } } @@ -1537,10 +1611,10 @@ void tst_qqmllanguage::attachedProperties() { QQmlComponent component(&engine, testFileUrl("attachedProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); - QObject *attached = qmlAttachedPropertiesObject<MyQmlObject>(object); - QVERIFY(attached != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QObject *attached = qmlAttachedPropertiesObject<MyQmlObject>(object.data()); + QVERIFY(attached != nullptr); QCOMPARE(attached->property("value"), QVariant(10)); QCOMPARE(attached->property("value2"), QVariant(13)); } @@ -1550,8 +1624,8 @@ void tst_qqmllanguage::dynamicObjects() { QQmlComponent component(&engine, testFileUrl("dynamicObject.1.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); } // Tests the registration of custom variant string converters @@ -1559,8 +1633,8 @@ void tst_qqmllanguage::customVariantTypes() { QQmlComponent component(&engine, testFileUrl("customVariantTypes.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->customType().a, 10); } @@ -1573,8 +1647,8 @@ void tst_qqmllanguage::valueTypes() QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->rectProperty(), QRect(10, 11, 12, 13)); @@ -1604,20 +1678,17 @@ void tst_qqmllanguage::cppnamespace() { QQmlComponent component(&engine, testFileUrl("cppnamespace.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2); - - delete object; } { QQmlComponent component(&engine, testFileUrl("cppnamespace.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); - delete object; + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); } } @@ -1627,8 +1698,8 @@ void tst_qqmllanguage::aliasProperties() { QQmlComponent component(&engine, testFileUrl("alias.1.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); // Read through alias QCOMPARE(object->property("valueAlias").toInt(), 10); @@ -1639,41 +1710,37 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("valueAlias", QVariant(19)); QCOMPARE(object->property("valueAlias").toInt(), 19); QCOMPARE(object->property("value").toInt(), 19); - - delete object; } // Complex object alias { QQmlComponent component(&engine, testFileUrl("alias.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); // Read through alias MyQmlObject *v = qvariant_cast<MyQmlObject *>(object->property("aliasObject")); - QVERIFY(v != 0); + QVERIFY(v != nullptr); QCOMPARE(v->value(), 10); // Write through alias MyQmlObject *v2 = new MyQmlObject(); - v2->setParent(object); + v2->setParent(object.data()); object->setProperty("aliasObject", qVariantFromValue(v2)); MyQmlObject *v3 = qvariant_cast<MyQmlObject *>(object->property("aliasObject")); - QVERIFY(v3 != 0); + QVERIFY(v3 != nullptr); QCOMPARE(v3, v2); - - delete object; } // Nested aliases { QQmlComponent component(&engine, testFileUrl("alias.3.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 1892); QCOMPARE(object->property("value2").toInt(), 1892); @@ -1685,28 +1752,24 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("value2", QVariant(8080)); QCOMPARE(object->property("value").toInt(), 8080); QCOMPARE(object->property("value2").toInt(), 8080); - - delete object; } // Enum aliases { QQmlComponent component(&engine, testFileUrl("alias.4.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("enumAlias").toInt(), 1); - - delete object; } // Id aliases { QQmlComponent component(&engine, testFileUrl("alias.5.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVariant v = object->property("otherAlias"); QCOMPARE(v.userType(), qMetaTypeId<MyQmlObject*>()); @@ -1719,16 +1782,14 @@ void tst_qqmllanguage::aliasProperties() QCOMPARE(v.userType(), qMetaTypeId<MyQmlObject*>()); o = qvariant_cast<MyQmlObject*>(v); QVERIFY(!o); - - delete object; } // Nested aliases - this used to cause a crash { QQmlComponent component(&engine, testFileUrl("alias.6.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 1923); } @@ -1739,23 +1800,23 @@ void tst_qqmllanguage::aliasProperties() QQmlComponent component(&engine, testFileUrl("alias.7.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QObject *object1 = qvariant_cast<QObject *>(object->property("object")); - QVERIFY(object1 != 0); + QVERIFY(object1 != nullptr); QObject *object2 = qvariant_cast<QObject *>(object1->property("object")); - QVERIFY(object2 != 0); + QVERIFY(object2 != nullptr); QObject *alias = qvariant_cast<QObject *>(object->property("aliasedObject")); QCOMPARE(alias, object2); delete object1; - QObject *alias2 = object; // "Random" start value + QObject *alias2 = object.data(); // "Random" start value int status = -1; - void *a[] = { &alias2, 0, &status }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, + void *a[] = { &alias2, nullptr, &status }; + QMetaObject::metacall(object.data(), QMetaObject::ReadProperty, object->metaObject()->indexOfProperty("aliasedObject"), a); QVERIFY(!alias2); } @@ -1764,24 +1825,20 @@ void tst_qqmllanguage::aliasProperties() { QQmlComponent component(&engine, testFileUrl("alias.8.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 10); - - delete object; } // Complex composite type { QQmlComponent component(&engine, testFileUrl("alias.9.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 10); - - delete object; } // Valuetype alias @@ -1789,8 +1846,8 @@ void tst_qqmllanguage::aliasProperties() { QQmlComponent component(&engine, testFileUrl("alias.10.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); // Read through alias QCOMPARE(object->property("valueAlias").toRect(), QRect(10, 11, 9, 8)); @@ -1801,16 +1858,14 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("valueAlias", QVariant(QRect(3, 3, 4, 9))); QCOMPARE(object->property("valueAlias").toRect(), QRect(3, 3, 4, 9)); QCOMPARE(object->property("rectProperty").toRect(), QRect(3, 3, 4, 9)); - - delete object; } // Valuetype sub-alias { QQmlComponent component(&engine, testFileUrl("alias.11.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); // Read through alias QCOMPARE(object->property("aliasProperty").toInt(), 19); @@ -1821,8 +1876,6 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("aliasProperty", QVariant(4)); QCOMPARE(object->property("aliasProperty").toInt(), 4); QCOMPARE(object->property("rectProperty").toRect(), QRect(4, 8, 102, 111)); - - delete object; } // Nested aliases with a qml file @@ -1866,6 +1919,30 @@ void tst_qqmllanguage::aliasProperties() QVERIFY(subObject->property("success").toBool()); } + + // Property bindings on group properties that are actually aliases (QTBUG-51043) + { + QQmlComponent component(&engine, testFileUrl("alias.15.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QObject> subItem = qvariant_cast<QObject*>(object->property("symbol")); + QVERIFY(!subItem.isNull()); + + QCOMPARE(subItem->property("y").toInt(), 1); + } + + // Alias to sub-object with binding (QTBUG-57041) + { + // This is shold *not* crash. + QQmlComponent component(&engine, testFileUrl("alias.16.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + } } // QTBUG-13374 Test that alias properties and signals can coexist @@ -1873,10 +1950,9 @@ void tst_qqmllanguage::aliasPropertiesAndSignals() { QQmlComponent component(&engine, testFileUrl("aliasPropertiesAndSignals.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer<QObject> o(component.create()); QVERIFY(o); QCOMPARE(o->property("test").toBool(), true); - delete o; } // Test that the root element in a composite type can be a Component @@ -1884,20 +1960,20 @@ void tst_qqmllanguage::componentCompositeType() { QQmlComponent component(&engine, testFileUrl("componentCompositeType.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); } class TestType : public QObject { Q_OBJECT public: - TestType(QObject *p=0) : QObject(p) {} + TestType(QObject *p=nullptr) : QObject(p) {} }; class TestType2 : public QObject { Q_OBJECT public: - TestType2(QObject *p=0) : QObject(p) {} + TestType2(QObject *p=nullptr) : QObject(p) {} }; void tst_qqmllanguage::i18n_data() @@ -1918,11 +1994,9 @@ void tst_qqmllanguage::i18n() QFETCH(QString, stringProperty); QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->stringProperty(), stringProperty); - - delete object; } // Check that the Component::onCompleted attached property works @@ -1933,8 +2007,8 @@ void tst_qqmllanguage::onCompleted() QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); QTest::ignoreMessage(QtDebugMsg, "Completed 10 11"); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); } // Check that the Component::onDestruction attached property works @@ -1942,12 +2016,11 @@ void tst_qqmllanguage::onDestruction() { QQmlComponent component(&engine, testFileUrl("onDestruction.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QTest::ignoreMessage(QtDebugMsg, "Destruction 6 10"); QTest::ignoreMessage(QtDebugMsg, "Destruction 6 10"); QTest::ignoreMessage(QtDebugMsg, "Destruction 10 11"); - delete object; } // Check that assignments to QQmlScriptString properties work @@ -1957,8 +2030,8 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QVERIFY(!object->scriptProperty().isEmpty()); QCOMPARE(object->scriptProperty().stringLiteral(), QString()); bool ok; @@ -1966,24 +2039,24 @@ void tst_qqmllanguage::scriptString() QCOMPARE(ok, false); const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(object->scriptProperty()); - QVERIFY(scriptPrivate != 0); + QVERIFY(scriptPrivate != nullptr); QCOMPARE(scriptPrivate->script, QString("foo + bar")); - QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object)); - QCOMPARE(scriptPrivate->context, qmlContext(object)); + QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object.data())); + QCOMPARE(scriptPrivate->context, qmlContext(object.data())); - QVERIFY(object->grouped() != 0); + QVERIFY(object->grouped() != nullptr); const QQmlScriptStringPrivate *groupedPrivate = QQmlScriptStringPrivate::get(object->grouped()->script()); QCOMPARE(groupedPrivate->script, QString("console.log(1921)")); - QCOMPARE(groupedPrivate->scope, qobject_cast<QObject*>(object)); - QCOMPARE(groupedPrivate->context, qmlContext(object)); + QCOMPARE(groupedPrivate->scope, qobject_cast<QObject*>(object.data())); + QCOMPARE(groupedPrivate->context, qmlContext(object.data())); } { QQmlComponent component(&engine, testFileUrl("scriptString2.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().stringLiteral(), QString("hello\\n\\\"world\\\"")); } @@ -1991,8 +2064,8 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString3.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); bool ok; QCOMPARE(object->scriptProperty().numberLiteral(&ok), qreal(12.345)); QCOMPARE(ok, true); @@ -2003,8 +2076,8 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString4.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); bool ok; QCOMPARE(object->scriptProperty().booleanLiteral(&ok), true); QCOMPARE(ok, true); @@ -2014,8 +2087,8 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString5.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().isNullLiteral(), true); } @@ -2023,20 +2096,20 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString6.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().isUndefinedLiteral(), true); } { QQmlComponent component(&engine, testFileUrl("scriptString7.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QQmlScriptString ss = object->scriptProperty(); { - QQmlExpression expr(ss, /*context*/0, object); + QQmlExpression expr(ss, /*context*/nullptr, object.data()); QCOMPARE(expr.evaluate().toInt(), int(100)); } @@ -2045,7 +2118,7 @@ void tst_qqmllanguage::scriptString() QVERIFY(testScope.metaObject()->indexOfProperty("intProperty") != object->metaObject()->indexOfProperty("intProperty")); testScope.setIntProperty(42); - QQmlExpression expr(ss, /*context*/0, &testScope); + QQmlExpression expr(ss, /*context*/nullptr, &testScope); QCOMPARE(expr.evaluate().toInt(), int(42)); } } @@ -2057,10 +2130,10 @@ void tst_qqmllanguage::scriptStringJs() QQmlComponent component(&engine, testFileUrl("scriptStringJs.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); - QQmlContext *context = QQmlEngine::contextForObject(object); - QVERIFY(context != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); + QQmlContext *context = QQmlEngine::contextForObject(object.data()); + QVERIFY(context != nullptr); bool ok; QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\" hello \\\" world \"")); @@ -2071,8 +2144,8 @@ void tst_qqmllanguage::scriptStringJs() QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); - QJSValue inst = engine.newQObject(object); - QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); + QJSValue inst = engine.newQObject(object.data()); + QJSValue func = engine.evaluate("(function(value) { this.scriptProperty = value })"); func.callWithInstance(inst, QJSValueList() << "test a \"string "); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\"test a \\\"string \"")); @@ -2134,26 +2207,27 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QUrl url = testFileUrl("scriptString7.qml"); { QQmlEnginePrivate *eng = QQmlEnginePrivate::get(&engine); - QQmlTypeData *td = eng->typeLoader.getType(url); + QQmlRefPointer<QQmlTypeData> td = eng->typeLoader.getType(url); Q_ASSERT(td); - const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->data; + const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->unitData(); Q_ASSERT(readOnlyQmlUnit); QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize)); memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize); qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; - td->compilationUnit()->data = qmlUnit; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = td->compilationUnit(); + compilationUnit->setUnitData(qmlUnit); - const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); - QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); + const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); + QCOMPARE(compilationUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); quint32 i; for (i = 0; i < rootObject->nBindings; ++i) { const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i; - if (qmlUnit->stringAt(binding->propertyNameIndex) != QString("scriptProperty")) + if (compilationUnit->stringAt(binding->propertyNameIndex) != QString("scriptProperty")) continue; - QCOMPARE(binding->valueAsScriptString(qmlUnit), QString("intProperty")); + QCOMPARE(binding->valueAsScriptString(compilationUnit.data()), QString("intProperty")); const_cast<QV4::CompiledData::Binding*>(binding)->stringIndex = 0; // empty string index - QVERIFY(binding->valueAsScriptString(qmlUnit).isEmpty()); + QVERIFY(binding->valueAsScriptString(compilationUnit.data()).isEmpty()); break; } QVERIFY(i < rootObject->nBindings); @@ -2161,8 +2235,8 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QQmlComponent component(&engine, url); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create())); + QVERIFY(object != nullptr); QQmlScriptString ss = object->scriptProperty(); QVERIFY(!ss.isEmpty()); QCOMPARE(ss.stringLiteral(), QString()); @@ -2171,13 +2245,13 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QCOMPARE(ok, false); const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(ss); - QVERIFY(scriptPrivate != 0); + QVERIFY(scriptPrivate != nullptr); QVERIFY(scriptPrivate->script.isEmpty()); - QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object)); - QCOMPARE(scriptPrivate->context, qmlContext(object)); + QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object.data())); + QCOMPARE(scriptPrivate->context, qmlContext(object.data())); { - QQmlExpression expr(ss, /*context*/0, object); + QQmlExpression expr(ss, /*context*/nullptr, object.data()); QCOMPARE(expr.evaluate().toInt(), int(100)); } } @@ -2188,23 +2262,23 @@ void tst_qqmllanguage::scriptStringComparison() { QQmlComponent component1(&engine, testFileUrl("scriptString.qml")); QVERIFY(!component1.isError() && component1.errors().isEmpty()); - MyTypeObject *object1 = qobject_cast<MyTypeObject*>(component1.create()); - QVERIFY(object1 != 0); + QScopedPointer<MyTypeObject> object1(qobject_cast<MyTypeObject*>(component1.create())); + QVERIFY(object1 != nullptr); QQmlComponent component2(&engine, testFileUrl("scriptString2.qml")); QVERIFY(!component2.isError() && component2.errors().isEmpty()); - MyTypeObject *object2 = qobject_cast<MyTypeObject*>(component2.create()); - QVERIFY(object2 != 0); + QScopedPointer<MyTypeObject> object2(qobject_cast<MyTypeObject*>(component2.create())); + QVERIFY(object2 != nullptr); QQmlComponent component3(&engine, testFileUrl("scriptString3.qml")); QVERIFY(!component3.isError() && component3.errors().isEmpty()); - MyTypeObject *object3 = qobject_cast<MyTypeObject*>(component3.create()); - QVERIFY(object3 != 0); + QScopedPointer<MyTypeObject> object3(qobject_cast<MyTypeObject*>(component3.create())); + QVERIFY(object3 != nullptr); //QJSValue inst1 = engine.newQObject(object1); - QJSValue inst2 = engine.newQObject(object2); - QJSValue inst3 = engine.newQObject(object3); - QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); + QJSValue inst2 = engine.newQObject(object2.data()); + QJSValue inst3 = engine.newQObject(object3.data()); + QJSValue func = engine.evaluate("(function(value) { this.scriptProperty = value })"); const QString s = "hello\\n\\\"world\\\""; const qreal n = 12.345; @@ -2258,8 +2332,8 @@ void tst_qqmllanguage::scriptStringComparison() // While this are two instances of the same object they are still considered different // because the (none literal) script string may access variables which have different // values in both instances and hence evaluated to different results. - MyTypeObject *object1_2 = qobject_cast<MyTypeObject*>(component1.create()); - QVERIFY(object1_2 != 0); + QScopedPointer<MyTypeObject> object1_2(qobject_cast<MyTypeObject*>(component1.create())); + QVERIFY(object1_2 != nullptr); QVERIFY(object1->scriptProperty() != object1_2->scriptProperty()); } @@ -2270,8 +2344,8 @@ void tst_qqmllanguage::defaultPropertyListOrder() QQmlComponent component(&engine, testFileUrl("defaultPropertyListOrder.qml")); VERIFY_ERRORS(0); - MyContainer *container = qobject_cast<MyContainer *>(component.create()); - QVERIFY(container != 0); + QScopedPointer<MyContainer> container(qobject_cast<MyContainer *>(component.create())); + QVERIFY(container != nullptr); QCOMPARE(container->getChildren()->count(), 6); QCOMPARE(container->getChildren()->at(0)->property("index"), QVariant(0)); @@ -2291,15 +2365,13 @@ void tst_qqmllanguage::declaredPropertyValues() void tst_qqmllanguage::dontDoubleCallClassBegin() { QQmlComponent component(&engine, testFileUrl("dontDoubleCallClassBegin.qml")); - QObject *o = component.create(); + QScopedPointer<QObject> o(component.create()); QVERIFY(o); MyParserStatus *o2 = qobject_cast<MyParserStatus *>(qvariant_cast<QObject *>(o->property("object"))); QVERIFY(o2); QCOMPARE(o2->classBeginCount(), 1); QCOMPARE(o2->componentCompleteCount(), 1); - - delete o; } void tst_qqmllanguage::reservedWords_data() @@ -2399,10 +2471,9 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q QCOMPARE(actualerror.left(partialMatch ? expectederror.length(): -1),expectederror); } else { VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(QString(object->metaObject()->className()), type); - delete object; } engine.setImportPathList(defaultImportPathList); @@ -2413,10 +2484,9 @@ void tst_qqmllanguage::inlineAssignmentsOverrideBindings() { QQmlComponent component(&engine, testFileUrl("inlineAssignmentsOverrideBindings.qml")); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 11); - delete o; } // QTBUG-19354 @@ -2847,10 +2917,9 @@ void tst_qqmllanguage::importsPath() QTRY_VERIFY(component.isReady()); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toString(), value); - delete object; engine.setImportPathList(defaultImportPathList); } @@ -3058,40 +3127,102 @@ void tst_qqmllanguage::importJs() } if (performTest) { - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(),true); - delete object; } engine.setImportPathList(defaultImportPathList); } +void tst_qqmllanguage::importJsModule_data() +{ + QTest::addColumn<QString>("file"); + + QTest::newRow("plainImport") + << "importJsModule.1.qml"; + + QTest::newRow("ImportQmlStyle") + << "importJsModule.2.qml"; + + QTest::newRow("plainImportWithCycle") + << "importJsModule.3.qml"; +} + +void tst_qqmllanguage::importJsModule() +{ + QFETCH(QString, file); + + engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); + auto importPathGuard = qScopeGuard([this]{ + engine.setImportPathList(defaultImportPathList); + }); + + QQmlComponent component(&engine, testFileUrl(file)); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QCOMPARE(object->property("test").toBool(),true); +} + +void tst_qqmllanguage::explicitSelfImport() +{ + engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); + + QQmlComponent component(&engine, testFileUrl("mixedModuleWithSelfImport.qml")); + QVERIFY(component.errors().count() == 0); + + engine.setImportPathList(defaultImportPathList); +} + +void tst_qqmllanguage::importInternalType() +{ + QQmlEngine engine; + engine.addImportPath(dataDirectory()); + + { + QQmlComponent component(&engine); + component.setData("import modulewithinternaltypes 1.0\nPublicType{}", QUrl()); + VERIFY_ERRORS(0); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVERIFY(obj->property("myInternalType").value<QObject*>() != 0); + } + { + QQmlComponent component(&engine); + component.setData("import modulewithinternaltypes 1.0\nPublicTypeWithExplicitImport{}", QUrl()); + VERIFY_ERRORS(0); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVERIFY(obj->property("myInternalType").value<QObject*>() != 0); + } +} + void tst_qqmllanguage::qmlAttachedPropertiesObjectMethod() { QObject object; - QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(&object, false), (QObject *)0); + QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(&object, false), (QObject *)nullptr); QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(&object, true)); { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.1.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); - QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(object, false), (QObject *)0); - QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object, true) != 0); + QCOMPARE(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), false), (QObject *)nullptr); + QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), true) != nullptr); } { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); - QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object, false) != 0); - QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object, true) != 0); + QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), false) != nullptr); + QVERIFY(qmlAttachedPropertiesObject<MyQmlObject>(object.data(), true) != nullptr); } } @@ -3112,12 +3243,10 @@ void tst_qqmllanguage::customOnProperty() QQmlComponent component(&engine, testFileUrl("customOnProperty.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("on").toInt(), 10); - - delete object; } // QTBUG-12601 @@ -3126,12 +3255,10 @@ void tst_qqmllanguage::variantNotify() QQmlComponent component(&engine, testFileUrl("variantNotify.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("notifyCount").toInt(), 1); - - delete object; } void tst_qqmllanguage::revisions() @@ -3140,38 +3267,32 @@ void tst_qqmllanguage::revisions() QQmlComponent component(&engine, testFileUrl("revisions11.qml")); VERIFY_ERRORS(0); - MyRevisionedClass *object = qobject_cast<MyRevisionedClass*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyRevisionedClass> object(qobject_cast<MyRevisionedClass*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->prop2(), 10.0); - - delete object; } { QQmlEngine myEngine; QQmlComponent component(&myEngine, testFileUrl("revisionssub11.qml")); VERIFY_ERRORS(0); - MyRevisionedSubclass *object = qobject_cast<MyRevisionedSubclass*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyRevisionedSubclass> object(qobject_cast<MyRevisionedSubclass*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->prop1(), 10.0); QCOMPARE(object->prop2(), 10.0); QCOMPARE(object->prop3(), 10.0); QCOMPARE(object->prop4(), 10.0); - - delete object; } { QQmlComponent component(&engine, testFileUrl("versionedbase.qml")); VERIFY_ERRORS(0); - MySubclass *object = qobject_cast<MySubclass*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MySubclass> object(qobject_cast<MySubclass*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->prop1(), 10.0); QCOMPARE(object->prop2(), 10.0); - - delete object; } } @@ -3217,8 +3338,8 @@ void tst_qqmllanguage::subclassedUncreateableRevision() QQmlComponent c(&engine); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); - QCOMPARE(obj, static_cast<QObject*>(0)); + QScopedPointer<QObject> obj(c.create()); + QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Cannot create MyUncreateableBaseClass")); } @@ -3229,17 +3350,16 @@ void tst_qqmllanguage::subclassedUncreateableRevision() if (!shouldWork) QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); if (!shouldWork) { - QCOMPARE(obj, static_cast<QObject*>(0)); + QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); return; } QVERIFY(obj); - MyUncreateableBaseClass *base = qobject_cast<MyUncreateableBaseClass*>(obj); + MyUncreateableBaseClass *base = qobject_cast<MyUncreateableBaseClass*>(obj.data()); QVERIFY(base); QCOMPARE(base->property(prop.toLatin1()).toBool(), true); - delete obj; } void tst_qqmllanguage::subclassedExtendedUncreateableRevision_data() @@ -3273,8 +3393,8 @@ void tst_qqmllanguage::subclassedExtendedUncreateableRevision() QQmlComponent c(&engine); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); - QCOMPARE(obj, static_cast<QObject*>(0)); + QScopedPointer<QObject> obj(c.create()); + QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Cannot create MyExtendedUncreateableBaseClass")); } @@ -3285,17 +3405,16 @@ void tst_qqmllanguage::subclassedExtendedUncreateableRevision() if (!shouldWork) QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); + QScopedPointer<QObject> obj(c.create()); if (!shouldWork) { - QCOMPARE(obj, static_cast<QObject*>(0)); + QCOMPARE(obj.data(), static_cast<QObject*>(nullptr)); return; } QVERIFY(obj); - MyExtendedUncreateableBaseClass *base = qobject_cast<MyExtendedUncreateableBaseClass*>(obj); + MyExtendedUncreateableBaseClass *base = qobject_cast<MyExtendedUncreateableBaseClass*>(obj.data()); QVERIFY(base); QCOMPARE(base->property(prop.toLatin1()).toBool(), true); - delete obj; } void tst_qqmllanguage::uncreatableTypesAsProperties() @@ -3356,12 +3475,10 @@ void tst_qqmllanguage::aliasPropertyChangeSignals() QQmlComponent component(&engine, testFileUrl("aliasPropertyChangeSignals.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); - - delete o; } // QTCREATORBUG-2769 @@ -3369,12 +3486,10 @@ void tst_qqmllanguage::aliasPropertyChangeSignals() QQmlComponent component(&engine, testFileUrl("aliasPropertyChangeSignals.2.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); - - delete o; } } @@ -3385,24 +3500,20 @@ void tst_qqmllanguage::propertyInit() QQmlComponent component(&engine, testFileUrl("propertyInit.1.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 1); - - delete o; } { QQmlComponent component(&engine, testFileUrl("propertyInit.2.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 123); - - delete o; } } @@ -3412,18 +3523,17 @@ void tst_qqmllanguage::registrationOrder() { QQmlComponent component(&engine, testFileUrl("registrationOrder.qml")); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->metaObject(), &MyVersion2Class::staticMetaObject); - delete o; } void tst_qqmllanguage::readonly() { QQmlComponent component(&engine, testFileUrl("readonly.qml")); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 18); @@ -3448,8 +3558,6 @@ void tst_qqmllanguage::readonly() QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 22); QCOMPARE(o->property("test3").toInt(), 2); - - delete o; } void tst_qqmllanguage::readonlyObjectProperties() @@ -3474,8 +3582,8 @@ void tst_qqmllanguage::receivers() { QQmlComponent component(&engine, testFileUrl("receivers.qml")); - MyReceiversTestObject *o = qobject_cast<MyReceiversTestObject*>(component.create()); - QVERIFY(o != 0); + QScopedPointer<MyReceiversTestObject> o(qobject_cast<MyReceiversTestObject*>(component.create())); + QVERIFY(o != nullptr); QCOMPARE(o->mySignalCount(), 1); QCOMPARE(o->propChangedCount(), 2); QCOMPARE(o->myUnconnectedSignalCount(), 0); @@ -3483,8 +3591,6 @@ void tst_qqmllanguage::receivers() QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::mySignal))); QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::propChanged))); QVERIFY(!o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::myUnconnectedSignal))); - - delete o; } void tst_qqmllanguage::registeredCompositeType() @@ -3492,10 +3598,8 @@ void tst_qqmllanguage::registeredCompositeType() QQmlComponent component(&engine, testFileUrl("registeredCompositeType.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); - - delete o; + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); } // QTBUG-43582 @@ -3504,14 +3608,12 @@ void tst_qqmllanguage::registeredCompositeTypeWithEnum() QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithEnum.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("enumValue0").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue0)); QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42)); QCOMPARE(o->property("enumValue15").toInt(), static_cast<int>(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15)); - - delete o; } // QTBUG-43581 @@ -3520,12 +3622,10 @@ void tst_qqmllanguage::registeredCompositeTypeWithAttachedProperty() QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithAttachedProperty.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); QCOMPARE(o->property("attachedProperty").toString(), QStringLiteral("test")); - - delete o; } // QTBUG-18268 @@ -3538,16 +3638,15 @@ void tst_qqmllanguage::remoteLoadCrash() while (component.isLoading()) QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50); - QObject *o = component.create(); - delete o; + QScopedPointer<QObject> o(component.create()); } void tst_qqmllanguage::signalWithDefaultArg() { QQmlComponent component(&engine, testFileUrl("signalWithDefaultArg.qml")); - MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->property("signalCount").toInt(), 0); QCOMPARE(object->property("signalArg").toInt(), 0); @@ -3561,15 +3660,13 @@ void tst_qqmllanguage::signalWithDefaultArg() QCOMPARE(object->property("signalArg").toInt(), 15); - QMetaObject::invokeMethod(object, "emitNoArgSignal"); + QMetaObject::invokeMethod(object.data(), "emitNoArgSignal"); QCOMPARE(object->property("signalCount").toInt(), 3); QCOMPARE(object->property("signalArg").toInt(), 5); - QMetaObject::invokeMethod(object, "emitArgSignal"); + QMetaObject::invokeMethod(object.data(), "emitArgSignal"); QCOMPARE(object->property("signalCount").toInt(), 4); QCOMPARE(object->property("signalArg").toInt(), 22); - - delete object; } void tst_qqmllanguage::signalParameterTypes() @@ -3577,19 +3674,17 @@ void tst_qqmllanguage::signalParameterTypes() // bound signal handlers { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.1.qml")); - QObject *obj = component.create(); - QVERIFY(obj != 0); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); - delete obj; } // dynamic signal connections { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.2.qml")); - QObject *obj = component.create(); - QVERIFY(obj != 0); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); - delete obj; } } @@ -3602,15 +3697,15 @@ void tst_qqmllanguage::globalEnums() QQmlComponent component(&engine, testFileUrl("globalEnums.qml")); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); MyEnum1Class *enum1Class = o->findChild<MyEnum1Class *>(QString::fromLatin1("enum1Class")); - QVERIFY(enum1Class != 0); + QVERIFY(enum1Class != nullptr); QVERIFY(enum1Class->getValue() == -1); MyEnumDerivedClass *enum2Class = o->findChild<MyEnumDerivedClass *>(QString::fromLatin1("enumDerivedClass")); - QVERIFY(enum2Class != 0); + QVERIFY(enum2Class != nullptr); QVERIFY(enum2Class->getValueA() == -1); QVERIFY(enum2Class->getValueB() == -1); QVERIFY(enum2Class->getValueC() == 0); @@ -3628,7 +3723,7 @@ void tst_qqmllanguage::globalEnums() QSignalSpy signalA(enum2Class, SIGNAL(valueAChanged(MyEnum1Class::EnumA))); QSignalSpy signalB(enum2Class, SIGNAL(valueBChanged(MyEnum2Class::EnumB))); - QMetaObject::invokeMethod(o, "setEnumValues"); + QMetaObject::invokeMethod(o.data(), "setEnumValues"); QVERIFY(enum1Class->getValue() == MyEnum1Class::A_13); QVERIFY(enum2Class->getValueA() == MyEnum1Class::A_11); @@ -3647,8 +3742,6 @@ void tst_qqmllanguage::globalEnums() QVERIFY(enum2Class->property("dValue") == 2); QVERIFY(enum2Class->property("eValue") == 14); QVERIFY(enum2Class->property("e2Value") == 76); - - delete o; } void tst_qqmllanguage::lowercaseEnumRuntime_data() @@ -3694,19 +3787,89 @@ void tst_qqmllanguage::scopedEnum() { QQmlComponent component(&engine, testFileUrl("scopedEnum.qml")); - MyTypeObject *o = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(o != 0); + QScopedPointer<MyTypeObject> o(qobject_cast<MyTypeObject *>(component.create())); + QVERIFY(o != nullptr); QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal1); QCOMPARE(o->intProperty(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); QCOMPARE(o->property("listValue").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal3); QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal1); - QMetaObject::invokeMethod(o, "assignNewValue"); + QMetaObject::invokeMethod(o.data(), "assignNewValue"); QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal2); QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); } +void tst_qqmllanguage::scopedEnumsWithNameClash() +{ + auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithNameClash>("ScopedEnumsWithNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); + auto registryGuard = qScopeGuard([typeId]() { + QQmlMetaType::unregisterType(typeId); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("scopedEnumsWithNameClash.qml")); + + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal1"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal2"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Previously registered enum will be overwritten due to name clash: ScopedEnumsWithNameClash.ScopedVal3"); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); + QVERIFY(obj->property("success").toBool()); +} + +void tst_qqmllanguage::scopedEnumsWithResolvedNameClash() +{ + auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithResolvedNameClash>("ScopedEnumsWithResolvedNameClashTest", 1, 0, "ScopedEnum", "Dummy reason"); + auto registryGuard = qScopeGuard([typeId]() { + QQmlMetaType::unregisterType(typeId); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("scopedEnumsWithResolvedNameClash.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != nullptr); + QVERIFY(obj->property("success").toBool()); +} + +void tst_qqmllanguage::qmlEnums() +{ + QQmlEngine engine; + engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); + + { + QQmlComponent component(&engine, testFileUrl("TypeWithEnum.qml")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QCOMPARE(o->property("enumValue").toInt(), 1); + QCOMPARE(o->property("enumValue2").toInt(), 2); + QCOMPARE(o->property("scopedEnumValue").toInt(), 1); + + QCOMPARE(o->property("otherEnumValue1").toInt(), 24); + QCOMPARE(o->property("otherEnumValue2").toInt(), 25); + QCOMPARE(o->property("otherEnumValue3").toInt(), 24); + QCOMPARE(o->property("otherEnumValue4").toInt(), 25); + QCOMPARE(o->property("otherEnumValue5").toInt(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("usingTypeWithEnum.qml")); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QCOMPARE(o->property("enumValue").toInt(), 1); + QCOMPARE(o->property("enumValue2").toInt(), 0); + QCOMPARE(o->property("scopedEnumValue").toInt(), 2); + QCOMPARE(o->property("enumValueFromSingleton").toInt(), 42); + // while this next test verifies current duplication behavior, I'm not sure it should be codified + QCOMPARE(o->property("duplicatedEnumValueFromSingleton").toInt(), 2); + QCOMPARE(o->property("scopedEnumValueFromSingleton1").toInt(), 43); + QCOMPARE(o->property("scopedEnumValueFromSingleton2").toInt(), 2); + QCOMPARE(o->property("scopedEnumValueFromSingleton3").toInt(), 2); + } +} + void tst_qqmllanguage::literals_data() { QTest::addColumn<QString>("property"); @@ -3744,10 +3907,9 @@ void tst_qqmllanguage::literals() QQmlComponent component(&engine, testFile("literals.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property(property.toLatin1()), value); - delete object; } void tst_qqmllanguage::objectDeletionNotify_data() @@ -3766,19 +3928,17 @@ void tst_qqmllanguage::objectDeletionNotify() QQmlComponent component(&engine, testFile(file)); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); - QMetaObject::invokeMethod(object, "destroyObject"); + QMetaObject::invokeMethod(object.data(), "destroyObject"); // Process the deletion event - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QCOMPARE(object->property("success").toBool(), true); - - delete object; } void tst_qqmllanguage::scopedProperties() @@ -3786,7 +3946,7 @@ void tst_qqmllanguage::scopedProperties() QQmlComponent component(&engine, testFile("scopedProperties.qml")); QScopedPointer<QObject> o(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QVERIFY(o->property("success").toBool()); } @@ -3794,7 +3954,7 @@ void tst_qqmllanguage::deepProperty() { QQmlComponent component(&engine, testFile("deepProperty.qml")); QScopedPointer<QObject> o(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); QFont font = qvariant_cast<QFont>(qvariant_cast<QObject*>(o->property("someObject"))->property("font")); QCOMPARE(font.family(), QStringLiteral("test")); } @@ -3812,11 +3972,11 @@ void tst_qqmllanguage::implicitImportsLast() QQmlComponent component(&engine, testFile("localOrderTest.qml")); VERIFY_ERRORS(0); - QObject *object = qobject_cast<QObject *>(component.create()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea"))); QObject* object2 = object->property("item").value<QObject*>(); - QVERIFY(object2 != 0); + QVERIFY(object2 != nullptr); QCOMPARE(QString(object2->metaObject()->className()), QLatin1String("QQuickRectangle")); engine.setImportPathList(defaultImportPathList); @@ -3824,36 +3984,38 @@ void tst_qqmllanguage::implicitImportsLast() void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* fileName, const char* propertyName, QObject** result /* out */) { - QVERIFY(fileName != 0); - QVERIFY(propertyName != 0); + QVERIFY(fileName != nullptr); + QVERIFY(propertyName != nullptr); if (!fileName || !propertyName) return; QQmlComponent component(&engine, testFile(fileName)); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); - getSingletonInstance(object, propertyName, result); + getSingletonInstance(object.data(), propertyName, result); } void tst_qqmllanguage::getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */) { - QVERIFY(o != 0); - QVERIFY(propertyName != 0); + QVERIFY(o != nullptr); + QVERIFY(propertyName != nullptr); if (!o || !propertyName) return; QVariant variant = o->property(propertyName); - QVERIFY(variant.userType() == qMetaTypeId<QObject *>()); + QVERIFY(variant.isValid()); - QObject *singleton = NULL; - if (variant.canConvert<QObject*>()) + QObject *singleton = nullptr; + if (variant.userType() == qMetaTypeId<QObject *>()) singleton = variant.value<QObject*>(); + else if (variant.userType() == qMetaTypeId<QJSValue>()) + singleton = variant.value<QJSValue>().toQObject(); - QVERIFY(singleton != 0); + QVERIFY(singleton != nullptr); *result = singleton; } @@ -3872,24 +4034,24 @@ void tst_qqmllanguage::compositeSingletonProperties() { QQmlComponent component(&engine, testFile("singletonTest1.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); } // Checks that the addresses of the composite singletons used in the same // engine are the same. void tst_qqmllanguage::compositeSingletonSameEngine() { - QObject* s1 = NULL; + QObject* s1 = nullptr; getSingletonInstance(engine, "singletonTest2.qml", "singleton1", &s1); - QVERIFY(s1 != 0); + QVERIFY(s1 != nullptr); s1->setProperty("testProp2", QVariant(13)); - QObject* s2 = NULL; + QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest3.qml", "singleton2", &s2); - QVERIFY(s2 != 0); + QVERIFY(s2 != nullptr); QCOMPARE(s2->property("testProp2"), QVariant(13)); QCOMPARE(s1, s2); @@ -3901,14 +4063,14 @@ void tst_qqmllanguage::compositeSingletonDifferentEngine() { QQmlEngine e2; - QObject* s1 = NULL; + QObject* s1 = nullptr; getSingletonInstance(engine, "singletonTest2.qml", "singleton1", &s1); - QVERIFY(s1 != 0); + QVERIFY(s1 != nullptr); s1->setProperty("testProp2", QVariant(13)); - QObject* s2 = NULL; + QObject* s2 = nullptr; getSingletonInstance(e2, "singletonTest3.qml", "singleton2", &s2); - QVERIFY(s2 != 0); + QVERIFY(s2 != nullptr); QCOMPARE(s2->property("testProp2"), QVariant(25)); QVERIFY(s1 != s2); @@ -3926,20 +4088,20 @@ void tst_qqmllanguage::compositeSingletonQualifiedNamespace() { QQmlComponent component(&engine, testFile("singletonTest5.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); // lets verify that the singleton instance we are using is the same // when loaded through another file (without namespace!) - QObject *s1 = NULL; - getSingletonInstance(o, "singletonInstance", &s1); - QVERIFY(s1 != 0); + QObject *s1 = nullptr; + getSingletonInstance(o.data(), "singletonInstance", &s1); + QVERIFY(s1 != nullptr); - QObject* s2 = NULL; + QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest5a.qml", "singletonInstance", &s2); - QVERIFY(s2 != 0); + QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } @@ -3951,21 +4113,21 @@ void tst_qqmllanguage::compositeSingletonModule() QQmlComponent component(&engine, testFile("singletonTest6.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55); - verifyCompositeSingletonPropertyValues(o, "value3", 125, "value4", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value3", 125, "value4", -55); // lets verify that the singleton instance we are using is the same // when loaded through another file - QObject *s1 = NULL; - getSingletonInstance(o, "singletonInstance", &s1); - QVERIFY(s1 != 0); + QObject *s1 = nullptr; + getSingletonInstance(o.data(), "singletonInstance", &s1); + QVERIFY(s1 != nullptr); - QObject* s2 = NULL; + QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest6a.qml", "singletonInstance", &s2); - QVERIFY(s2 != 0); + QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } @@ -3977,21 +4139,21 @@ void tst_qqmllanguage::compositeSingletonModuleVersioned() QQmlComponent component(&engine, testFile("singletonTest7.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 225, "value2", 55); - verifyCompositeSingletonPropertyValues(o, "value3", 225, "value4", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 225, "value2", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value3", 225, "value4", 55); // lets verify that the singleton instance we are using is the same // when loaded through another file - QObject *s1 = NULL; - getSingletonInstance(o, "singletonInstance", &s1); - QVERIFY(s1 != 0); + QObject *s1 = nullptr; + getSingletonInstance(o.data(), "singletonInstance", &s1); + QVERIFY(s1 != nullptr); - QObject* s2 = NULL; + QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest7a.qml", "singletonInstance", &s2); - QVERIFY(s2 != 0); + QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } @@ -4003,21 +4165,21 @@ void tst_qqmllanguage::compositeSingletonModuleQualified() QQmlComponent component(&engine, testFile("singletonTest8.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 225, "value2", 55); - verifyCompositeSingletonPropertyValues(o, "value3", 225, "value4", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 225, "value2", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value3", 225, "value4", 55); // lets verify that the singleton instance we are using is the same // when loaded through another file - QObject *s1 = NULL; - getSingletonInstance(o, "singletonInstance", &s1); - QVERIFY(s1 != 0); + QObject *s1 = nullptr; + getSingletonInstance(o.data(), "singletonInstance", &s1); + QVERIFY(s1 != nullptr); - QObject* s2 = NULL; + QObject* s2 = nullptr; getSingletonInstance(engine, "singletonTest8a.qml", "singletonInstance", &s2); - QVERIFY(s2 != 0); + QVERIFY(s2 != nullptr); QCOMPARE(s1, s2); } @@ -4042,10 +4204,10 @@ void tst_qqmllanguage::compositeSingletonDynamicSignal() { QQmlComponent component(&engine, testFile("singletonTest11.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 99, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55); } // Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it. @@ -4091,10 +4253,10 @@ void tst_qqmllanguage::compositeSingletonRemote() QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 525, "value2", 355); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 525, "value2", 355); } // Load a composite singleton type and a javascript file that has .pragma library @@ -4104,14 +4266,14 @@ void tst_qqmllanguage::compositeSingletonJavaScriptPragma() { QQmlComponent component(&engine, testFile("singletonTest16.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); // The value1 that is read from the SingletonType was changed from 125 to 99 // in compositeSingletonDynamicSignal() above. As the type is a singleton and // the engine has not been destroyed, we just retrieve the old instance and // the value is still 99. - verifyCompositeSingletonPropertyValues(o, "value1", 99, "value2", 333); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333); } // Reads values from a Singleton accessed through selectors. @@ -4122,10 +4284,10 @@ void tst_qqmllanguage::compositeSingletonSelectors() qmlSelector.setExtraSelectors(QStringList() << "basicSelector"); QQmlComponent component(&e2, testFile("singletonTest1.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 625, "value2", 455); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 625, "value2", 455); } // Reads values from a Singleton that was registered through the C++ API: @@ -4134,10 +4296,44 @@ void tst_qqmllanguage::compositeSingletonRegistered() { QQmlComponent component(&engine, testFile("singletonTest17.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); + + verifyCompositeSingletonPropertyValues(o.data(), "value1", 925, "value2", 755); +} + +void tst_qqmllanguage::compositeSingletonCircular() +{ + QQmlComponent component(&engine, testFile("circularSingleton.qml")); + VERIFY_ERRORS(0); + + QQmlTestMessageHandler messageHandler; + + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); + + // ensure we aren't hitting the recursion warning + QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); + + QCOMPARE(o->property("value").toInt(), 2); +} + +void tst_qqmllanguage::singletonsHaveContextAndEngine() +{ + QObject *qmlSingleton = nullptr; + getSingletonInstance(engine, "singletonTest18.qml", "qmlSingleton", &qmlSingleton); + QVERIFY(qmlContext(qmlSingleton)); + QCOMPARE(qmlEngine(qmlSingleton), &engine); - verifyCompositeSingletonPropertyValues(o, "value1", 925, "value2", 755); + QObject *jsSingleton = nullptr; + getSingletonInstance(engine, "singletonTest18.qml", "jsSingleton", &jsSingleton); + QVERIFY(qmlContext(jsSingleton)); + QCOMPARE(qmlEngine(jsSingleton), &engine); + + QObject *cppSingleton = nullptr; + getSingletonInstance(engine, "singletonTest18.qml", "cppSingleton", &cppSingleton); + QVERIFY(qmlContext(cppSingleton)); + QCOMPARE(qmlEngine(cppSingleton), &engine); } void tst_qqmllanguage::customParserBindingScopes() @@ -4219,7 +4415,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects() QVERIFY(ddata); QQmlPropertyCache *subCache = ddata->propertyCache; QVERIFY(subCache); - QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/0, /*context*/0); + QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/nullptr, /*context*/nullptr); QVERIFY(pd); QCOMPARE(pd->propType(), qMetaTypeId<int>()); } @@ -4269,13 +4465,225 @@ void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() QVERIFY(!ddata->rootObjectInCreation); } +// QTBUG-63036 +void tst_qqmllanguage::lazyDeferredSubObject() +{ + QQmlComponent component(&engine, testFile("lazyDeferredSubObject.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObject *subObject = qvariant_cast<QObject *>(object->property("subObject")); + QVERIFY(subObject); + + QCOMPARE(object->objectName(), QStringLiteral("custom")); + QCOMPARE(subObject->objectName(), QStringLiteral("custom")); +} + +// QTBUG-63200 +void tst_qqmllanguage::deferredProperties() +{ + QQmlComponent component(&engine, testFile("deferredProperties.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObject *innerObj = object->findChild<QObject *>(QStringLiteral("innerobj")); + QVERIFY(!innerObj); + + QObject *outerObj = object->findChild<QObject *>(QStringLiteral("outerobj")); + QVERIFY(!outerObj); + + QObject *groupProperty = object->property("groupProperty").value<QObject *>(); + QVERIFY(!groupProperty); + + QQmlListProperty<QObject> listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>(); + QCOMPARE(listProperty.count(&listProperty), 0); + + QQmlData *qmlData = QQmlData::get(object.data()); + QVERIFY(qmlData); + + QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml + QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2" + QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2" + + qmlExecuteDeferred(object.data()); + + QCOMPARE(qmlData->deferredData.count(), 0); + + innerObj = object->findChild<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml + QVERIFY(innerObj); + QCOMPARE(innerObj->property("wasCompleted"), QVariant(true)); + + outerObj = object->findChild<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml + QVERIFY(outerObj); + QCOMPARE(outerObj->property("wasCompleted"), QVariant(true)); + + groupProperty = object->property("groupProperty").value<QObject *>(); + QCOMPARE(groupProperty, outerObj); + + listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>(); + QCOMPARE(listProperty.count(&listProperty), 4); + + QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("innerlist1")); // MyDeferredListProperty.qml + QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true)); + QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("innerlist2")); // MyDeferredListProperty.qml + QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); + + QCOMPARE(listProperty.at(&listProperty, 2)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml + QCOMPARE(listProperty.at(&listProperty, 2)->property("wasCompleted"), QVariant(true)); + QCOMPARE(listProperty.at(&listProperty, 3)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml + QCOMPARE(listProperty.at(&listProperty, 3)->property("wasCompleted"), QVariant(true)); +} + +static void beginDeferredOnce(QQmlEnginePrivate *enginePriv, + const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState) +{ + QObject *object = property.object(); + QQmlData *ddata = QQmlData::get(object); + Q_ASSERT(!ddata->deferredData.isEmpty()); + + int propertyIndex = property.index(); + + for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) { + QQmlData::DeferredData *deferData = *dit; + + auto range = deferData->bindings.equal_range(propertyIndex); + if (range.first == deferData->bindings.end()) + continue; + + QQmlComponentPrivate::ConstructionState *state = new QQmlComponentPrivate::ConstructionState; + state->completePending = true; + + QQmlContextData *creationContext = nullptr; + state->creator.reset(new QQmlObjectCreator(deferData->context->parent, deferData->compilationUnit, creationContext)); + + enginePriv->inProgressCreations++; + + 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); + while (it != last) { + if (!state->creator->populateDeferredBinding(property, deferData, *it)) + state->errors << state->creator->errors; + ++it; + } + + deferredState->constructionStates += state; + + // Cleanup any remaining deferred bindings for this property, also in inner contexts, + // to avoid executing them later and overriding the property that was just populated. + while (dit != ddata->deferredData.rend()) { + (*dit)->bindings.remove(propertyIndex); + ++dit; + } + break; + } +} + +static void testExecuteDeferredOnce(const QQmlProperty &property) +{ + QObject *object = property.object(); + QQmlData *data = QQmlData::get(object); + if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); + + QQmlComponentPrivate::DeferredState state; + beginDeferredOnce(ep, property, &state); + + // Release deferred data for those compilation units that no longer have deferred bindings + data->releaseDeferredData(); + + QQmlComponentPrivate::completeDeferred(ep, &state); + } +} + +void tst_qqmllanguage::executeDeferredPropertiesOnce() +{ + QQmlComponent component(&engine, testFile("deferredProperties.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectList innerObjsAtCreation = object->findChildren<QObject *>(QStringLiteral("innerobj")); + QVERIFY(innerObjsAtCreation.isEmpty()); + + QObjectList outerObjsAtCreation = object->findChildren<QObject *>(QStringLiteral("outerobj")); + QVERIFY(outerObjsAtCreation.isEmpty()); + + QObject *groupProperty = object->property("groupProperty").value<QObject *>(); + QVERIFY(!groupProperty); + + QQmlListProperty<QObject> listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>(); + QCOMPARE(listProperty.count(&listProperty), 0); + + QQmlData *qmlData = QQmlData::get(object.data()); + QVERIFY(qmlData); + + QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml + QCOMPARE(qmlData->deferredData.first()->bindings.count(), 3); // "innerobj", "innerlist1", "innerlist2" + QCOMPARE(qmlData->deferredData.last()->bindings.count(), 3); // "outerobj", "outerlist1", "outerlist2" + + // first execution creates the outer object + testExecuteDeferredOnce(QQmlProperty(object.data(), "groupProperty")); + + QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml + QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2" + QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2" + + QObjectList innerObjsAfterFirstExecute = object->findChildren<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml + QVERIFY(innerObjsAfterFirstExecute.isEmpty()); + + QObjectList outerObjsAfterFirstExecute = object->findChildren<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml + QCOMPARE(outerObjsAfterFirstExecute.count(), 1); + QCOMPARE(outerObjsAfterFirstExecute.first()->property("wasCompleted"), QVariant(true)); + + groupProperty = object->property("groupProperty").value<QObject *>(); + QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first()); + + listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>(); + QCOMPARE(listProperty.count(&listProperty), 0); + + // re-execution does nothing (to avoid overriding the property) + testExecuteDeferredOnce(QQmlProperty(object.data(), "groupProperty")); + + QCOMPARE(qmlData->deferredData.count(), 2); // MyDeferredListProperty.qml + deferredListProperty.qml + QCOMPARE(qmlData->deferredData.first()->bindings.count(), 2); // "innerlist1", "innerlist2" + QCOMPARE(qmlData->deferredData.last()->bindings.count(), 2); // "outerlist1", "outerlist2" + + QObjectList innerObjsAfterSecondExecute = object->findChildren<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml + QVERIFY(innerObjsAfterSecondExecute.isEmpty()); + + QObjectList outerObjsAfterSecondExecute = object->findChildren<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml + QCOMPARE(outerObjsAfterFirstExecute, outerObjsAfterSecondExecute); + + groupProperty = object->property("groupProperty").value<QObject *>(); + QCOMPARE(groupProperty, outerObjsAfterFirstExecute.first()); + + listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>(); + QCOMPARE(listProperty.count(&listProperty), 0); + + // execution of a list property should execute all outer list bindings + testExecuteDeferredOnce(QQmlProperty(object.data(), "listProperty")); + + QCOMPARE(qmlData->deferredData.count(), 0); + + listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>(); + QCOMPARE(listProperty.count(&listProperty), 2); + + QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml + QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true)); + QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml + QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true)); +} + void tst_qqmllanguage::noChildEvents() { QQmlComponent component(&engine); component.setData("import QtQml 2.0; import Test 1.0; MyQmlObject { property QtObject child: QtObject {} }", QUrl()); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); - QVERIFY(object != 0); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create())); + QVERIFY(object != nullptr); QCOMPARE(object->childAddedEventCount(), 0); } @@ -4294,15 +4702,15 @@ void tst_qqmllanguage::deleteSingletons() QQmlEngine tmpEngine; QQmlComponent component(&tmpEngine, testFile("singletonTest5.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); - QVERIFY(o != 0); - QObject *s1 = NULL; - getSingletonInstance(o, "singletonInstance", &s1); - QVERIFY(s1 != 0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != nullptr); + QObject *s1 = nullptr; + getSingletonInstance(o.data(), "singletonInstance", &s1); + QVERIFY(s1 != nullptr); singleton = s1; - QVERIFY(singleton.data() != 0); + QVERIFY(singleton.data() != nullptr); } - QVERIFY(singleton.data() == 0); + QVERIFY(singleton.data() == nullptr); } void tst_qqmllanguage::arrayBuffer_data() @@ -4321,8 +4729,8 @@ void tst_qqmllanguage::arrayBuffer() QFETCH(QString, file); QQmlComponent component(&engine, testFile(file)); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); QCOMPARE(object->property("ok").toBool(), true); } @@ -4541,9 +4949,9 @@ void tst_qqmllanguage::instanceof() VERIFY_ERRORS(0); QScopedPointer<QObject> o(component.create()); - QVERIFY(o != 0); + QVERIFY(o != nullptr); - QQmlExpression expr(engine.contextForObject(o.data()), 0, QString::fromLatin1(QTest::currentDataTag())); + QQmlExpression expr(engine.contextForObject(o.data()), nullptr, QString::fromLatin1(QTest::currentDataTag())); QVariant ret = expr.evaluate(); if (expectedValue.type() == QVariant::Bool) { @@ -4575,6 +4983,92 @@ void tst_qqmllanguage::concurrentLoadQmlDir() engine.setImportPathList(defaultImportPathList); } +// Test that deleting an object and then accessing it doesn't crash. +// QTBUG-44153 +class ObjectCreator : public QObject +{ + Q_OBJECT +public slots: + QObject *create() { return (new ObjectCreator); } + void del() { delete this; } +}; + +void tst_qqmllanguage::accessDeletedObject() +{ + QQmlEngine engine; + + engine.rootContext()->setContextProperty("objectCreator", new ObjectCreator); + QQmlComponent component(&engine, testFileUrl("accessDeletedObject.qml")); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::lowercaseTypeNames() +{ + QCOMPARE(qmlRegisterType<QObject>("Test", 1, 0, "lowerCaseTypeName"), -1); + QCOMPARE(qmlRegisterSingletonType<QObject>("Test", 1, 0, "lowerCaseTypeName", nullptr), -1); +} + +void tst_qqmllanguage::thisInQmlScope() +{ + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("thisInQmlScope.qml")); + QTRY_VERIFY(component.isReady()); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("x"), QVariant(42)); + QCOMPARE(o->property("y"), QVariant(42)); +} + +void tst_qqmllanguage::valueTypeGroupPropertiesInBehavior() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("groupPropertyInPropertyValueSource.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QObject *animation = qmlContext(o.data())->contextProperty("animation").value<QObject*>(); + QVERIFY(animation); + + QCOMPARE(animation->property("easing").value<QEasingCurve>().type(), QEasingCurve::InOutQuad); +} + +void tst_qqmllanguage::retrieveQmlTypeId() +{ + // Register in reverse order to provoke wrong minor version matching. + int id2 = qmlRegisterType<QObject>("Test", 2, 3, "SomeTestType"); + int id1 = qmlRegisterType<QObject>("Test", 2, 1, "SomeTestType"); + QCOMPARE(qmlTypeId("Test", 2, 1, "SomeTestType"), id1); + QCOMPARE(qmlTypeId("Test", 2, 2, "SomeTestType"), id1); + QCOMPARE(qmlTypeId("Test", 2, 3, "SomeTestType"), id2); + + // Error cases + QCOMPARE(qmlTypeId("Test", 2, 0, "SomeTestType"), -1); + QCOMPARE(qmlTypeId("Test", 2, 3, "DoesNotExist"), -1); + QCOMPARE(qmlTypeId("DoesNotExist", 2, 3, "SomeTestType"), -1); + + // Must also work for other types (defined in testtpes.cpp) + QVERIFY(qmlTypeId("Test", 1, 0, "MyExtendedUncreateableBaseClass") >= 0); + QVERIFY(qmlTypeId("Test", 1, 0, "MyUncreateableBaseClass") >= 0); + QVERIFY(qmlTypeId("Test", 1, 0, "MyTypeObjectSingleton") >= 0); +} + +void tst_qqmllanguage::polymorphicFunctionLookup() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("polymorphicFunctionLookup.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QVERIFY(o->property("ok").toBool()); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp index 513bea2e16..b49832499e 100644 --- a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp +++ b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp @@ -45,13 +45,13 @@ struct Range template <typename T> struct Array { - Array() : array(0), count(0) {} + Array() : array(nullptr) {} template<int N> Array(const T (&array)[N]) : array(array), count(N) {} T operator [](int index) const { return array[index]; } const T *array; - int count; + int count = 0; }; typedef Array<int> IndexArray; @@ -181,7 +181,7 @@ void tst_qqmllistcompositor::find_data() << (RangeList() << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) - << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) + << Range(nullptr, 0, 1, int(VisibleFlag| C::CacheFlag))) << C::Cache << 2 << Selection << 0 << 0 << 0 << 0 << 0 @@ -238,7 +238,7 @@ void tst_qqmllistcompositor::findInsertPosition_data() << (RangeList() << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) - << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) + << Range(nullptr, 0, 1, int(VisibleFlag| C::CacheFlag))) << Selection << 0 << 0 << 0 << 0 << 0 << uint(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) << 0; @@ -246,7 +246,7 @@ void tst_qqmllistcompositor::findInsertPosition_data() << (RangeList() << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) - << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) + << Range(nullptr, 0, 1, int(VisibleFlag| C::CacheFlag))) << Selection << 1 << 1 << 1 << 1 << 3 << uint(0) << 0; @@ -390,17 +390,17 @@ void tst_qqmllistcompositor::clearFlags_data() int listA; void *a = &listA; { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int visibleIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; QTest::newRow("Default, 2, 2, Selection") << (RangeList() << Range(a, 0, 12, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << Range(nullptr, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) << C::Default << 2 << 2 << int(SelectionFlag) << (RemoveList() << Remove(2, 2, 2, 2, 2, SelectionFlag | C::CacheFlag)) @@ -409,19 +409,19 @@ void tst_qqmllistcompositor::clearFlags_data() << IndexArray(visibleIndexes) << ListArray(visibleLists) << IndexArray(selectionIndexes) << ListArray(selectionLists); } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; QTest::newRow("Selection, 1, 2, Visible") << (RangeList() << Range(a, 0, 2, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 4, 8, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << Range(nullptr, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) << Selection << 1 << 2 << int(VisibleFlag) << (RemoveList() << Remove(1, 1, 1, 1, 1, VisibleFlag | C::CacheFlag) @@ -431,13 +431,13 @@ void tst_qqmllistcompositor::clearFlags_data() << IndexArray(visibleIndexes) << ListArray(visibleLists) << IndexArray(selectionIndexes) << ListArray(selectionLists); } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr}; static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr}; static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr}; QTest::newRow("Default, 13, 1, Prepend | Selection | Visible | Default") << (RangeList() << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) @@ -445,7 +445,7 @@ void tst_qqmllistcompositor::clearFlags_data() << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 5, 7, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << Range(nullptr, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) << C::Default << 13 << 1 << int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag) << (RemoveList() << Remove(11, 11, 13, 13, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) @@ -454,13 +454,13 @@ void tst_qqmllistcompositor::clearFlags_data() << IndexArray(visibleIndexes) << ListArray(visibleLists) << IndexArray(selectionIndexes) << ListArray(selectionLists); } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr}; static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr}; static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,nullptr,nullptr,nullptr}; QTest::newRow("Cache, 11, 4, Cache") << (RangeList() << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) @@ -468,8 +468,8 @@ void tst_qqmllistcompositor::clearFlags_data() << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 5, 7, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 1, int(C::CacheFlag)) - << Range(0, 0, 3, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << Range(nullptr, 0, 1, int(C::CacheFlag)) + << Range(nullptr, 0, 3, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) << C::Cache << 11 << 4 << int(C::CacheFlag) << (RemoveList()) << IndexArray(cacheIndexes) << ListArray(cacheLists) @@ -477,13 +477,13 @@ void tst_qqmllistcompositor::clearFlags_data() << IndexArray(visibleIndexes) << ListArray(visibleLists) << IndexArray(selectionIndexes) << ListArray(selectionLists); } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a,nullptr}; static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a,nullptr}; static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a,nullptr}; QTest::newRow("Default, 11, 3, Default | Visible | Selection") << (RangeList() << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) @@ -492,8 +492,8 @@ void tst_qqmllistcompositor::clearFlags_data() << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 5, 6, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) << Range(a, 11, 1, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag)) - << Range(0, 0, 2, int(SelectionFlag | VisibleFlag | C::DefaultFlag)) - << Range(0, 0, 1, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << Range(nullptr, 0, 2, int(SelectionFlag | VisibleFlag | C::DefaultFlag)) + << Range(nullptr, 0, 1, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) << C::Default << 11 << 3 << int(C::DefaultFlag | VisibleFlag| SelectionFlag) << (RemoveList() << Remove(9, 9, 11, 11, 1, SelectionFlag | VisibleFlag | C::DefaultFlag) @@ -584,13 +584,13 @@ void tst_qqmllistcompositor::setFlags_data() int listA; void *a = &listA; { static const int cacheIndexes[] = {0,0,0,0}; - static const void *cacheLists[] = {0,0,0,0}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; QTest::newRow("Default, 2, 2, Default") << (RangeList() << Range(a, 0, 12, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) + << Range(nullptr, 0, 4, C::CacheFlag)) << C::Default << 2 << 2 << int(C::DefaultFlag) << (InsertList()) << IndexArray(cacheIndexes) << ListArray(cacheLists) @@ -598,7 +598,7 @@ void tst_qqmllistcompositor::setFlags_data() << IndexArray() << ListArray() << IndexArray() << ListArray(); } { static const int cacheIndexes[] = {0,0,0,0}; - static const void *cacheLists[] = {0,0,0,0}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; static const int visibleIndexes[] = {2,3}; @@ -606,7 +606,7 @@ void tst_qqmllistcompositor::setFlags_data() QTest::newRow("Default, 2, 2, Visible") << (RangeList() << Range(a, 0, 12, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) + << Range(nullptr, 0, 4, C::CacheFlag)) << C::Default << 2 << 2 << int(VisibleFlag) << (InsertList() << Insert(0, 0, 2, 0, 2, VisibleFlag)) @@ -615,7 +615,7 @@ void tst_qqmllistcompositor::setFlags_data() << IndexArray(visibleIndexes) << ListArray(visibleLists) << IndexArray() << ListArray(); } { static const int cacheIndexes[] = {3,6,0,0,0,0}; - static const void *cacheLists[] = {a,a,0,0,0,0}; + static const void *cacheLists[] = {a,a,nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; static const int visibleIndexes[] = {2,3,6,7}; @@ -629,7 +629,7 @@ void tst_qqmllistcompositor::setFlags_data() << Range(a, 4, 2, C::DefaultFlag) << Range(a, 6, 2, VisibleFlag | C::DefaultFlag) << Range(a, 8, 4, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) + << Range(nullptr, 0, 4, C::CacheFlag)) << Visible << 1 << 2 << int(SelectionFlag | C::CacheFlag) << (InsertList() << Insert(0, 1, 3, 0, 1, SelectionFlag | C::CacheFlag) @@ -639,11 +639,11 @@ void tst_qqmllistcompositor::setFlags_data() << IndexArray(visibleIndexes) << ListArray(visibleLists) << IndexArray(selectionIndexes) << ListArray(selectionLists); } { static const int cacheIndexes[] = {3,6,0,0,0,0}; - static const void *cacheLists[] = {a,a,0,0,0,0}; + static const void *cacheLists[] = {a,a,nullptr,nullptr,nullptr,nullptr}; static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; static const int visibleIndexes[] = {2,3,6,7,0}; - static const void *visibleLists[] = {a,a,a,a,0}; + static const void *visibleLists[] = {a,a,a,a,nullptr}; static const int selectionIndexes[] = {3,6}; static const void *selectionLists[] = {a,a}; QTest::newRow("Cache, 3, 1, Visible") @@ -655,7 +655,7 @@ void tst_qqmllistcompositor::setFlags_data() << Range(a, 6, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) << Range(a, 7, 1, VisibleFlag | C::DefaultFlag) << Range(a, 8, 4, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) + << Range(nullptr, 0, 4, C::CacheFlag)) << C::Cache << 3 << 1 << int(VisibleFlag) << (InsertList() << Insert(2, 4, 12, 3, 1, VisibleFlag | C::CacheFlag)) @@ -770,14 +770,14 @@ void tst_qqmllistcompositor::move_data() int listC; void *c = &listC; { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr,c,c}; static const int defaultIndexes[] = {0,0,1,2,3,4,5,0,1,2,3,4,5,1,2,3,0,1,2,3,4,5}; - static const void *defaultLists[] = {0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,0,c,c,c,c,c,c}; + static const void *defaultLists[] = {nullptr,a,a,a,a,a,a,b,b,b,b,b,b,nullptr,nullptr,nullptr,c,c,c,c,c,c}; QTest::newRow("15, 0, 1") << (RangeList() << Range(a, 0, 6, C::DefaultFlag) << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 4, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 4, C::DefaultFlag | C::CacheFlag) << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) @@ -791,15 +791,15 @@ void tst_qqmllistcompositor::move_data() << IndexArray() << ListArray() << IndexArray() << ListArray(); } { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr,c,c}; static const int defaultIndexes[] = {0,1,0,1,2,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5}; - static const void *defaultLists[] = {0,0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c}; + static const void *defaultLists[] = {nullptr,nullptr,a,a,a,a,a,a,b,b,b,b,b,b,nullptr,nullptr,c,c,c,c,c,c}; QTest::newRow("15, 1, 1") << (RangeList() - << Range(0, 0, 1, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 1, C::DefaultFlag | C::CacheFlag) << Range(a, 0, 6, C::DefaultFlag) << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 3, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 3, C::DefaultFlag | C::CacheFlag) << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) @@ -813,15 +813,15 @@ void tst_qqmllistcompositor::move_data() << IndexArray() << ListArray() << IndexArray() << ListArray(); } { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr,c,c}; static const int defaultIndexes[] = {0,1,2,0,1,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5}; - static const void *defaultLists[] = {a,a,a,0,0,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c}; + static const void *defaultLists[] = {a,a,a,nullptr,nullptr,a,a,a,b,b,b,b,b,b,nullptr,nullptr,c,c,c,c,c,c}; QTest::newRow("0, 3, 2") << (RangeList() - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 2, C::DefaultFlag | C::CacheFlag) << Range(a, 0, 6, C::DefaultFlag) << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 2, C::DefaultFlag | C::CacheFlag) << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) @@ -835,16 +835,16 @@ void tst_qqmllistcompositor::move_data() << IndexArray() << ListArray() << IndexArray() << ListArray(); } { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr,c,c}; static const int defaultIndexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,1,2,3,4,5}; - static const void *defaultLists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c}; + static const void *defaultLists[] = {a,a,b,b,b,b,b,b,nullptr,nullptr,c,a,a,nullptr,nullptr,a,a,c,c,c,c,c}; QTest::newRow("7, 1, 10") << (RangeList() << Range(a, 0, 3, C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 2, C::DefaultFlag | C::CacheFlag) << Range(a, 3, 3, C::DefaultFlag) << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 2, C::DefaultFlag | C::CacheFlag) << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) @@ -864,18 +864,18 @@ void tst_qqmllistcompositor::move_data() << IndexArray() << ListArray() << IndexArray() << ListArray(); } { static const int cacheIndexes[] = {0,0,0,0,3,2}; - static const void *cacheLists[] = {0,0,0,0,c,c}; + static const void *cacheLists[] = {nullptr,nullptr,nullptr,nullptr,c,c}; static const int defaultIndexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,3,4,5,1,2}; - static const void *defaultLists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c}; + static const void *defaultLists[] = {a,a,b,b,b,b,b,b,nullptr,nullptr,c,a,a,nullptr,nullptr,a,a,c,c,c,c,c}; QTest::newRow("17, 20, 2") << (RangeList() << Range(a, 0, 1, C::DefaultFlag) << Range(a, 5, 1, C::DefaultFlag) << Range(b, 0, 6, C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 2, C::DefaultFlag | C::CacheFlag) << Range(c, 0, 1, C::DefaultFlag) << Range(a, 1, 2, C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(nullptr, 0, 2, C::DefaultFlag | C::CacheFlag) << Range(a, 3, 2, C::DefaultFlag) << Range(b, 0, 6, C::AppendFlag | C::PrependFlag) << Range(c, 0, 1, C::PrependFlag) @@ -1072,7 +1072,7 @@ void tst_qqmllistcompositor::clear() compositor.append(a, 0, 8, C::AppendFlag | C::PrependFlag | VisibleFlag | C::DefaultFlag); compositor.append(b, 4, 5, VisibleFlag | C::DefaultFlag); - compositor.append(0, 0, 3, VisibleFlag | C::DefaultFlag | C::CacheFlag); + compositor.append(nullptr, 0, 3, VisibleFlag | C::DefaultFlag | C::CacheFlag); QCOMPARE(compositor.count(C::Default), 16); QCOMPARE(compositor.count(Visible), 16); diff --git a/tests/auto/qml/qqmllistmodel/data/dynamicroles.qml b/tests/auto/qml/qqmllistmodel/data/dynamicroles.qml new file mode 100644 index 0000000000..7d3650c3b9 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/dynamicroles.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +Item { + id: root + + ListModel { + id: listModel + objectName: "listModel" + dynamicRoles: true + + // have to add elements dynamically when dynamicRoles = true + function appendNewElement() { + listModel.append({"name": "test", "obj": null}) + } + + function setElementAgain() { + var element = listModel.get(0) + listModel.set(0, element) + } + } +} diff --git a/tests/auto/qml/qqmllistmodel/data/qtbug38907.qml b/tests/auto/qml/qqmllistmodel/data/qtbug38907.qml new file mode 100644 index 0000000000..0abf221f60 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/qtbug38907.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 +import QtTest 1.0 + +Item { + + Item { + id : testItem + property string name : "testObject" + property var object : this + function testMethod() { + return -1; + } + } + + ListModel { + id : listModel + dynamicRoles : true + } + + function exec() { + listModel.append(testItem); + listModel.append({ item : testItem }); + return true; + } +} diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index e442dd1421..771f3e5c4e 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -49,7 +49,7 @@ Q_DECLARE_METATYPE(QList<QVariantHash>) inline QVariant runexpr(QQmlEngine *engine, const QString &str) { - QQmlExpression expr(engine->rootContext(), 0, str); + QQmlExpression expr(engine->rootContext(), nullptr, str); return expr.evaluate(); } @@ -106,6 +106,7 @@ private slots: void get_nested_data(); void crash_model_with_multiple_roles(); void crash_model_with_unknown_roles(); + void crash_model_with_dynamic_roles(); void set_model_cache(); void property_changes(); void property_changes_data(); @@ -123,6 +124,10 @@ private slots: void about_to_be_signals(); void modify_through_delegate(); void bindingsOnGetResult(); + void stringifyModelEntry(); + void qobjectTrackerForDynamicModelObjects(); + void crash_append_empty_array(); + void dynamic_roles_crash_QTBUG_38907(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -130,7 +135,7 @@ bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVarian bool allOk = true; QQmlListModel *model = qobject_cast<QQmlListModel *>(object.value<QObject *>()); - if (model == 0) + if (model == nullptr) return false; if (model->count() != testList.count()) @@ -253,7 +258,7 @@ void tst_qqmllistmodel::static_types() QVERIFY(!component.isError()); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); if (error.isEmpty()) { QVariant actual = obj->property("test"); @@ -324,7 +329,7 @@ void tst_qqmllistmodel::static_i18n() QVERIFY(!component.isError()); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVariant actual = obj->property("test"); @@ -373,7 +378,7 @@ void tst_qqmllistmodel::dynamic_i18n() QVERIFY(!component.isError()); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVariant actual = obj->property("test"); @@ -411,7 +416,7 @@ void tst_qqmllistmodel::static_nestedElements() component.setData(componentStr.toUtf8(), QUrl::fromLocalFile("")); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVariant count = obj->property("count"); QCOMPARE(count.type(), QVariant::Int); @@ -447,6 +452,7 @@ void tst_qqmllistmodel::dynamic_data() QTest::newRow("get2") << "{get(-1) === undefined}" << 1 << "" << dr; QTest::newRow("get3") << "{append({'foo':123});get(0) != undefined}" << 1 << "" << dr; QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "" << dr; + QTest::newRow("get5") << "{append({'foo':123});get(0) == get(0)}" << 1 << "" << dr; QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "" << dr; QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "" << dr; @@ -610,7 +616,7 @@ void tst_qqmllistmodel::enumerate() QQmlComponent component(&eng, testFileUrl("enumerate.qml")); QVERIFY(!component.isError()); QQuickItem *item = qobject_cast<QQuickItem*>(component.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QLatin1String expectedStrings[] = { QLatin1String("val1=1Y"), @@ -955,9 +961,9 @@ void tst_qqmllistmodel::crash_model_with_multiple_roles() QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); QObject *rootItem = component.create(); QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); + QVERIFY(rootItem != nullptr); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); - QVERIFY(model != 0); + QVERIFY(model != nullptr); // used to cause a crash model->setProperty(0, "black", true); @@ -971,15 +977,106 @@ void tst_qqmllistmodel::crash_model_with_unknown_roles() QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); QScopedPointer<QObject> rootItem(component.create()); QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); + QVERIFY(rootItem != nullptr); QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); - QVERIFY(model != 0); + QVERIFY(model != nullptr); // used to cause a crash in debug builds model->index(0, 0, QModelIndex()).data(Qt::DisplayRole); model->index(0, 0, QModelIndex()).data(Qt::UserRole); } +//QTBUG-35639 +void tst_qqmllistmodel::crash_model_with_dynamic_roles() +{ + { + // setting a dynamic role to a QObject value, then triggering dtor + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); + QObject *rootItem = component.create(); + qWarning() << component.errorString(); + QVERIFY(component.errorString().isEmpty()); + QVERIFY(rootItem != 0); + QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); + QVERIFY(model != 0); + + QMetaObject::invokeMethod(model, "appendNewElement"); + + QObject *testObj = new QObject; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); + delete testObj; + + // delete the root item, which will cause the model dtor to run + // previously, this would crash as it attempted to delete testObj. + delete rootItem; + } + + { + // setting a dynamic role to a QObject value, then triggering + // DynamicRoleModelNode::updateValues() to trigger unsafe qobject_cast + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); + QObject *rootItem = component.create(); + qWarning() << component.errorString(); + QVERIFY(component.errorString().isEmpty()); + QVERIFY(rootItem != 0); + QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); + QVERIFY(model != 0); + + QMetaObject::invokeMethod(model, "appendNewElement"); + + QObject *testObj = new QObject; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); + delete testObj; + + QMetaObject::invokeMethod(model, "setElementAgain"); + + delete rootItem; + } + + { + // setting a dynamic role to a QObject value, then triggering + // DynamicRoleModelNodeMetaObject::propertyWrite() + + /* + XXX TODO: I couldn't reproduce this one simply - I think it + requires a WorkerScript sync() call, and that's non-trivial. + I thought I could do it simply via: + + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("dynamicroles.qml")); + QObject *rootItem = component.create(); + qWarning() << component.errorString(); + QVERIFY(component.errorString().isEmpty()); + QVERIFY(rootItem != 0); + QQmlListModel *model = rootItem->findChild<QQmlListModel*>("listModel"); + QVERIFY(model != 0); + + QMetaObject::invokeMethod(model, "appendNewElement"); + + QObject *testObj = new QObject; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj)); + delete testObj; + QObject *testObj2 = new QObject; + model->setProperty(0, "obj", QVariant::fromValue<QObject*>(testObj2)); + + But it turns out that that doesn't work (the setValue() call within + setProperty() doesn't seem to trigger the right codepath, for some + reason), and you can't trigger it manually via: + + QObject *testObj2 = new QObject; + void *a[] = { testObj2, 0 }; + QMetaObject::metacall(dynamicNodeModel, QMetaObject::WriteProperty, 0, a); + + because the dynamicNodeModel for that index cannot be retrieved + using the public API. + + But, anyway, I think the above two test cases are sufficient to + show that QObject* values should be guarded internally. + */ + } +} + //QTBUG-15190 void tst_qqmllistmodel::set_model_cache() { @@ -987,7 +1084,7 @@ void tst_qqmllistmodel::set_model_cache() QQmlComponent component(&eng, testFileUrl("setmodelcachelist.qml")); QObject *model = component.create(); QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); - QVERIFY(model != 0); + QVERIFY(model != nullptr); QVERIFY(model->property("ok").toBool()); delete model; @@ -1185,10 +1282,10 @@ void tst_qqmllistmodel::signal_handlers() QQmlComponent component(&eng, testFileUrl("signalhandlers.qml")); QObject *model = component.create(); QQmlListModel *lm = qobject_cast<QQmlListModel *>(model); - QVERIFY(lm != 0); + QVERIFY(lm != nullptr); lm->setDynamicRoles(dynamicRoles); QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); - QVERIFY(model != 0); + QVERIFY(model != nullptr); QVERIFY(model->property("ok").toBool()); delete model; @@ -1272,7 +1369,7 @@ void tst_qqmllistmodel::empty_element_warning() QVERIFY(!component.isError()); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); delete obj; } @@ -1482,6 +1579,94 @@ void tst_qqmllistmodel::bindingsOnGetResult() QVERIFY(obj->property("success").toBool()); } +void tst_qqmllistmodel::stringifyModelEntry() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "Item {\n" + " ListModel {\n" + " id: testModel\n" + " objectName: \"testModel\"\n" + " ListElement { name: \"Joe\"; age: 22 }\n" + " }\n" + "}\n", QUrl()); + QScopedPointer<QObject> scene(component.create()); + QQmlListModel *model = scene->findChild<QQmlListModel*>("testModel"); + QQmlExpression expr(engine.rootContext(), model, "JSON.stringify(get(0));"); + QVariant v = expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + const QString expectedString = QStringLiteral("{\"age\":22,\"name\":\"Joe\"}"); + QCOMPARE(v.toString(), expectedString); +} + +void tst_qqmllistmodel::qobjectTrackerForDynamicModelObjects() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "Item {\n" + " ListModel {\n" + " id: testModel\n" + " objectName: \"testModel\"\n" + " ListElement { name: \"Joe\"; age: 22 }\n" + " }\n" + "}\n", QUrl()); + QScopedPointer<QObject> scene(component.create()); + QQmlListModel *model = scene->findChild<QQmlListModel*>("testModel"); + QQmlExpression expr(engine.rootContext(), model, "get(0);"); + QVariant v = expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + + QObject *obj = v.value<QObject*>(); + QVERIFY(obj); + + QQmlData *ddata = QQmlData::get(obj, /*create*/false); + QVERIFY(ddata); + QVERIFY(!ddata->jsWrapper.isNullOrUndefined()); +} + +void tst_qqmllistmodel::crash_append_empty_array() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "Item {\n" + " ListModel {\n" + " id: testModel\n" + " objectName: \"testModel\"" + " }\n" + "}\n", QUrl()); + QScopedPointer<QObject> scene(component.create()); + QQmlListModel *model = scene->findChild<QQmlListModel*>("testModel"); + QSignalSpy spy(model, &QQmlListModel::rowsAboutToBeInserted); + QQmlExpression expr(engine.rootContext(), model, "append(new Array())"); + expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + QCOMPARE(spy.count(), 0); +} + +void tst_qqmllistmodel::dynamic_roles_crash_QTBUG_38907() +{ + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("qtbug38907.qml")); + QVERIFY(!component.isError()); + QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(component.create())); + QVERIFY(item != 0); + + QVariant retVal; + + QMetaObject::invokeMethod(item.data(), + "exec", + Qt::DirectConnection, + Q_RETURN_ARG(QVariant, retVal)); + + QVERIFY(retVal.toBool()); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp index 9564d6a8b2..21b0508e4d 100644 --- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp +++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp @@ -48,7 +48,7 @@ Q_DECLARE_METATYPE(QList<QVariantHash>) inline QVariant runexpr(QQmlEngine *engine, const QString &str) { - QQmlExpression expr(engine->rootContext(), 0, str); + QQmlExpression expr(engine->rootContext(), nullptr, str); return expr.evaluate(); } @@ -111,7 +111,7 @@ bool tst_qqmllistmodelworkerscript::compareVariantList(const QVariantList &testL bool allOk = true; QQmlListModel *model = qobject_cast<QQmlListModel *>(object.value<QObject *>()); - if (model == 0) + if (model == nullptr) return false; if (model->count() != testList.count()) @@ -192,7 +192,7 @@ void tst_qqmllistmodelworkerscript::dynamic_data() QTest::addColumn<QString>("warning"); QTest::addColumn<bool>("dynamicRoles"); - for (int i=0 ; i < 2 ; ++i) { + for (int i = 0; i < 2; ++i) { bool dr = (i != 0); // Simple flat model @@ -204,6 +204,7 @@ void tst_qqmllistmodelworkerscript::dynamic_data() QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "" << dr; QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "" << dr; QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "" << dr; + QTest::newRow("get-set") << "{append({'foo':123});get(0).foo;setProperty(0, 'foo', 999);get(0).foo}" << 999 << "" << dr; QTest::newRow("append1") << "{append({'foo':123});count}" << 1 << "" << dr; QTest::newRow("append2") << "{append({'foo':123,'bar':456});count}" << 1 << "" << dr; @@ -349,7 +350,7 @@ void tst_qqmllistmodelworkerscript::dynamic_worker() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("model.qml")); QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QSignalSpy spyCount(&model, SIGNAL(countChanged())); @@ -400,7 +401,7 @@ void tst_qqmllistmodelworkerscript::dynamic_worker_sync() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("model.qml")); QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) script = script.mid(1, script.length() - 2); @@ -434,7 +435,7 @@ void tst_qqmllistmodelworkerscript::get_data() QTest::addColumn<QVariant>("roleValue"); QTest::addColumn<bool>("dynamicRoles"); - for (int i=0 ; i < 2 ; ++i) { + for (int i =0; i < 2; ++i) { bool dr = (i != 0); QTest::newRow("simple value") << "get(0).roleA = 500" << 0 << "roleA" << QVariant(500) << dr; @@ -463,7 +464,7 @@ void tst_qqmllistmodelworkerscript::get_worker() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("model.qml")); QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); // Add some values like get() test RUNEVAL(item, "model.append({roleA: 100})"); @@ -515,7 +516,7 @@ void tst_qqmllistmodelworkerscript::property_changes_data() QTest::addColumn<QString>("testExpression"); QTest::addColumn<bool>("dynamicRoles"); - for (int i=0 ; i < 2 ; ++i) { + for (int i=1 ; i < 2 ; ++i) { bool dr = (i != 0); QTest::newRow("set: plain") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':123});" @@ -574,7 +575,7 @@ void tst_qqmllistmodelworkerscript::property_changes_data() << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; QTest::newRow("nested-set: list, no changes, empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[]});" - << "b" << 0 << true << "get(0).b.count == 0" << dr; + << "b" << 0 << false << "get(0).b.count == 0" << dr; } } @@ -593,7 +594,7 @@ void tst_qqmllistmodelworkerscript::property_changes_worker() QQmlComponent component(&engine, testFileUrl("model.qml")); QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); QQuickItem *item = createWorkerTest(&engine, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QQmlExpression expr(engine.rootContext(), &model, script_setup); expr.evaluate(); @@ -640,7 +641,7 @@ void tst_qqmllistmodelworkerscript::worker_sync() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workersync.qml")); QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QCOMPARE(model.count(), 0); @@ -705,7 +706,7 @@ void tst_qqmllistmodelworkerscript::worker_remove_element() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); @@ -738,7 +739,7 @@ void tst_qqmllistmodelworkerscript::worker_remove_element() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); QQuickItem *item = createWorkerTest(&eng, &component, model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QVERIFY(QMetaObject::invokeMethod(item, "addItem")); @@ -768,7 +769,7 @@ void tst_qqmllistmodelworkerscript::worker_remove_list() QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("workerremovelist.qml")); QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); @@ -815,7 +816,7 @@ void tst_qqmllistmodelworkerscript::dynamic_role() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("model.qml")); QQuickItem *item = createWorkerTest(&engine, &component, &model); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QQmlExpression preExp(engine.rootContext(), &model, preamble); QCOMPARE(preExp.evaluate().toInt(), 0); diff --git a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp index 5c16a48378..199f7bc7e4 100644 --- a/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp +++ b/tests/auto/qml/qqmllistreference/tst_qqmllistreference.cpp @@ -115,7 +115,7 @@ void tst_qqmllistreference::qmllistreference_invalid() QVERIFY(!r.canAt()); QVERIFY(!r.canClear()); QVERIFY(!r.canCount()); - QVERIFY(!r.append(0)); + QVERIFY(!r.append(nullptr)); QVERIFY(!r.at(10)); QVERIFY(!r.clear()); QCOMPARE(r.count(), 0); @@ -132,7 +132,7 @@ void tst_qqmllistreference::qmllistreference_invalid() QVERIFY(!r.canAt()); QVERIFY(!r.canClear()); QVERIFY(!r.canCount()); - QVERIFY(!r.append(0)); + QVERIFY(!r.append(nullptr)); QVERIFY(!r.at(10)); QVERIFY(!r.clear()); QCOMPARE(r.count(), 0); @@ -149,7 +149,7 @@ void tst_qqmllistreference::qmllistreference_invalid() QVERIFY(!r.canAt()); QVERIFY(!r.canClear()); QVERIFY(!r.canCount()); - QVERIFY(!r.append(0)); + QVERIFY(!r.append(nullptr)); QVERIFY(!r.at(10)); QVERIFY(!r.clear()); QCOMPARE(r.count(), 0); @@ -247,7 +247,7 @@ void tst_qqmllistreference::canAppend() { TestType tt; - tt.property.append = 0; + tt.property.append = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.canAppend()); } @@ -276,7 +276,7 @@ void tst_qqmllistreference::canAt() { TestType tt; - tt.property.at = 0; + tt.property.at = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.canAt()); } @@ -305,7 +305,7 @@ void tst_qqmllistreference::canClear() { TestType tt; - tt.property.clear = 0; + tt.property.clear = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.canClear()); } @@ -334,7 +334,7 @@ void tst_qqmllistreference::canCount() { TestType tt; - tt.property.count = 0; + tt.property.count = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.canCount()); } @@ -363,7 +363,7 @@ void tst_qqmllistreference::isReadable() { TestType tt; - tt.property.count = 0; + tt.property.count = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.isReadable()); } @@ -392,7 +392,7 @@ void tst_qqmllistreference::isManipulable() { TestType tt; - tt.property.count = 0; + tt.property.count = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.isManipulable()); } @@ -421,17 +421,17 @@ void tst_qqmllistreference::append() QVERIFY(!ref.append(&object)); QCOMPARE(tt->data.count(), 1); QCOMPARE(tt->data.at(0), tt); - QVERIFY(ref.append(0)); + QVERIFY(ref.append(nullptr)); QCOMPARE(tt->data.count(), 2); QCOMPARE(tt->data.at(0), tt); QVERIFY(!tt->data.at(1)); delete tt; - QVERIFY(!ref.append(0)); + QVERIFY(!ref.append(nullptr)); } { TestType tt; - tt.property.append = 0; + tt.property.append = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.append(&tt)); } @@ -466,7 +466,7 @@ void tst_qqmllistreference::at() { TestType tt; tt.data.append(&tt); - tt.property.at = 0; + tt.property.at = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.at(0)); } @@ -499,7 +499,7 @@ void tst_qqmllistreference::clear() { TestType tt; - tt.property.clear = 0; + tt.property.clear = nullptr; QQmlListReference ref(&tt, "data"); QVERIFY(!ref.clear()); } @@ -534,7 +534,7 @@ void tst_qqmllistreference::count() { TestType tt; tt.data.append(&tt); - tt.property.count = 0; + tt.property.count = nullptr; QQmlListReference ref(&tt, "data"); QCOMPARE(ref.count(), 0); } @@ -626,17 +626,17 @@ void tst_qqmllistreference::listProperty() QQmlComponent component(&engine, testFileUrl("propertyList.qml")); QScopedPointer<QObject> object( component.create() ); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE( object->property("state").toString(), QStringLiteral("MyState2") ); QQmlListReference list( object.data(), "states"); QCOMPARE( list.count(), 2 ); QQuickState* state1 = dynamic_cast<QQuickState*>( list.at( 0 ) ); - QVERIFY(state1 != 0); + QVERIFY(state1 != nullptr); QCOMPARE( state1->name(), QStringLiteral("MyState1") ); QQuickState* state2 = dynamic_cast<QQuickState*>( list.at( 1 ) ); - QVERIFY(state2 != 0); + QVERIFY(state2 != nullptr); QCOMPARE( state2->name(), QStringLiteral("MyState2") ); } diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp index d0ce83b997..eb6eb62648 100644 --- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp +++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp @@ -152,7 +152,7 @@ void tst_qqmllocale::addPropertyData(const QString &l) LOCALE_PROP(QString,exponential), LOCALE_PROP(int,measurementSystem), LOCALE_PROP(int,textDirection), - { 0, QVariant() } + { nullptr, QVariant() } }; int i = 0; @@ -663,7 +663,7 @@ void tst_qqmllocale::addDateTimeFormatData(const QString &l) "h:m:sap ddd MMMM d yy", "'The date and time is: 'H:mm:ss:zzz dd/MM/yy", "MMM d yyyy HH:mm t", - 0 + nullptr }; QByteArray locale = l.toLatin1(); int i = 0; @@ -751,7 +751,7 @@ void tst_qqmllocale::addDateFormatData(const QString &l) "ddd MMMM d yy", "'The date is: 'dd/MM/yy", "MMM d yyyy", - 0 + nullptr }; QByteArray locale = l.toLatin1(); int i = 0; @@ -839,7 +839,7 @@ void tst_qqmllocale::addTimeFormatData(const QString &l) "h:m:sap", "'The time is: 'H:mm:ss:zzz", "HH:mm t", - 0 + nullptr }; QByteArray locale = l.toLatin1(); int i = 0; @@ -1232,7 +1232,7 @@ class DateFormatter : public QObject { Q_OBJECT public: - DateFormatter() : QObject() {} + DateFormatter() {} Q_INVOKABLE QString getLocalizedForm(const QString &isoTimestamp); }; @@ -1268,8 +1268,8 @@ void tst_qqmllocale::timeZoneUpdated() { QByteArray original(qgetenv("TZ")); - // Set the timezone to Brisbane time - setTimeZone(QByteArray("AEST-10:00")); + // Set the timezone to Brisbane time, AEST-10:00 + setTimeZone(QByteArray("Australia/Brisbane")); DateFormatter formatter; @@ -1281,8 +1281,8 @@ void tst_qqmllocale::timeZoneUpdated() QVERIFY(obj); QVERIFY(obj->property("success").toBool()); - // Change to Indian time - setTimeZone(QByteArray("IST-05:30")); + // Change to Indian time, IST-05:30 + setTimeZone(QByteArray("Asia/Kolkata")); QMetaObject::invokeMethod(obj.data(), "check"); diff --git a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp index 9467af6754..ea157a7d15 100644 --- a/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp +++ b/tests/auto/qml/qqmlmetaobject/tst_qqmlmetaobject.cpp @@ -186,10 +186,10 @@ void tst_QQmlMetaObject::property() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(testFile)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); const QMetaObject *mo = object->metaObject(); - QVERIFY(mo->superClass() != 0); + QVERIFY(mo->superClass() != nullptr); QVERIFY(QByteArray(mo->className()).contains("_QML_")); QCOMPARE(mo->propertyOffset(), mo->superClass()->propertyCount()); QCOMPARE(mo->propertyCount(), mo->superClass()->propertyCount() + 1); @@ -361,10 +361,10 @@ void tst_QQmlMetaObject::method() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl(testFile)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); const QMetaObject *mo = object->metaObject(); - QVERIFY(mo->superClass() != 0); + QVERIFY(mo->superClass() != nullptr); QVERIFY(QByteArray(mo->className()).contains("_QML_")); QCOMPARE(mo->methodOffset(), mo->superClass()->methodCount()); QCOMPARE(mo->methodCount(), mo->superClass()->methodCount() + 1); diff --git a/tests/auto/qml/qqmlmetatype/data/revisionedGroupedPropertiesInvalid.qml b/tests/auto/qml/qqmlmetatype/data/revisionedGroupedPropertiesInvalid.qml new file mode 100644 index 0000000000..df6d801cde --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/revisionedGroupedPropertiesInvalid.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import GroupedTest 1.0 + +MyItem { + grouped.prop: 5 +} diff --git a/tests/auto/qml/qqmlmetatype/data/revisionedGroupedPropertiesValid.qml b/tests/auto/qml/qqmlmetatype/data/revisionedGroupedPropertiesValid.qml new file mode 100644 index 0000000000..b7ea017acf --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/revisionedGroupedPropertiesValid.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import GroupedTest 1.1 + +MyItem { + grouped.prop: 5 +} diff --git a/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml new file mode 100644 index 0000000000..85b8f5ac8b --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 +import mytypes 1.0 + +Item { + id: root + property string text: StaticProvider.singletonGetString() +} + diff --git a/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml new file mode 100644 index 0000000000..f6ee4e9b77 --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 +import mytypes 1.0 + +Item { + id: root + Controller { id: controller; objectName: "controller" } +} + diff --git a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro index 0d8de91931..345bc59615 100644 --- a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro +++ b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro @@ -6,4 +6,8 @@ macx:CONFIG -= app_bundle TESTDATA = data/* include (../../shared/util.pri) +qmlfiles.files = data/CompositeType.qml +qmlfiles.prefix = /tstqqmlmetatype +RESOURCES += qmlfiles + QT += core-private gui-private qml-private testlib diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index 30e517c8f9..ac75eeab26 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -35,7 +35,7 @@ #include <private/qqmlmetatype_p.h> #include <private/qqmlpropertyvalueinterceptor_p.h> -#include <private/qhashedstring_p.h> +#include <private/qqmlengine_p.h> #include "../../shared/util.h" class tst_qqmlmetatype : public QQmlDataTest @@ -60,6 +60,12 @@ private slots: void isList(); void defaultObject(); + void unregisterCustomType(); + void unregisterCustomSingletonType(); + + void normalizeUrls(); + void unregisterAttachedProperties(); + void revisionedGroupedProperties(); }; class TestType : public QObject @@ -148,14 +154,14 @@ void tst_qqmlmetatype::initTestCase() void tst_qqmlmetatype::qmlParserStatusCast() { - QVERIFY(!QQmlMetaType::qmlType(QVariant::Int)); - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()) != 0); - QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>())->parserStatusCast(), -1); - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()) != 0); - QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>())->parserStatusCast(), -1); - - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()) != 0); - int cast = QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>())->parserStatusCast(); + QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid()); + QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).parserStatusCast(), -1); + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).isValid()); + QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).parserStatusCast(), -1); + + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid()); + int cast = QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).parserStatusCast(); QVERIFY(cast != -1); QVERIFY(cast != 0); @@ -168,14 +174,14 @@ void tst_qqmlmetatype::qmlParserStatusCast() void tst_qqmlmetatype::qmlPropertyValueSourceCast() { - QVERIFY(!QQmlMetaType::qmlType(QVariant::Int)); - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()) != 0); - QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>())->propertyValueSourceCast(), -1); - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()) != 0); - QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>())->propertyValueSourceCast(), -1); - - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()) != 0); - int cast = QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>())->propertyValueSourceCast(); + QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid()); + QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).propertyValueSourceCast(), -1); + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid()); + QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).propertyValueSourceCast(), -1); + + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).isValid()); + int cast = QQmlMetaType::qmlType(qMetaTypeId<ValueSourceTestType *>()).propertyValueSourceCast(); QVERIFY(cast != -1); QVERIFY(cast != 0); @@ -188,14 +194,14 @@ void tst_qqmlmetatype::qmlPropertyValueSourceCast() void tst_qqmlmetatype::qmlPropertyValueInterceptorCast() { - QVERIFY(!QQmlMetaType::qmlType(QVariant::Int)); - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()) != 0); - QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>())->propertyValueInterceptorCast(), -1); - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()) != 0); - QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>())->propertyValueInterceptorCast(), -1); - - QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueInterceptorTestType *>()) != 0); - int cast = QQmlMetaType::qmlType(qMetaTypeId<ValueInterceptorTestType *>())->propertyValueInterceptorCast(); + QVERIFY(!QQmlMetaType::qmlType(QVariant::Int).isValid()); + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).isValid()); + QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<TestType *>()).propertyValueInterceptorCast(), -1); + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).isValid()); + QCOMPARE(QQmlMetaType::qmlType(qMetaTypeId<ParserStatusTestType *>()).propertyValueInterceptorCast(), -1); + + QVERIFY(QQmlMetaType::qmlType(qMetaTypeId<ValueInterceptorTestType *>()).isValid()); + int cast = QQmlMetaType::qmlType(qMetaTypeId<ValueInterceptorTestType *>()).propertyValueInterceptorCast(); QVERIFY(cast != -1); QVERIFY(cast != 0); @@ -208,32 +214,28 @@ void tst_qqmlmetatype::qmlPropertyValueInterceptorCast() void tst_qqmlmetatype::qmlType() { - QQmlType *type = QQmlMetaType::qmlType(QString("ParserStatusTestType"), QString("Test"), 1, 0); - QVERIFY(type); - QVERIFY(type->module() == QLatin1String("Test")); - QVERIFY(type->elementName() == QLatin1String("ParserStatusTestType")); - QCOMPARE(type->qmlTypeName(), QLatin1String("Test/ParserStatusTestType")); + QQmlType type = QQmlMetaType::qmlType(QString("ParserStatusTestType"), QString("Test"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(type.module() == QLatin1String("Test")); + QVERIFY(type.elementName() == QLatin1String("ParserStatusTestType")); + QCOMPARE(type.qmlTypeName(), QLatin1String("Test/ParserStatusTestType")); type = QQmlMetaType::qmlType("Test/ParserStatusTestType", 1, 0); - QVERIFY(type); - QVERIFY(type->module() == QLatin1String("Test")); - QVERIFY(type->elementName() == QLatin1String("ParserStatusTestType")); - QCOMPARE(type->qmlTypeName(), QLatin1String("Test/ParserStatusTestType")); + QVERIFY(type.isValid()); + QVERIFY(type.module() == QLatin1String("Test")); + QVERIFY(type.elementName() == QLatin1String("ParserStatusTestType")); + QCOMPARE(type.qmlTypeName(), QLatin1String("Test/ParserStatusTestType")); } void tst_qqmlmetatype::invalidQmlTypeName() { - QStringList currFailures = QQmlMetaType::typeRegistrationFailures(); + QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"testtype\"; type names must begin with an uppercase letter"); + QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"Test$Type\""); + QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"EndingInSlash/\""); + QCOMPARE(qmlRegisterType<TestType>("TestNamespace", 1, 0, "Test$Type"), -1); // should fail due to invalid QML type name. QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "EndingInSlash/"), -1); - QStringList nowFailures = QQmlMetaType::typeRegistrationFailures(); - - foreach (const QString &f, currFailures) - nowFailures.removeOne(f); - - QCOMPARE(nowFailures.size(), 2); - QCOMPARE(nowFailures.at(0), QStringLiteral("Invalid QML element name \"Test$Type\"")); - QCOMPARE(nowFailures.at(1), QStringLiteral("Invalid QML element name \"EndingInSlash/\"")); + QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "testtype"), -1); } void tst_qqmlmetatype::prettyTypeName() @@ -269,7 +271,7 @@ void tst_qqmlmetatype::defaultObject() TestType t; ParserStatusTestType p; - QVERIFY(QQmlMetaType::defaultProperty((QObject *)0).name() == 0); + QVERIFY(QQmlMetaType::defaultProperty((QObject *)nullptr).name() == nullptr); QVERIFY(!QQmlMetaType::defaultProperty(&o).name()); QVERIFY(!QQmlMetaType::defaultProperty(&p).name()); QCOMPARE(QString(QQmlMetaType::defaultProperty(&t).name()), QString("foo")); @@ -277,23 +279,23 @@ void tst_qqmlmetatype::defaultObject() void tst_qqmlmetatype::registrationType() { - QQmlType *type = QQmlMetaType::qmlType(QString("TestType"), QString("Test"), 1, 0); - QVERIFY(type); - QVERIFY(!type->isInterface()); - QVERIFY(!type->isSingleton()); - QVERIFY(!type->isComposite()); + QQmlType type = QQmlMetaType::qmlType(QString("TestType"), QString("Test"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); type = QQmlMetaType::qmlType(QString("TestTypeSingleton"), QString("Test"), 1, 0); - QVERIFY(type); - QVERIFY(!type->isInterface()); - QVERIFY(type->isSingleton()); - QVERIFY(!type->isComposite()); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); type = QQmlMetaType::qmlType(QString("TestTypeComposite"), QString("Test"), 1, 0); - QVERIFY(type); - QVERIFY(!type->isInterface()); - QVERIFY(!type->isSingleton()); - QVERIFY(type->isComposite()); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(type.isComposite()); } void tst_qqmlmetatype::compositeType() @@ -305,12 +307,12 @@ void tst_qqmlmetatype::compositeType() QObject* obj = c.create(); QVERIFY(obj); - QQmlType *type = QQmlMetaType::qmlType(QString("ImplicitType"), QString(""), 1, 0); - QVERIFY(type); - QVERIFY(type->module().isEmpty()); - QCOMPARE(type->elementName(), QLatin1String("ImplicitType")); - QCOMPARE(type->qmlTypeName(), QLatin1String("ImplicitType")); - QCOMPARE(type->sourceUrl(), testFileUrl("ImplicitType.qml")); + QQmlType type = QQmlMetaType::qmlType(QString("ImplicitType"), QString(""), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(type.module().isEmpty()); + QCOMPARE(type.elementName(), QLatin1String("ImplicitType")); + QCOMPARE(type.qmlTypeName(), QLatin1String("ImplicitType")); + QCOMPARE(type.sourceUrl(), testFileUrl("ImplicitType.qml")); } void tst_qqmlmetatype::externalEnums() @@ -330,6 +332,302 @@ void tst_qqmlmetatype::externalEnums() } +class Controller1 : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString string MEMBER m_string) + Q_PROPERTY(Controller1Enum enumVal MEMBER m_enumVal) +public: + enum Controller1Enum { + ENUM_VALUE_1 = 1, + ENUM_VALUE_2 = 2 + }; + Q_ENUMS(Controller1Enum) + + Controller1(QObject *parent = nullptr) : QObject(parent), m_string("Controller #1"), + m_enumVal(ENUM_VALUE_1) + {} +private: + QString m_string; + Controller1Enum m_enumVal; +}; + +class Controller2 : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString string MEMBER m_string) + Q_PROPERTY(Controller2Enum enumVal MEMBER m_enumVal) +public: + enum Controller2Enum { + ENUM_VALUE_1 = 111, + ENUM_VALUE_2 = 222 + }; + Q_ENUMS(Controller2Enum) + + Controller2(QObject *parent = nullptr) : QObject(parent), m_string("Controller #2"), + m_enumVal(ENUM_VALUE_1) + {} +private: + QString m_string; + Controller2Enum m_enumVal; +}; + +void tst_qqmlmetatype::unregisterCustomType() +{ + int controllerId = 0; + { + QQmlEngine engine; + QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(!type.isValid()); + controllerId = qmlRegisterType<Controller1>("mytypes", 1, 0, "Controller"); + type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj); + QObject *controller = obj->findChild<QObject *>("controller"); + QVERIFY(qobject_cast<Controller1 *>(controller)); + QVariant stringVal = controller->property("string"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); + QVariant enumVal = controller->property("enumVal"); + QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.toInt(), 1); + } + QQmlMetaType::unregisterType(controllerId); + { + QQmlEngine engine; + QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(!type.isValid()); + controllerId = qmlRegisterType<Controller2>("mytypes", 1, 0, "Controller"); + type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj); + QObject *controller = obj->findChild<QObject *>("controller"); + QVERIFY(qobject_cast<Controller2 *>(controller)); + QVariant stringVal = controller->property("string"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("Controller #2")); + QVariant enumVal = controller->property("enumVal"); + QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.toInt(), 111); + } + QQmlMetaType::unregisterType(controllerId); + { + QQmlEngine engine; + QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(!type.isValid()); + controllerId = qmlRegisterType<Controller1>("mytypes", 1, 0, "Controller"); + type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj); + QObject *controller = obj->findChild<QObject *>("controller"); + QVERIFY(qobject_cast<Controller1 *>(controller)); + QVariant stringVal = controller->property("string"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); + QVariant enumVal = controller->property("enumVal"); + QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.toInt(), 1); + } +} + +class StaticProvider1 : public QObject +{ + Q_OBJECT +public: + StaticProvider1(QObject *parent = nullptr) : QObject(parent) {} + Q_INVOKABLE QString singletonGetString() { return "StaticProvider #1"; } +}; + +static QObject* createStaticProvider1(QQmlEngine *, QJSEngine *) +{ + return new StaticProvider1; +} + +class StaticProvider2 : public QObject +{ + Q_OBJECT +public: + StaticProvider2(QObject *parent = nullptr) : QObject(parent) {} + Q_INVOKABLE QString singletonGetString() { return "StaticProvider #2"; } +}; + +static QObject* createStaticProvider2(QQmlEngine *, QJSEngine *) +{ + return new StaticProvider2; +} + +void tst_qqmlmetatype::unregisterCustomSingletonType() +{ + int staticProviderId = 0; + { + QQmlEngine engine; + staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1); + QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj.data()); + QVariant stringVal = obj->property("text"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); + } + QQmlMetaType::unregisterType(staticProviderId); + { + QQmlEngine engine; + staticProviderId = qmlRegisterSingletonType<StaticProvider2>("mytypes", 1, 0, "StaticProvider", createStaticProvider2); + QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj.data()); + QVariant stringVal = obj->property("text"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2")); + } + QQmlMetaType::unregisterType(staticProviderId); + { + QQmlEngine engine; + staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1); + QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(obj.data()); + QVariant stringVal = obj->property("text"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); + } +} + +void tst_qqmlmetatype::normalizeUrls() +{ + const QUrl url("qrc:///tstqqmlmetatype/data/CompositeType.qml"); + QVERIFY(!QQmlMetaType::qmlType(url).isValid()); + const auto registrationId = qmlRegisterType(url, "Test", 1, 0, "ResourceCompositeType"); + QVERIFY(QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); + QUrl normalizedURL("qrc:/tstqqmlmetatype/data/CompositeType.qml"); + QVERIFY(QQmlMetaType::qmlType(normalizedURL, /*includeNonFileImports=*/true).isValid()); + QQmlMetaType::unregisterType(registrationId); + QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid()); +} + +void tst_qqmlmetatype::unregisterAttachedProperties() +{ + qmlClearTypeRegistrations(); + + const QUrl dummy("qrc:///doesnotexist.qml"); + { + QQmlEngine e; + QQmlComponent c(&e); + c.setData("import QtQuick 2.2\n Item { }", dummy); + + const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation", 2, 2); + QCOMPARE(attachedType.attachedPropertiesId(QQmlEnginePrivate::get(&e)), + attachedType.index()); + + QVERIFY(c.create()); + } + + qmlClearTypeRegistrations(); + { + QQmlEngine e; + QQmlComponent c(&e); + + // The extra import shuffles the type IDs around, so that we + // get a different ID for the attached properties. If the attached + // properties aren't properly cleared, this will crash. + c.setData("import QtQml.StateMachine 1.0 \n" + "import QtQuick 2.2 \n" + "Item { KeyNavigation.up: null }", dummy); + + const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation", 2, 2); + QCOMPARE(attachedType.attachedPropertiesId(QQmlEnginePrivate::get(&e)), + attachedType.index()); + + QVERIFY(c.create()); + } +} + +class Grouped : public QObject +{ + Q_OBJECT + Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged REVISION 1) +public: + int prop() const { return m_prop; } + void setProp(int prop) + { + if (prop != m_prop) { + m_prop = prop; + emit propChanged(prop); + } + } + +signals: + Q_REVISION(1) void propChanged(int prop); + +private: + int m_prop = 0; +}; + +class MyItem : public QObject +{ + Q_OBJECT + Q_PROPERTY(Grouped *grouped READ grouped CONSTANT) +public: + MyItem() : m_grouped(new Grouped) {} + Grouped *grouped() const { return m_grouped.data(); } + +private: + QScopedPointer<Grouped> m_grouped; +}; + +void tst_qqmlmetatype::revisionedGroupedProperties() +{ + qmlClearTypeRegistrations(); + qmlRegisterType<MyItem>("GroupedTest", 1, 0, "MyItem"); + qmlRegisterType<MyItem, 1>("GroupedTest", 1, 1, "MyItem"); + qmlRegisterUncreatableType<Grouped>("GroupedTest", 1, 0, "Grouped", "Grouped"); + qmlRegisterUncreatableType<Grouped, 1>("GroupedTest", 1, 1, "Grouped", "Grouped"); + + { + QQmlEngine engine; + QQmlComponent valid(&engine, testFileUrl("revisionedGroupedPropertiesValid.qml")); + QVERIFY(valid.isReady()); + QScopedPointer<QObject> obj(valid.create()); + QVERIFY(!obj.isNull()); + } + + { + QQmlEngine engine; + QQmlComponent invalid(&engine, testFileUrl("revisionedGroupedPropertiesInvalid.qml")); + QVERIFY(invalid.isError()); + } +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp index fa9782f8c2..b44bc58373 100644 --- a/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/invalidFirstCommandModule/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp index fe01507412..ccd1066a36 100644 --- a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro deleted file mode 100644 index 150f2f08d3..0000000000 --- a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro +++ /dev/null @@ -1,12 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin -SOURCES = plugin.cpp -QT = core qml -DESTDIR = ../imports/org/qtproject/InvalidStrictModule - -QT += core-private gui-private qml-private - -IMPORT_FILES = \ - qmldir - -include (../../../shared/imports.pri) diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir deleted file mode 100644 index 20716dc9f9..0000000000 --- a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module org.qtproject.InvalidStrictModule -plugin invalidStrictModule diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir new file mode 100644 index 0000000000..104c4bf673 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithStaticPlugin/qmldir @@ -0,0 +1,2 @@ +module moduleWithStaticPlugin +plugin secondStaticPlugin diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir new file mode 100644 index 0000000000..45a02b2ffe --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithWaitingPlugin/qmldir @@ -0,0 +1,2 @@ +module moduleWithWaitingPlugin +plugin pluginThatWaits diff --git a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp index 92d30351a7..610710fbf8 100644 --- a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp @@ -36,7 +36,7 @@ class MyPluginType : public QObject Q_PROPERTY(QString value READ value) public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} QString value() const { return "Hello"; } }; @@ -47,7 +47,7 @@ class MyNestedPluginType : public QObject Q_PROPERTY(QString value READ value) public: - MyNestedPluginType(QObject *parent=0) : QObject(parent) {} + MyNestedPluginType(QObject *parent=nullptr) : QObject(parent) {} QString value() const { return "Goodbye"; } }; diff --git a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp index 5fc05b91bd..4c2109e02a 100644 --- a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp index 515d56a3c4..cb8c395fdf 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp @@ -37,7 +37,7 @@ class MyChildPluginType : public QObject Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) public: - MyChildPluginType(QObject *parent=0) : QObject(parent) + MyChildPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("child import2.1 worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp index 6cae5254bc..ced7e0895d 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/plugin.cpp @@ -37,7 +37,7 @@ class MyPluginType : public QObject Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) public: - MyPluginType(QObject *parent=0) : QObject(parent) + MyPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("import2.1 worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp index ecec870374..67cfc2a579 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.2/plugin.cpp @@ -37,7 +37,7 @@ class MyPluginType : public QObject Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) public: - MyPluginType(QObject *parent=0) : QObject(parent) + MyPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("import2.2 worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp index 56545cfa3c..7b8f2a96be 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp @@ -37,7 +37,7 @@ class MyChildPluginType : public QObject Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) public: - MyChildPluginType(QObject *parent=0) : QObject(parent) + MyChildPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("child import2 worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp index 49a2a747a4..8a4598e3bf 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/plugin.cpp @@ -37,7 +37,7 @@ class MyPluginType : public QObject Q_PROPERTY(int valueOnlyIn2 READ value WRITE setValue) public: - MyPluginType(QObject *parent=0) : QObject(parent) + MyPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("import2 worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp index 28490d3d98..6a2deca1df 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp @@ -36,7 +36,7 @@ class MyChildPluginType : public QObject Q_PROPERTY(int value READ value WRITE setValue) public: - MyChildPluginType(QObject *parent=0) : QObject(parent) + MyChildPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("child import worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp index db51185de6..5e39966a2a 100644 --- a/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/plugin/plugin.cpp @@ -36,7 +36,7 @@ class MyPluginType : public QObject Q_PROPERTY(int value READ value WRITE setValue) public: - MyPluginType(QObject *parent=0) : QObject(parent) + MyPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("import worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp index 7669d65568..5c3c1b81c6 100644 --- a/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/pluginWrongCase/plugin.cpp @@ -36,7 +36,7 @@ class MyPluginType : public QObject Q_PROPERTY(int value READ value WRITE setValue) public: - MyPluginType(QObject *parent=0) : QObject(parent) + MyPluginType(QObject *parent=nullptr) : QObject(parent) { qWarning("import worked"); } diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp index 92211ebf9d..0780ea8325 100644 --- a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp index 3df3e9cc81..bc896716e2 100644 --- a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp index afdeea80f4..ae8c231aab 100644 --- a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp @@ -33,7 +33,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro index 0f548aa6f8..ae13a041cc 100644 --- a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro @@ -11,7 +11,6 @@ SUBDIRS =\ nestedPlugin\ strictModule\ strictModule.2\ - invalidStrictModule\ nonstrictModule\ preemptiveModule\ preemptedStrictModule\ diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp index 4f5176ae62..312ed325b1 100644 --- a/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/strictModule.2/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp index eaa9aeb1d0..a622078159 100644 --- a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp @@ -34,7 +34,7 @@ class MyPluginType : public QObject { Q_OBJECT public: - MyPluginType(QObject *parent=0) : QObject(parent) {} + MyPluginType(QObject *parent=nullptr) : QObject(parent) {} }; diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index 8600e1e8ab..97ca3fa1de 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -29,6 +29,10 @@ #include <qdir.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlextensionplugin.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonarray.h> #include <QDebug> #if defined(Q_OS_MAC) @@ -73,12 +77,80 @@ private slots: void importsChildPlugin(); void importsChildPlugin2(); void importsChildPlugin21(); + void parallelPluginImport(); private: QString m_importsDirectory; QString m_dataImportsDirectory; }; +class PluginThatWaits : public QQmlExtensionPlugin +{ +public: + static QByteArray metaData; + + static QMutex initializeEngineEntered; + static QWaitCondition waitingForInitializeEngineEntry; + static QMutex leavingInitializeEngine; + static QWaitCondition waitingForInitializeEngineLeave; + + void registerTypes(const char *uri) override + { + qmlRegisterModule(uri, 1, 0); + } + + void initializeEngine(QQmlEngine *engine, const char *uri) override + { + initializeEngineEntered.lock(); + leavingInitializeEngine.lock(); + waitingForInitializeEngineEntry.wakeOne(); + initializeEngineEntered.unlock(); + waitingForInitializeEngineLeave.wait(&leavingInitializeEngine); + leavingInitializeEngine.unlock(); + } +}; +QByteArray PluginThatWaits::metaData; +QMutex PluginThatWaits::initializeEngineEntered; +QWaitCondition PluginThatWaits::waitingForInitializeEngineEntry; +QMutex PluginThatWaits::leavingInitializeEngine; +QWaitCondition PluginThatWaits::waitingForInitializeEngineLeave; + +class SecondStaticPlugin : public QQmlExtensionPlugin +{ +public: + static QByteArray metaData; + + void registerTypes(const char *uri) override + { + qmlRegisterModule(uri, 1, 0); + } +}; +QByteArray SecondStaticPlugin::metaData; + +template <typename PluginType> +void registerStaticPlugin(const char *uri) +{ + QStaticPlugin plugin; + plugin.instance = []() { + static PluginType plugin; + return static_cast<QObject*>(&plugin); + }; + + QJsonObject md; + md.insert(QStringLiteral("IID"), QQmlExtensionInterface_iid); + QJsonArray uris; + uris.append(uri); + md.insert(QStringLiteral("uri"), uris); + + PluginType::metaData.append(QLatin1String("QTMETADATA ")); + PluginType::metaData.append(QJsonDocument(md).toBinaryData()); + + plugin.rawMetaData = []() { + return PluginType::metaData.constData(); + }; + qRegisterStaticPluginFunction(plugin); +}; + void tst_qqmlmoduleplugin::initTestCase() { QQmlDataTest::initTestCase(); @@ -88,6 +160,9 @@ void tst_qqmlmoduleplugin::initTestCase() m_dataImportsDirectory = directory() + QStringLiteral("/imports"); QVERIFY2(QFileInfo(m_dataImportsDirectory).isDir(), qPrintable(QString::fromLatin1("Imports directory '%1' does not exist.").arg(m_dataImportsDirectory))); + + registerStaticPlugin<PluginThatWaits>("moduleWithWaitingPlugin"); + registerStaticPlugin<SecondStaticPlugin>("moduleWithStaticPlugin"); } #define VERIFY_ERRORS(errorfile) \ @@ -143,7 +218,7 @@ void tst_qqmlmoduleplugin::importsPlugin() qWarning() << err; VERIFY_ERRORS(0); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(),123); delete object; } @@ -211,7 +286,7 @@ void tst_qqmlmoduleplugin::importPluginWithQmlFile() qWarning() << err; VERIFY_ERRORS(0); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; } @@ -227,7 +302,7 @@ void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl() QTRY_COMPARE(component.status(), QQmlComponent::Ready); QObject *object = component.create(); QCOMPARE(object->property("width").toInt(), 300); - QVERIFY(object != 0); + QVERIFY(object != nullptr); delete object; foreach (QQmlError err, component.errors()) @@ -249,7 +324,7 @@ void tst_qqmlmoduleplugin::remoteImportWithUnquotedUri() QTRY_COMPARE(component.status(), QQmlComponent::Ready); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("width").toInt(), 300); delete object; @@ -258,6 +333,29 @@ void tst_qqmlmoduleplugin::remoteImportWithUnquotedUri() VERIFY_ERRORS(0); } +static QByteArray msgComponentError(const QQmlComponent &c, const QQmlEngine *engine /* = 0 */) +{ + QString result; + const QList<QQmlError> errors = c.errors(); + QTextStream str(&result); + str << "Component '" << c.url().toString() << "' has " << errors.size() << " errors: '"; + for (int i = 0; i < errors.size(); ++i) { + if (i) + str << ", '"; + str << errors.at(i).toString() << '\''; + } + if (!engine) { + if (QQmlContext *context = c.creationContext()) + engine = context->engine(); + } + if (engine) { + str << " Import paths: (" << engine->importPathList().join(QStringLiteral(", ")) + << ") Plugin paths: (" << engine->pluginPathList().join(QStringLiteral(", ")) + << ')'; + } + return result.toLocal8Bit(); +} + // QTBUG-17324 void tst_qqmlmoduleplugin::importsMixedQmlCppPlugin() @@ -271,7 +369,7 @@ void tst_qqmlmoduleplugin::importsMixedQmlCppPlugin() QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.qml"))); QObject *o = component.create(); - QVERIFY2(o != 0, QQmlDataTest::msgComponentError(component, &engine)); + QVERIFY2(o != nullptr, msgComponentError(component, &engine)); QCOMPARE(o->property("test").toBool(), true); delete o; } @@ -280,7 +378,7 @@ void tst_qqmlmoduleplugin::importsMixedQmlCppPlugin() QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.2.qml"))); QObject *o = component.create(); - QVERIFY2(o != 0, QQmlDataTest::msgComponentError(component, &engine)); + QVERIFY2(o != nullptr, msgComponentError(component, &engine)); QCOMPARE(o->property("test").toBool(), true); QCOMPARE(o->property("test2").toBool(), true); delete o; @@ -408,7 +506,7 @@ void tst_qqmlmoduleplugin::importLocalModule() component.setData(qml.toUtf8(), testFileUrl("empty.qml")); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("majorVersion").value<int>(), majorVersion); QCOMPARE(object->property("minorVersion").value<int>(), minorVersion); } @@ -465,7 +563,7 @@ void tst_qqmlmoduleplugin::importStrictModule() if (error.isEmpty()) { QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } else { QVERIFY(!component.isReady()); QCOMPARE(component.errors().count(), 1); @@ -506,12 +604,6 @@ void tst_qqmlmoduleplugin::importStrictModule_data() << QString() << QString(); - QTest::newRow("wrong target") - << "import org.qtproject.InvalidStrictModule 1.0\n" - "MyPluginType {}" - << QString() - << ":1:1: plugin cannot be loaded for module \"org.qtproject.InvalidStrictModule\": Cannot install element 'MyPluginType' into unregistered namespace 'org.qtproject.SomeOtherModule'"; - QTest::newRow("non-strict clash") << "import org.qtproject.NonstrictModule 1.0\n" "MyPluginType {}" @@ -555,7 +647,7 @@ void tst_qqmlmoduleplugin::importProtectedModule() //If plugin is loaded due to import, should assert QScopedPointer<QObject> object(component.create()); //qDebug() << component.errorString(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); } void tst_qqmlmoduleplugin::importVersionedModule() @@ -596,7 +688,7 @@ void tst_qqmlmoduleplugin::importsChildPlugin() qWarning() << err; VERIFY_ERRORS(0); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(),123); delete object; } @@ -613,7 +705,7 @@ void tst_qqmlmoduleplugin::importsChildPlugin2() qWarning() << err; VERIFY_ERRORS(0); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(),123); delete object; } @@ -630,11 +722,56 @@ void tst_qqmlmoduleplugin::importsChildPlugin21() qWarning() << err; VERIFY_ERRORS(0); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(),123); delete object; } +void tst_qqmlmoduleplugin::parallelPluginImport() +{ + QMutexLocker locker(&PluginThatWaits::initializeEngineEntered); + + QThread worker; + QObject::connect(&worker, &QThread::started, [&worker](){ + // Engines in separate threads are tricky, but as long as we do not create a graphical + // object and move objects created by the engines across thread boundaries, this is safe. + // At the same time this allows us to place the engine's loader thread into the position + // where, without the fix for this bug, the global lock is acquired. + QQmlEngine engineInThread; + + QQmlComponent component(&engineInThread); + component.setData("import moduleWithWaitingPlugin 1.0\nimport QtQml 2.0\nQtObject {}", + QUrl()); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + worker.quit(); + }); + worker.start(); + + PluginThatWaits::waitingForInitializeEngineEntry.wait(&PluginThatWaits::initializeEngineEntered); + + { + // After acquiring this lock, the engine in the other thread as well as its type loader + // thread are blocked. However they should not hold the global plugin lock + // qmlEnginePluginsWithRegisteredTypes()->mutex in qqmllimports.cpp, allowing for the load + // of a component in a different engine with its own plugin to proceed. + QMutexLocker continuationLock(&PluginThatWaits::leavingInitializeEngine); + + QQmlEngine secondEngine; + QQmlComponent secondComponent(&secondEngine); + secondComponent.setData("import moduleWithStaticPlugin 1.0\nimport QtQml 2.0\nQtObject {}", + QUrl()); + QScopedPointer<QObject> o(secondComponent.create()); + QVERIFY(!o.isNull()); + + PluginThatWaits::waitingForInitializeEngineLeave.wakeOne(); + } + + worker.wait(); +} + QTEST_MAIN(tst_qqmlmoduleplugin) #include "tst_qqmlmoduleplugin.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro index 43bd112415..118ca26ee9 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.pro @@ -10,4 +10,12 @@ include (../../shared/util.pri) TESTDATA = data/* imports/* $$OUT_PWD/imports/* +waitingPlugin.files = moduleWithWaitingPlugin +waitingPlugin.prefix = /qt-project.org/imports/ +RESOURCES += waitingPlugin + +staticPlugin.files = moduleWithStaticPlugin +staticPlugin.prefix = /qt-project.org/imports/ +RESOURCES += staticPlugin + QT += core-private gui-private qml-private network testlib diff --git a/tests/auto/qml/qqmlnotifier/data/objectRenamer.qml b/tests/auto/qml/qqmlnotifier/data/objectRenamer.qml new file mode 100644 index 0000000000..65d2206880 --- /dev/null +++ b/tests/auto/qml/qqmlnotifier/data/objectRenamer.qml @@ -0,0 +1,9 @@ +import QtQml 2.2 + +QtObject { + property Timer timer: Timer { + running: true + interval: 0 + onTriggered: parent.objectName = "havoc" + } +} diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index beb60925bb..6e831eacc1 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -32,6 +32,9 @@ #include <QQmlContext> #include <qqml.h> #include <QMetaMethod> +#if QT_CONFIG(process) +#include <QProcess> +#endif #include "../../shared/util.h" @@ -46,21 +49,17 @@ class ExportedClass : public QObject Q_PROPERTY(int v4BindingProp2 READ v4BindingProp2 NOTIFY v4BindingProp2Changed) Q_PROPERTY(int scriptBindingProp READ scriptBindingProp NOTIFY scriptBindingPropChanged) public: - int qmlObjectPropConnections; - int cppObjectPropConnections; - int unboundPropConnections; - int v8BindingPropConnections; - int v4BindingPropConnections; - int v4BindingProp2Connections; - int scriptBindingPropConnections; - int boundSignalConnections; - int unusedSignalConnections; - - ExportedClass() - : qmlObjectPropConnections(0), cppObjectPropConnections(0), unboundPropConnections(0), - v8BindingPropConnections(0), v4BindingPropConnections(0), v4BindingProp2Connections(0), - scriptBindingPropConnections(0), boundSignalConnections(0), unusedSignalConnections(0) - {} + int qmlObjectPropConnections = 0; + int cppObjectPropConnections = 0; + int unboundPropConnections = 0; + int v8BindingPropConnections = 0; + int v4BindingPropConnections = 0; + int v4BindingProp2Connections = 0; + int scriptBindingPropConnections = 0; + int boundSignalConnections = 0; + int unusedSignalConnections = 0; + + ExportedClass() {} ~ExportedClass() { @@ -98,7 +97,7 @@ public: } protected: - void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE { + void connectNotify(const QMetaMethod &signal) override { if (signal.name() == "qmlObjectPropChanged") qmlObjectPropConnections++; if (signal.name() == "cppObjectPropChanged") cppObjectPropConnections++; if (signal.name() == "unboundPropChanged") unboundPropConnections++; @@ -112,7 +111,7 @@ protected: //qDebug() << Q_FUNC_INFO << this << signal.name(); } - void disconnectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE { + void disconnectNotify(const QMetaMethod &signal) override { if (signal.name() == "qmlObjectPropChanged") qmlObjectPropConnections--; if (signal.name() == "cppObjectPropChanged") cppObjectPropConnections--; if (signal.name() == "unboundPropChanged") unboundPropConnections--; @@ -141,12 +140,10 @@ class tst_qqmlnotifier : public QQmlDataTest { Q_OBJECT public: - tst_qqmlnotifier() - : root(0), exportedClass(0), exportedObject(0) - {} + tst_qqmlnotifier() {} private slots: - void initTestCase() Q_DECL_OVERRIDE; + void initTestCase() override; void cleanupTestCase(); void testConnectNotify(); @@ -162,13 +159,15 @@ private slots: void disconnectOnDestroy(); void lotsOfBindings(); + void deleteFromHandler(); + private: void createObjects(); QQmlEngine engine; - QObject *root; - ExportedClass *exportedClass; - ExportedClass *exportedObject; + QObject *root = nullptr; + ExportedClass *exportedClass = nullptr; + ExportedClass *exportedObject = nullptr; }; void tst_qqmlnotifier::initTestCase() @@ -180,28 +179,28 @@ void tst_qqmlnotifier::initTestCase() void tst_qqmlnotifier::createObjects() { delete root; - root = 0; - exportedClass = exportedObject = 0; + root = nullptr; + exportedClass = exportedObject = nullptr; QQmlComponent component(&engine, testFileUrl("connectnotify.qml")); exportedObject = new ExportedClass(); exportedObject->setObjectName("exportedObject"); engine.rootContext()->setContextProperty("_exportedObject", exportedObject); root = component.create(); - QVERIFY(root != 0); + QVERIFY(root != nullptr); exportedClass = qobject_cast<ExportedClass *>( root->findChild<ExportedClass*>("exportedClass")); - QVERIFY(exportedClass != 0); + QVERIFY(exportedClass != nullptr); exportedClass->verifyReceiverCount(); } void tst_qqmlnotifier::cleanupTestCase() { delete root; - root = 0; + root = nullptr; delete exportedObject; - exportedObject = 0; + exportedObject = nullptr; } void tst_qqmlnotifier::testConnectNotify() @@ -303,7 +302,7 @@ void tst_qqmlnotifier::disconnectOnDestroy() // Deleting a QML object should remove all connections. For exportedClass, this is tested in // the destructor, and for exportedObject, it is tested below. delete root; - root = 0; + root = nullptr; QCOMPARE(exportedObject->cppObjectPropConnections, 0); exportedObject->verifyReceiverCount(); } @@ -341,6 +340,33 @@ void tst_qqmlnotifier::lotsOfBindings() delete e; } +void tst_qqmlnotifier::deleteFromHandler() +{ +#if !QT_CONFIG(process) + QSKIP("Need QProcess support to test qFatal."); +#else + if (qEnvironmentVariableIsSet("TST_QQMLNOTIFIER_DO_CRASH")) { + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("objectRenamer.qml")); + QPointer<QObject> mess = component.create(); + QObject::connect(mess, &QObject::objectNameChanged, [&]() { delete mess; }); + QTRY_VERIFY(mess.isNull()); // BANG! + } else { + QProcess process; + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("TST_QQMLNOTIFIER_DO_CRASH", "bang"); + process.setProcessEnvironment(env); + process.setProgram(QCoreApplication::applicationFilePath()); + process.setArguments({"deleteFromHandler"}); + process.start(); + QTRY_COMPARE(process.exitStatus(), QProcess::CrashExit); + const QByteArray output = process.readAllStandardOutput(); + QVERIFY(output.contains("QFATAL")); + QVERIFY(output.contains("destroyed while one of its QML signal handlers is in progress")); + } +#endif +} + QTEST_MAIN(tst_qqmlnotifier) #include "tst_qqmlnotifier.moc" diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp index 430a9c2a22..fb63d811a8 100644 --- a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp +++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp @@ -26,6 +26,7 @@ ** ****************************************************************************/ #include <QtQml/private/qqmlobjectmodel_p.h> +#include <QtQml/private/qqmlchangeset_p.h> #include <QtTest/qsignalspy.h> #include <QtTest/qtest.h> @@ -46,16 +47,41 @@ static bool compareItems(QQmlObjectModel *model, const QObjectList &items) return true; } +static bool verifyChangeSet(const QQmlChangeSet &changeSet, int expectedInserts, int expectedRemoves, bool isMove, int moveId = -1) +{ + int actualRemoves = 0; + for (const QQmlChangeSet::Change &r : changeSet.removes()) { + if (r.isMove() != isMove && (!isMove || moveId == r.moveId)) + return false; + actualRemoves += r.count; + } + + int actualInserts = 0; + for (const QQmlChangeSet::Change &i : changeSet.inserts()) { + if (i.isMove() != isMove && (!isMove || moveId == i.moveId)) + return false; + actualInserts += i.count; + } + + return actualRemoves == expectedRemoves && actualInserts == expectedInserts; +} + +Q_DECLARE_METATYPE(QQmlChangeSet) + void tst_QQmlObjectModel::changes() { QQmlObjectModel model; + qRegisterMetaType<QQmlChangeSet>(); + QSignalSpy countSpy(&model, SIGNAL(countChanged())); QSignalSpy childrenSpy(&model, SIGNAL(childrenChanged())); + QSignalSpy modelUpdateSpy(&model, SIGNAL(modelUpdated(QQmlChangeSet,bool))); int count = 0; int countSignals = 0; int childrenSignals = 0; + int modelUpdateSignals = 0; QObjectList items; QObject item0, item1, item2, item3; @@ -66,6 +92,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 1, 0, false)); // insert(0, item1) -> [item1, item0] model.insert(0, &item1); items.insert(0, &item1); @@ -73,6 +101,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 1, 0, false)); // append(item2) -> [item1, item0, item2] model.append(&item2); items.append(&item2); @@ -80,6 +110,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 1, 0, false)); // insert(2, item3) -> [item1, item0, item3, item2] model.insert(2, &item3); items.insert(2, &item3); @@ -87,6 +119,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 1, 0, false)); // move(0, 1) -> [item0, item1, item3, item2] model.move(0, 1); items.move(0, 1); @@ -94,6 +128,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 1, 1, true, 1)); // move(3, 2) -> [item0, item1, item2, item3] model.move(3, 2); items.move(3, 2); @@ -101,6 +137,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 1, 1, true, 2)); // remove(0) -> [item1, item2, item3] model.remove(0); items.removeAt(0); @@ -108,6 +146,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 0, 1, false)); // remove(2) -> [item1, item2] model.remove(2); items.removeAt(2); @@ -115,6 +155,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 0, 1, false)); // clear() -> [] model.clear(); items.clear(); @@ -122,6 +164,8 @@ void tst_QQmlObjectModel::changes() QVERIFY(compareItems(&model, items)); QCOMPARE(countSpy.count(), ++countSignals); QCOMPARE(childrenSpy.count(), ++childrenSignals); + QCOMPARE(modelUpdateSpy.count(), ++modelUpdateSignals); + QVERIFY(verifyChangeSet(modelUpdateSpy.last().first().value<QQmlChangeSet>(), 0, 2, false)); } QTEST_MAIN(tst_QQmlObjectModel) diff --git a/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp b/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp index c3ace5e0f3..44ce1d6987 100644 --- a/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp +++ b/tests/auto/qml/qqmlopenmetaobject/tst_qqmlopenmetaobject.cpp @@ -44,7 +44,7 @@ class CustomObject: public QObject { Q_OBJECT public: - CustomObject(QObject *parent = 0) + CustomObject(QObject *parent = nullptr) : QObject(parent) {} }; @@ -53,7 +53,7 @@ void tst_qqmlopenmetaobject::createProperties() QQmlEngine engine; CustomObject object; const QQmlRefPointer<QQmlOpenMetaObjectType> mot = new QQmlOpenMetaObjectType(object.metaObject(), &engine); - QQmlOpenMetaObject *const mo = new QQmlOpenMetaObject(&object, mot); + QQmlOpenMetaObject *const mo = new QQmlOpenMetaObject(&object, mot.data()); mo->setCached(true); mot->createProperty("customProperty"); QVERIFY(true); diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index ba2b836a6d..c2c73935c0 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -50,6 +50,10 @@ private slots: void qmlParser(); #endif void invalidEscapeSequence(); + void stringLiteral(); + void noSubstitutionTemplateLiteral(); + void templateLiteral(); + void leadingSemicolonInClass(); private: QStringList excludedDirs; @@ -78,6 +82,13 @@ public: const quint32 parentBegin = parent->firstSourceLocation().begin(); const quint32 parentEnd = parent->lastSourceLocation().end(); + if (node->firstSourceLocation().begin() < parentBegin) + qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn + << "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn; + if (node->lastSourceLocation().end() > parentEnd) + qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn + << "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn; + QVERIFY(node->firstSourceLocation().begin() >= parentBegin); QVERIFY(node->lastSourceLocation().end() <= parentEnd); } @@ -183,13 +194,12 @@ void tst_qqmlparser::qmlParser() Lexer lexer(&engine); lexer.setCode(code, 1, qmlMode); Parser parser(&engine); - if (qmlMode) - parser.parse(); - else - parser.parseProgram(); + bool ok = qmlMode ? parser.parse() : parser.parseProgram(); - check::Check chk; - chk(parser.rootNode()); + if (ok) { + check::Check chk; + chk(parser.rootNode()); + } } #endif @@ -204,6 +214,76 @@ void tst_qqmlparser::invalidEscapeSequence() parser.parse(); } +void tst_qqmlparser::stringLiteral() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + QLatin1String code("'hello string'"); + lexer.setCode(code , 1); + Parser parser(&engine); + QVERIFY(parser.parseExpression()); + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expression); + QVERIFY(literal); + QCOMPARE(literal->value, "hello string"); + QCOMPARE(literal->firstSourceLocation().begin(), 0); + QCOMPARE(literal->lastSourceLocation().end(), code.size()); +} + +void tst_qqmlparser::noSubstitutionTemplateLiteral() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + QLatin1String code("`hello template`"); + lexer.setCode(code, 1); + Parser parser(&engine); + QVERIFY(parser.parseExpression()); + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + + auto *literal = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression); + QVERIFY(literal); + + QCOMPARE(literal->value, "hello template"); + QCOMPARE(literal->firstSourceLocation().begin(), 0); + QCOMPARE(literal->lastSourceLocation().end(), code.size()); +} + +void tst_qqmlparser::templateLiteral() +{ + using namespace QQmlJS; + + Engine engine; + Lexer lexer(&engine); + QLatin1String code("`one plus one equals ${1+1}!`"); + lexer.setCode(code, 1); + Parser parser(&engine); + QVERIFY(parser.parseExpression()); + AST::ExpressionNode *expression = parser.expression(); + QVERIFY(expression); + + auto *templateLiteral = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression); + QVERIFY(templateLiteral); + + QCOMPARE(templateLiteral->firstSourceLocation().begin(), 0); + auto *e = templateLiteral->expression; + QVERIFY(e); +} + +void tst_qqmlparser::leadingSemicolonInClass() +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String("class X{;n(){}}"), 1); + QQmlJS::Parser parser(&engine); + QVERIFY(parser.parseProgram()); +} + QTEST_MAIN(tst_qqmlparser) #include "tst_qqmlparser.moc" diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml b/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml new file mode 100644 index 0000000000..43ccf24965 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-all-empty-input.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property bool wasTestSuccessful: false + + property var promise: Promise.all([]); + + Component.onCompleted: { + promise.then(function(value) { + if (value.length !== 0) { + return; + } + + wasTestSuccessful = true + + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml b/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml new file mode 100644 index 0000000000..ec59c47130 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-all-invoke-then-method.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int callCount: 0 + property bool wasTestSuccessful: false + + property var promise1: new Promise(function() {}) + property var promise2: new Promise(function() {}) + property var promise3: new Promise(function() {}) + + Component.onCompleted: { + promise1.then = null + console.log(promise1.then) + + // TODO: This assinment works in JS scope but does not work + // in QML scope + promise1.then = promise2.then = promise3.then = function(a, b) { + console.log("then was called") + callCount++; + } + + Promise.all([promise1, promise2, promise3]) + + console.log("callCount " + callCount) + wasTestSuccessful = (callCount === 3) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml b/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml new file mode 100644 index 0000000000..a9bdbf2fb7 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-all-noniterable-input.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property bool wasTestSuccessful: false + + property var nonIterable: 3 + property var promise: Promise.all(nonIterable); + + Component.onCompleted: { + promise.then(function() { + throw new Error("Should never be called") + }, function(err) { + if (!(err instanceof TypeError)) { + throw new Error("Should reject with TypeError") + return; + } + + wasTestSuccessful = true + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml new file mode 100644 index 0000000000..0d3d31d494 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-last.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + // TODO! + + property int resolveValue: 5 + property int rejectValue: 10 + property int resultValue: rejectValue + property bool wasTestSuccessful: false + + + property var delayedEvent: Timer { + interval: 0 + property var handler: null + onTriggered: { + if (handler) { + handler(); + } + } + } + + function postEvent(event, value) { + delayedEvent.handler = function() { event(value) } + delayedEvent.restart(); + } + + property var promise1: Promise.resolve(resolveValue); + property int promise2: resolveValue + property var promise3: new Promise(function (resolve, reject) { + postEvent(resolve, resolveValue) + }) + property var promise4: Promise.reject(rejectValue) + + Component.onCompleted: { + Promise.all([promise1, promise2, promise3, promise4]).then(function() { + throw new Error("Should never be called") + }, function(value) { + if (value !== rejectValue) { + return; + } + + wasTestSuccessful = true + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml new file mode 100644 index 0000000000..67ca95971c --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-all-reject-reject-is-mid.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + // TODO! + + property int resolveValue: 5 + property int rejectValue: 10 + property int resultValue: rejectValue + property bool wasTestSuccessful: false + + + property var delayedEvent: Timer { + interval: 0 + property var handler: null + onTriggered: { + if (handler) { + handler(); + } + } + } + + function postEvent(event, value) { + delayedEvent.handler = function() { event(value) } + delayedEvent.restart(); + } + + property var promise1: Promise.resolve(resolveValue); + property int promise2: resolveValue + property var promise3: Promise.reject(rejectValue) + property var promise4: new Promise(function (resolve, reject) { + postEvent(resolve, resolveValue) + }) + + Component.onCompleted: { + Promise.all([promise1, promise2, promise3, promise4]).then(function() { + throw new Error("Should never be called") + }, function(value) { + if (value !== rejectValue) { + return; + } + + wasTestSuccessful = true + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml b/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml new file mode 100644 index 0000000000..1996b7f9c8 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-all-resolve.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int resolveValue: 5 + property var resultValue: [resolveValue, resolveValue, resolveValue] + property bool wasTestSuccessful: false + + + property var delayedEvent: Timer { + interval: 0 + property var handler: null + onTriggered: { + if (handler) { + handler(); + } + } + } + + function postEvent(event, value) { + delayedEvent.handler = function() { event(value) } + delayedEvent.restart(); + } + + property var promise1: Promise.resolve(resolveValue); + property int promise2: resolveValue + property var promise3: new Promise(function (resolve, reject) { + postEvent(resolve, resolveValue) + }) + + Component.onCompleted: { + Promise.all([promise1, promise2, promise3]).then(function(value) { + if (value.length !== resultValue.length) { + return; + } + + for (var i in value) { + if (value[i] !== resultValue[i]) { + return; + } + } + + wasTestSuccessful = true + + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml new file mode 100644 index 0000000000..4c54ad3fc7 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-async-reject-with-value.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int rejectValue: 5 + property bool wasTestSuccessful: false + + + property var delayedExecutor: Timer { + interval: 0 + property var executor: null + onTriggered: { + if (executor) { + executor(); + } + } + } + + property var promise: new Promise(function (resolve, reject) { + delayedExecutor.executor = function() { + reject(rejectValue) + } + delayedExecutor.restart(); + }) + + Component.onCompleted: { + promise.then(function() { + throw new Error("Should never be called") + }, function(value) { + if (value === rejectValue) { + wasTestSuccessful = true + } + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml new file mode 100644 index 0000000000..a491394a30 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-async-resolve-with-value.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int resolveValue: 5 + property bool wasTestSuccessful: false + + + property var delayedExecutor: Timer { + interval: 0 + property var executor: null + onTriggered: { + if (executor) { + executor(); + } + } + } + + property var promise: new Promise(function (resolve, reject) { + delayedExecutor.executor = function() { + resolve(resolveValue) + } + delayedExecutor.restart(); + }) + + Component.onCompleted: { + promise.then(function(value) { + if (value === resolveValue) { + wasTestSuccessful = true + } + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml new file mode 100644 index 0000000000..e2a264dd3c --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-function-extensible.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property bool wasTestSuccessful: false + + property var executorFunction: null + + function notPromise(executor) { + executorFunction = executor; + executor(function() {}, function() {}); + } + + Component.onCompleted: { + Promise.resolve.call(notPromise); + wasTestSuccessful = executorFunction !== null && + Object.isExtensible(executorFunction); + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml new file mode 100644 index 0000000000..4e609ae4a4 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-reject.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property string resolution: "reject promise"; + + property bool wasExecutorCalled: false + property bool wasPromiseRejected: false + property bool wasPromiseTypeReturnedByThen: false; + property bool wasResolutionForwardedCorrectly: false; + + Component.onCompleted: { + var promise = new Promise(function(resolve, reject) { + wasExecutorCalled = true; + reject(resolution); + }); + + var res = promise.then(function(result) { + wasPromiseRejected = false; + }, function(err) { + wasPromiseRejected = true; + wasResolutionForwardedCorrectly = (err === resolution); + }); + + wasPromiseTypeReturnedByThen = (typeof res === 'Promise'); + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml new file mode 100644 index 0000000000..659636d6a8 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-resolve.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property string resolution: "fullfill promise"; + + property bool wasExecutorCalled: false + property bool wasPromiseResolved: false + property bool wasPromiseTypeReturnedByThen: false + property bool wasResolutionForwardedCorrectly: false + property bool wasNewPromiseObjectCreatedByThen: false + + Component.onCompleted: { + var promise = new Promise(function(resolve, reject) { + wasExecutorCalled = true; + resolve(resolution); + }); + + var res = promise.then(function(result) { + wasPromiseResolved = true; + wasResolutionForwardedCorrectly = (result === resolution); + }, function(err) { + wasPromiseResolved = false; + }); + + wasPromiseTypeReturnedByThen = (typeof res === 'Promise'); + console.debug("typeof res: " + (typeof res)) // TODO: remove + wasNewPromiseObjectCreatedByThen = (res !== promise); + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml b/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml new file mode 100644 index 0000000000..9e912635d6 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-executor-throw-exception.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property bool wasTestSuccessful: false + + property var errorObject: { text: "Exception should not escape executor" } + + property var promise: new Promise(function() { + throw errorObject + }) + + Component.onCompleted: { + promise.then(function() { + throw new Error("Should never be called") + }, function(error) { + if (error === errorObject) { + wasTestSuccessful = true + } + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-get-length.qml b/tests/auto/qml/qqmlpromise/data/promise-get-length.qml new file mode 100644 index 0000000000..d42bc61179 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-get-length.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property bool wasTestSuccessful: (Promise.length === 1) +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml b/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml new file mode 100644 index 0000000000..188b1c2f15 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-race-empty-input.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property bool wasTestSuccessful: true + + Component.onCompleted: { + Promise.race([]).then(function(value) { + wasTestSuccessful = false + throw new Error("Should never be called") + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml new file mode 100644 index 0000000000..1d6e47b87e --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st-in-executor-function.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int resolveValue: 33 + + property var promise: new Promise(function (resolve, reject) { + resolve(resolveValue) + }) + + property var resolvedPromiseArray: [promise, Promise.resolve(resolveValue + 5)] + property bool wasTestSuccessful: false + + Component.onCompleted: { + Promise.race(resolvedPromiseArray).then(function(value) { + if (value !== resolveValue) { + return; + } + + wasTestSuccessful = true + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml new file mode 100644 index 0000000000..bd7b3a4026 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-1st.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int resolveValue: 33 + property var resolvedPromiseArray: [Promise.resolve(resolveValue), Promise.resolve(resolveValue + 5)] + property bool wasTestSuccessful: false + + Component.onCompleted: { + Promise.race(resolvedPromiseArray).then(function(value) { + if (value !== resolveValue) { + return; + } + + wasTestSuccessful = true + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml new file mode 100644 index 0000000000..e4c1b98acc --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-race-resolve-2nd.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 + +QtObject { + property int resolveValue: 33 + + property var delayedExecutor: Timer { + interval: 0 + property var executor: null + onTriggered: { + if (executor) { + executor(); + } + } + } + + property var promise: new Promise(function (resolve, reject) { + delayedExecutor.executor = function() { + resolve(resolveValue + 5) + } + delayedExecutor.restart(); + }) + + property var resolvedPromiseArray: [promise, Promise.resolve(resolveValue)] + property bool wasTestSuccessful: false + + Component.onCompleted: { + Promise.race(resolvedPromiseArray).then(function(value) { + if (value !== resolveValue) { + return; + } + + wasTestSuccessful = true + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml b/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml new file mode 100644 index 0000000000..a83d2670ba --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-reject-catch.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int rejectValue: 5 + property var promise: Promise.reject(rejectValue) + property bool wasTestSuccessful: false + + Component.onCompleted: { + promise.then(function() { + throw new Error("Should never be called") + }).then(function() { + throw new Error("Should never be called") + }).catch(function(value) { + if (value === rejectValue) { + wasTestSuccessful = true + } + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml new file mode 100644 index 0000000000..6b81e83643 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-reject-with-value.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int rejectValue: 5 + property var promise: Promise.reject(rejectValue) + property bool wasTestSuccessful: false + + Component.onCompleted: { + promise.then(function() { + throw new Error("Should never be called") + }, function(value) { + if (value === rejectValue) { + wasTestSuccessful = true + } + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml new file mode 100644 index 0000000000..40d35e5a3b --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-function-length.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property var resolveFunction + property var promise: new Promise(function(resolve, reject) { + resolveFunction = resolve + }) + + property bool wasTestSuccessful: (typeof resolveFunction === "function" && + typeof resolveFunction.length !== "undefined" && + resolveFunction.length === 1) + + Component.onCompleted: { + // TODO: Function length field should be NotWritabel & NotEnumerable & Configurable + console.log(Object.getOwnPropertyDescriptor(resolveFunction, "length").configurable) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml new file mode 100644 index 0000000000..fbdcc0f8e9 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-is-a-function.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property bool wasTestSuccessful: (typeof Promise.resolve === "function") +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml new file mode 100644 index 0000000000..04d2209907 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-array.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property var resolveValue: [1, 2, 3] + property var promise: Promise.resolve(resolveValue) + property bool wasTestSuccessful: false + + Component.onCompleted: { + promise.then(function(value) { + if (value === resolveValue) { + wasTestSuccessful = true + } + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml new file mode 100644 index 0000000000..61c7bf17a6 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-empty.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property var promise: Promise.resolve() + property bool wasTestSuccessful: false + + Component.onCompleted: { + promise.then(function(value) { + if (typeof value === "undefined") { + wasTestSuccessful = true + } + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml new file mode 100644 index 0000000000..65b39508ad --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-promise.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int resolveValue: 5 + property var originalPromise: Promise.resolve(resolveValue) + + property var castPromise: Promise.resolve(originalPromise) + property bool wasTestSuccessful: false + + Component.onCompleted: { + if (castPromise !== originalPromise) { + console.log("resolve did not return original promise") + return; + } + + castPromise.then(function(value) { + if (value !== resolveValue) { + console.log("resolved values are not the same") + return; + } + + wasTestSuccessful = true + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml new file mode 100644 index 0000000000..a1294cf9a6 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/promise-resolve-with-value.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int resolveValue: 5 + property var promise: Promise.resolve(resolveValue) + property bool wasTestSuccessful: false + + Component.onCompleted: { + promise.then(function(value) { + if (value === resolveValue) { + wasTestSuccessful = true + } + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml b/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml new file mode 100644 index 0000000000..ee7e3ed7be --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/then-fulfilled-non-callable.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int resolveValue: 5 + + property bool promise1WasResolved: false + property bool promise2WasResolved: false + property bool promise3WasResolved: false + property bool promise4WasResolved: true + + property bool wasTestSuccessful: promise1WasResolved && promise2WasResolved && + promise3WasResolved && promise4WasResolved + + // TODO: Should this work as well? + // property Promise promise + property var promise1: new Promise(function (resolve, reject) { + resolve(resolveValue) + }) + property var promise2: new Promise(function (resolve, reject) { + resolve(resolveValue) + }) + property var promise3: new Promise(function (resolve, reject) { + resolve(resolveValue) + }) + property var promise4: new Promise(function (resolve, reject) { + resolve(resolveValue) + }) + + Component.onCompleted: { + promise1.then().then(function (result) { + promise1WasResolved = (result === resolveValue); + }, function() { + throw new Error("Should never be called") + }) + promise2.then(3, 5).then(function (result) { + promise2WasResolved = (result === resolveValue); + }, function() { + throw new Error("Should never be called") + }) + promise3.then(null, function() { + throw new Error("Should never be called") + }).then(function (result) { + promise3WasResolved = (result === resolveValue); + }, function() { + throw new Error("Should never be called") + }) + /* + promise4.then(undefined, undefined).then(function (result) { + promise4WasResolved = (result === resolveValue); + }, function() { + throw new Error("Should never be called") + }) + */ + } +} diff --git a/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml b/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml new file mode 100644 index 0000000000..645ae9b07c --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/then-reject-chaining.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int rejectValue: 1 + property int expectedValue: rejectValue + 2; + property bool wasTestSuccessful: false + + Component.onCompleted: { + var promise = new Promise(function(resolve, reject) { + reject(rejectValue); + }); + + promise.then(function() { + throw new Error("Should never be called") + }, function(val) { + return val + 2; + }).then(function(val) { + if (val === expectedValue) { + wasTestSuccessful = true + } + }, function() { + throw new Error("Should never be called") + }); + } +} diff --git a/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml b/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml new file mode 100644 index 0000000000..df708c6218 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/then-reject-non-callable.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int rejectValue: 5 + + property bool promise1WasRejected: false + property bool promise2WasRejected: false + + property bool wasTestSuccessful: promise1WasRejected && promise2WasRejected + + // TODO: Should this work as well? + // property Promise promise + property var promise1: new Promise(function (resolve, reject) { + reject(rejectValue) + }) + property var promise2: new Promise(function (resolve, reject) { + reject(rejectValue) + }) + + Component.onCompleted: { + promise1.then().then(function() { + promise1WasRejected = false + throw new Error("Should never be called") + }, function (result) { + promise1WasRejected = (result === rejectValue); + }) + promise2.then(3, 5).then(function() { + promise2WasRejected = false + throw new Error("Should never be called") + }, function (result) { + promise2WasRejected = (result === rejectValue); + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml b/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml new file mode 100644 index 0000000000..d65e9c86ac --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/then-resolve-chaining.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int resolveValue: 1 + property int expectedValue: resolveValue + 2; + property bool wasTestSuccessful: false + + Component.onCompleted: { + var promise = new Promise(function(resolve, reject) { + resolve(resolveValue); + }); + + promise.then(function(val) { + return val + 2; + }).then(function(val) { + if (val === expectedValue) { + wasTestSuccessful = true; + } + }); + } +} diff --git a/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml b/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml new file mode 100644 index 0000000000..48000d5ddc --- /dev/null +++ b/tests/auto/qml/qqmlpromise/data/then-resolve-multiple-then.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +QtObject { + property int resolveValue: 5 + + property bool was1stCallSucessfull: false + property bool was2ndCallSucessfull: false + + property bool wasTestSuccessful: was1stCallSucessfull && was2ndCallSucessfull + + property var promise: new Promise(function (resolve, reject) { + resolve(resolveValue) + }) + + Component.onCompleted: { + promise.then(function (result) { + was1stCallSucessfull = (result === resolveValue); + }, function() { + throw new Error("Should never be called") + }) + promise.then(function (result) { + was2ndCallSucessfull = (result === resolveValue); + }, function() { + throw new Error("Should never be called") + }) + } +} diff --git a/tests/auto/qml/qqmlpromise/qqmlpromise.pro b/tests/auto/qml/qqmlpromise/qqmlpromise.pro new file mode 100644 index 0000000000..7cdb02cd61 --- /dev/null +++ b/tests/auto/qml/qqmlpromise/qqmlpromise.pro @@ -0,0 +1,46 @@ +CONFIG += testcase +TARGET = tst_qqmlpromise +macos:CONFIG -= app_bundle + +SOURCES += tst_qqmlpromise.cpp + +OTHER_FILES += + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + +DISTFILES += \ + data/then-fulfilled-non-callable.qml \ + data/then-reject-non-callable.qml \ + data/then-resolve-multiple-then.qml \ + data/then-resolve-chaining.qml \ + data/then-reject-chaining.qml \ + data/promise-resolve-with-value.qml \ + data/promise-resolve-with-promise.qml \ + data/promise-reject-with-value.qml \ + data/promise-executor-resolve.qml \ + data/promise-get-length.qml \ + data/promise-executor-reject.qml \ + data/promise-reject-catch.qml \ + data/promise-async-resolve-with-value.qml \ + data/promise-async-reject-with-value.qml \ + data/promise-all-resolve.qml \ + data/promise-all-empty-input.qml \ + data/promise-resolve-with-array.qml \ + data/promise-all-reject-reject-is-last.qml \ + data/promise-all-reject-reject-is-mid.qml \ + data/promise-race-resolve-1st.qml \ + data/promise-race-empty-input.qml \ + data/promise-race-resolve-2nd.qml \ + data/promise-race-resolve-1st-in-executor-function.qml \ + data/promise-resolve-is-a-function.qml \ + data/promise-resolve-function-length.qml \ + data/promise-all-invoke-then-method.qml \ + data/promise-resolve-with-empty.qml \ + data/promise-executor-throw-exception.qml \ + data/promise-executor-function-extensible.qml \ + data/promise-all-noniterable-input.qml diff --git a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp new file mode 100644 index 0000000000..0f4bb5cdcc --- /dev/null +++ b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QString> +#include <QtTest> +#include <QQmlEngine> +#include <QQmlComponent> +#include <QDebug> +#include <QScopedPointer> +#include "../../shared/util.h" + +class tst_qqmlpromise : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_qqmlpromise() {} + +private slots: + void promise_all_empty_input(); + void promise_all_noniterable_input(); +// void promise_all_invoke_then_method(); + void promise_all_resolve(); + void promise_all_reject_reject_is_last(); + void promise_all_reject_reject_is_mid(); + void promise_get_length(); + void promise_executor_function_extensible(); + void promise_executor_reject(); + void promise_executor_resolve(); + void promise_executor_throw_exception(); + void promise_async_resolve_with_value(); + void promise_async_reject_with_value(); + void promise_resolve_with_value(); + void promise_resolve_function_length(); + void promise_resolve_is_a_function(); + void promise_resolve_with_array(); + void promise_resolve_with_empty(); + void promise_resolve_with_promise(); + void promise_race_empty_input(); + void promise_race_resolve_1st_in_executor_function(); + void promise_race_resolve_1st(); + void promise_race_resolve_2nd(); + void promise_reject_with_value(); + void promise_reject_catch(); + void then_resolve_chaining(); + void then_reject_chaining(); + void then_fulfilled_non_callable(); + void then_reject_non_callable(); + void then_resolve_multiple_then(); + +private: + void execute_test(QString testName); +}; + +void tst_qqmlpromise::promise_all_empty_input() +{ + execute_test("promise-all-empty-input.qml"); +} + +void tst_qqmlpromise::promise_all_noniterable_input() +{ + execute_test("promise-all-noniterable-input.qml"); +} + +// TODO: Fix the test +//void tst_qqmlpromise::promise_all_invoke_then_method() +//{ +// execute_test("promise-all-invoke-then-method.qml"); +//} + +void tst_qqmlpromise::promise_all_resolve() +{ + execute_test("promise-all-resolve.qml"); +} + +void tst_qqmlpromise::promise_all_reject_reject_is_last() +{ + execute_test("promise-all-reject-reject-is-last.qml"); +} + +void tst_qqmlpromise::promise_all_reject_reject_is_mid() +{ + execute_test("promise-all-reject-reject-is-mid.qml"); +} + +void tst_qqmlpromise::promise_get_length() +{ + execute_test("promise-get-length.qml"); +} + +void tst_qqmlpromise::promise_executor_resolve() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("promise-executor-resolve.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); + component.completeCreate(); + + QTRY_COMPARE(object->property("wasExecutorCalled").toBool(), true); + QTRY_COMPARE(object->property("wasPromiseResolved").toBool(), true); + // TODO: now "object" type is returned. fix + // QCOMPARE(object->property("wasPromiseTypeReturnedByThen").toBool(), true); + QTRY_COMPARE(object->property("wasResolutionForwardedCorrectly").toBool(), true); + QTRY_COMPARE(object->property("wasNewPromiseObjectCreatedByThen").toBool(), true); +} + +void tst_qqmlpromise::promise_executor_throw_exception() +{ + execute_test("promise-executor-throw-exception.qml"); +} + +void tst_qqmlpromise::promise_async_resolve_with_value() +{ + execute_test("promise-async-resolve-with-value.qml"); +} + +void tst_qqmlpromise::promise_async_reject_with_value() +{ + execute_test("promise-async-reject-with-value.qml"); +} + +void tst_qqmlpromise::promise_resolve_with_value() +{ + execute_test("promise-resolve-with-value.qml"); +} + +void tst_qqmlpromise::promise_resolve_function_length() +{ + execute_test("promise-resolve-function-length.qml"); +} + +void tst_qqmlpromise::promise_resolve_is_a_function() +{ + execute_test("promise-resolve-is-a-function.qml"); +} + +void tst_qqmlpromise::promise_resolve_with_array() +{ + execute_test("promise-resolve-with-array.qml"); +} + +void tst_qqmlpromise::promise_resolve_with_empty() +{ + execute_test("promise-resolve-with-empty.qml"); +} + +void tst_qqmlpromise::promise_resolve_with_promise() +{ + execute_test("promise-resolve-with-promise.qml"); +} + +void tst_qqmlpromise::promise_race_empty_input() +{ + execute_test("promise-race-empty-input.qml"); +} + +void tst_qqmlpromise::promise_race_resolve_1st_in_executor_function() +{ + execute_test("promise-race-resolve-1st-in-executor-function.qml"); +} + +void tst_qqmlpromise::promise_race_resolve_1st() +{ + execute_test("promise-race-resolve-1st.qml"); +} + +void tst_qqmlpromise::promise_race_resolve_2nd() +{ + execute_test("promise-race-resolve-2nd.qml"); +} + +void tst_qqmlpromise::promise_reject_with_value() +{ + execute_test("promise-reject-with-value.qml"); +} + +void tst_qqmlpromise::promise_reject_catch() +{ + execute_test("promise-reject-catch.qml"); +} + +void tst_qqmlpromise::promise_executor_function_extensible() +{ + execute_test("promise-executor-function-extensible.qml"); +} + +void tst_qqmlpromise::promise_executor_reject() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("promise-executor-reject.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); + component.completeCreate(); + + QTRY_COMPARE(object->property("wasExecutorCalled").toBool(), true); + QTRY_COMPARE(object->property("wasPromiseRejected").toBool(), true); + // TODO: now "object" type is returned. fix + // QCOMPARE(object->property("wasPromiseTypeReturnedByThen").toBool(), true); + QTRY_COMPARE(object->property("wasResolutionForwardedCorrectly").toBool(), true); +} + +void tst_qqmlpromise::then_resolve_chaining() +{ + execute_test("then-resolve-chaining.qml"); +} + +void tst_qqmlpromise::then_reject_chaining() +{ + execute_test("then-reject-chaining.qml"); +} + +void tst_qqmlpromise::then_fulfilled_non_callable() +{ + execute_test("then-fulfilled-non-callable.qml"); +} + +void tst_qqmlpromise::then_reject_non_callable() +{ + execute_test("then-reject-non-callable.qml"); +} + +void tst_qqmlpromise::then_resolve_multiple_then() +{ + execute_test("then-resolve-multiple-then.qml"); +} + +void tst_qqmlpromise::execute_test(QString testName) +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl(testName)); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); + component.completeCreate(); + + QTRY_COMPARE(object->property("wasTestSuccessful").toBool(), true); +} + + +QTEST_MAIN(tst_qqmlpromise) + +#include "tst_qqmlpromise.moc" diff --git a/tests/auto/qml/qqmlproperty/data/aliasToIdWithMatchingQmlFileName.qml b/tests/auto/qml/qqmlproperty/data/aliasToIdWithMatchingQmlFileName.qml new file mode 100644 index 0000000000..8cbd928f36 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/aliasToIdWithMatchingQmlFileName.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + property alias testType: testType + + Rectangle { + id: testType + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 84a1bd9cc5..27e06c6f67 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -44,7 +44,7 @@ class MyQmlObject : public QObject Q_OBJECT Q_PROPERTY(QPoint pointProperty MEMBER m_point) public: - MyQmlObject(QObject *parent = 0) : QObject(parent) {} + MyQmlObject(QObject *parent = nullptr) : QObject(parent) {} private: QPoint m_point; @@ -56,7 +56,7 @@ class MyQObject : public QObject { Q_OBJECT public: - MyQObject(QObject *parent = 0) : QObject(parent), m_i(0) {} + MyQObject(QObject *parent = nullptr) : QObject(parent), m_i(0) {} int inc() { return ++m_i; } @@ -143,6 +143,7 @@ private slots: void registeredCompositeTypeProperty(); void deeplyNestedObject(); void readOnlyDynamicProperties(); + void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem(); void floatToStringPrecision_data(); void floatToStringPrecision(); @@ -158,21 +159,21 @@ void tst_qqmlproperty::qmlmetaproperty() QObject *obj = new QObject; - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QCOMPARE(prop.name(), QString()); QCOMPARE(prop.read(), QVariant()); QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -183,10 +184,10 @@ void tst_qqmlproperty::qmlmetaproperty() QCOMPARE(prop.isResettable(), false); QCOMPARE(prop.isSignalProperty(), false); QCOMPARE(prop.isValid(), false); - QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.object(), (QObject *)nullptr); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); QVERIFY(!prop.property().name()); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); @@ -333,7 +334,7 @@ class PropertyObject : public QObject Q_CLASSINFO("DefaultProperty", "defaultProperty") public: - PropertyObject() : m_resetProperty(9), m_qObject(0), m_stringProperty("foo") {} + PropertyObject() : m_resetProperty(9), m_qObject(nullptr), m_stringProperty("foo") {} int defaultProperty() { return 10; } QRect rectProperty() { return QRect(10, 10, 1, 209); } @@ -400,11 +401,11 @@ void tst_qqmlproperty::qmlmetaproperty_object() { QQmlProperty prop(&object); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -413,10 +414,10 @@ void tst_qqmlproperty::qmlmetaproperty_object() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -427,10 +428,10 @@ void tst_qqmlproperty::qmlmetaproperty_object() QCOMPARE(prop.isResettable(), false); QCOMPARE(prop.isSignalProperty(), false); QCOMPARE(prop.isValid(), false); - QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.object(), (QObject *)nullptr); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); QVERIFY(!prop.property().name()); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); @@ -448,12 +449,12 @@ void tst_qqmlproperty::qmlmetaproperty_object() { QQmlProperty prop(&dobject); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -462,10 +463,10 @@ void tst_qqmlproperty::qmlmetaproperty_object() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), true); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -505,11 +506,11 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&object, QString("defaultProperty")); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -518,10 +519,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -532,10 +533,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.isResettable(), false); QCOMPARE(prop.isSignalProperty(), false); QCOMPARE(prop.isValid(), false); - QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.object(), (QObject *)nullptr); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); QVERIFY(!prop.property().name()); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); @@ -553,12 +554,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("defaultProperty")); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -567,10 +568,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), true); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -604,12 +605,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("onClicked")); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -618,10 +619,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.write(QVariant("Hello")), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QCOMPARE(QString(prop.method().methodSignature()), QString("clicked()")); @@ -635,8 +636,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject)); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); - QCOMPARE(prop.property().name(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); + QCOMPARE(prop.property().name(), (const char *)nullptr); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); QVERIFY(binding->ref == 1); @@ -654,12 +655,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() { QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged")); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -668,10 +669,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.write(QVariant("Hello")), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QCOMPARE(QString(prop.method().methodSignature()), QString("oddlyNamedNotifySignal()")); @@ -685,8 +686,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject)); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); - QCOMPARE(prop.property().name(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); + QCOMPARE(prop.property().name(), (const char *)nullptr); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); QVERIFY(binding->ref == 1); @@ -710,11 +711,11 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() { QQmlProperty prop(&object, engine.rootContext()); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -723,10 +724,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -737,10 +738,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QCOMPARE(prop.isResettable(), false); QCOMPARE(prop.isSignalProperty(), false); QCOMPARE(prop.isValid(), false); - QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.object(), (QObject *)nullptr); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); QVERIFY(!prop.property().name()); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); @@ -758,12 +759,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() { QQmlProperty prop(&dobject, engine.rootContext()); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -772,10 +773,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), true); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -815,11 +816,11 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&object, QString("defaultProperty"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -828,10 +829,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -842,10 +843,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.isResettable(), false); QCOMPARE(prop.isSignalProperty(), false); QCOMPARE(prop.isValid(), false); - QCOMPARE(prop.object(), (QObject *)0); + QCOMPARE(prop.object(), (QObject *)nullptr); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); QVERIFY(!prop.property().name()); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); @@ -863,12 +864,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("defaultProperty"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -877,10 +878,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.write(QVariant()), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), true); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QVERIFY(!prop.method().isValid()); @@ -914,12 +915,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("onClicked"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -928,10 +929,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.write(QVariant("Hello")), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QCOMPARE(QString(prop.method().methodSignature()), QString("clicked()")); @@ -945,8 +946,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject)); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); - QCOMPARE(prop.property().name(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); + QCOMPARE(prop.property().name(), (const char *)nullptr); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); QVERIFY(binding->ref == 1); @@ -964,12 +965,12 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() { QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext()); - QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, QQmlContextData::get(engine.rootContext()))); + QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), nullptr, QQmlContextData::get(engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding); - QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1); QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr); - QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); + QVERIFY(sigExpr != nullptr && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -978,10 +979,10 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.write(QVariant("Hello")), false); QCOMPARE(prop.hasNotifySignal(), false); QCOMPARE(prop.needsNotifySignal(), false); - QCOMPARE(prop.connectNotifySignal(0, SLOT(deleteLater())), false); + QCOMPARE(prop.connectNotifySignal(nullptr, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, SLOT(deleteLater())), false); QCOMPARE(prop.connectNotifySignal(obj, 0), false); - QCOMPARE(prop.connectNotifySignal(0, obj->metaObject()->indexOfMethod("deleteLater()")), false); + QCOMPARE(prop.connectNotifySignal(nullptr, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, obj->metaObject()->indexOfMethod("deleteLater()")), false); QCOMPARE(prop.connectNotifySignal(obj, -1), false); QCOMPARE(QString(prop.method().methodSignature()), QString("oddlyNamedNotifySignal()")); @@ -995,8 +996,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QCOMPARE(prop.object(), qobject_cast<QObject*>(&dobject)); QCOMPARE(prop.propertyTypeCategory(), QQmlProperty::InvalidCategory); QCOMPARE(prop.propertyType(), 0); - QCOMPARE(prop.propertyTypeName(), (const char *)0); - QCOMPARE(prop.property().name(), (const char *)0); + QCOMPARE(prop.propertyTypeName(), (const char *)nullptr); + QCOMPARE(prop.property().name(), (const char *)nullptr); QVERIFY(!QQmlPropertyPrivate::binding(prop)); QQmlPropertyPrivate::setBinding(prop, binding.data()); QVERIFY(binding->ref == 1); @@ -1062,7 +1063,7 @@ void tst_qqmlproperty::name() } { - QQmlProperty p(0, "foo"); + QQmlProperty p(nullptr, "foo"); QCOMPARE(p.name(), QString()); } @@ -1150,8 +1151,8 @@ void tst_qqmlproperty::read() QQmlProperty p(&o, "onClicked"); QCOMPARE(p.read(), QVariant()); - QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1)); - QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); + QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1)); + QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.read(), QVariant()); } @@ -1162,8 +1163,8 @@ void tst_qqmlproperty::read() QQmlProperty p(&o, "onPropertyWithNotifyChanged"); QCOMPARE(p.read(), QVariant()); - QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1)); - QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); + QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1)); + QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.read(), QVariant()); } @@ -1213,7 +1214,7 @@ void tst_qqmlproperty::read() { QQmlComponent component(&engine, testFileUrl("readSynthesizedObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlProperty p(object, "test", &engine); @@ -1228,7 +1229,7 @@ void tst_qqmlproperty::read() { // static QQmlComponent component(&engine, testFileUrl("readSynthesizedObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant v = QQmlProperty::read(object, "test", &engine); QCOMPARE(v.userType(), int(QMetaType::QObjectStar)); @@ -1241,7 +1242,7 @@ void tst_qqmlproperty::read() QQmlComponent component(&engine); component.setData("import Test 1.0\nMyContainer { }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlProperty p(object, "MyContainer.foo", qmlContext(object)); QCOMPARE(p.read(), QVariant(13)); @@ -1251,7 +1252,7 @@ void tst_qqmlproperty::read() QQmlComponent component(&engine); component.setData("import Test 1.0\nMyContainer { MyContainer.foo: 10 }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlProperty p(object, "MyContainer.foo", qmlContext(object)); QCOMPARE(p.read(), QVariant(10)); @@ -1261,7 +1262,7 @@ void tst_qqmlproperty::read() QQmlComponent component(&engine); component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlProperty p(object, "Foo.MyContainer.foo", qmlContext(object)); QCOMPARE(p.read(), QVariant(10)); @@ -1271,7 +1272,7 @@ void tst_qqmlproperty::read() QQmlComponent component(&engine); component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(QQmlProperty::read(object, "Foo.MyContainer.foo", qmlContext(object)), QVariant(10)); delete object; @@ -1341,12 +1342,12 @@ void tst_qqmlproperty::write() QQmlProperty p(&o, "onClicked"); QCOMPARE(p.write(QVariant("console.log(1921)")), false); - QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1)); - QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); + QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1)); + QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.write(QVariant("console.log(1921)")), false); - QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); + QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p)); } // Automatic signal property @@ -1355,12 +1356,12 @@ void tst_qqmlproperty::write() QQmlProperty p(&o, "onPropertyWithNotifyChanged"); QCOMPARE(p.write(QVariant("console.log(1921)")), false); - QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1)); - QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); + QQmlPropertyPrivate::takeSignalExpression(p, new QQmlBoundSignalExpression(&o, QQmlPropertyPrivate::get(p)->signalIndex(), QQmlContextData::get(engine.rootContext()), nullptr, QLatin1String("null"), QString(), -1, -1)); + QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.write(QVariant("console.log(1921)")), false); - QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); + QVERIFY(nullptr != QQmlPropertyPrivate::signalExpression(p)); } // Value-type property @@ -1436,7 +1437,7 @@ void tst_qqmlproperty::write() QQmlComponent component(&engine); component.setData("import Test 1.0\nPropertyObject { stringProperty: constQChar }", QUrl()); PropertyObject *obj = qobject_cast<PropertyObject*>(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); if (obj) { QQmlProperty stringProperty(obj, "stringProperty"); QCOMPARE(stringProperty.read(), QVariant(QString(obj->constQChar()))); @@ -1476,7 +1477,7 @@ void tst_qqmlproperty::write() QQmlComponent component(&engine); component.setData("import Test 1.0\nMyContainer { }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlProperty p(object, "MyContainer.foo", qmlContext(object)); p.write(QVariant(99)); @@ -1487,7 +1488,7 @@ void tst_qqmlproperty::write() QQmlComponent component(&engine); component.setData("import Test 1.0 as Foo\nFoo.MyContainer { Foo.MyContainer.foo: 10 }", QUrl()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QQmlProperty p(object, "Foo.MyContainer.foo", qmlContext(object)); p.write(QVariant(99)); @@ -1498,7 +1499,7 @@ void tst_qqmlproperty::write() { PropertyObject o; QQmlProperty p(&o, QString("qObject")); - QCOMPARE(o.qObject(), (QObject*)0); + QCOMPARE(o.qObject(), (QObject*)nullptr); QObject *newObject = new MyQObject(this); QCOMPARE(p.write(QVariant::fromValue(newObject)), true); QCOMPARE(o.qObject(), newObject); @@ -1515,7 +1516,7 @@ void tst_qqmlproperty::write() QQmlEngine engine; PropertyObject o; QQmlProperty p(&o, QString("qObject"), &engine); - QCOMPARE(o.qObject(), (QObject*)0); + QCOMPARE(o.qObject(), (QObject*)nullptr); QObject *newObject = new MyQObject(this); QCOMPARE(p.write(QVariant::fromValue(newObject)), true); QCOMPARE(o.qObject(), newObject); @@ -1617,7 +1618,7 @@ void tst_qqmlproperty::writeObjectToList() QQmlComponent containerComponent(&engine); containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl()); MyContainer *container = qobject_cast<MyContainer*>(containerComponent.create()); - QVERIFY(container != 0); + QVERIFY(container != nullptr); QQmlListReference list(container, "children"); QCOMPARE(list.count(), 1); @@ -1633,7 +1634,7 @@ void tst_qqmlproperty::writeListToList() QQmlComponent containerComponent(&engine); containerComponent.setData("import Test 1.0\nMyContainer { children: MyQmlObject {} }", QUrl()); MyContainer *container = qobject_cast<MyContainer*>(containerComponent.create()); - QVERIFY(container != 0); + QVERIFY(container != nullptr); QQmlListReference list(container, "children"); QCOMPARE(list.count(), 1); @@ -1817,7 +1818,7 @@ void tst_qqmlproperty::crashOnValueProperty() component.setData("import Test 1.0\nPropertyObject { wrectProperty.x: 10 }", QUrl()); PropertyObject *obj = qobject_cast<PropertyObject*>(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QQmlProperty p(obj, "wrectProperty.x", qmlContext(obj)); QCOMPARE(p.name(), QString("wrectProperty.x")); @@ -1826,7 +1827,7 @@ void tst_qqmlproperty::crashOnValueProperty() //don't crash once the engine is deleted delete engine; - engine = 0; + engine = nullptr; QCOMPARE(p.propertyTypeName(), "int"); QCOMPARE(p.read(), QVariant(10)); @@ -1852,7 +1853,7 @@ void tst_qqmlproperty::aliasPropertyBindings() QQmlComponent component(&engine, testFileUrl(file)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // the object where realProperty lives QObject *realPropertyObject = object; @@ -1871,8 +1872,8 @@ void tst_qqmlproperty::aliasPropertyBindings() QQmlProperty aliasProperty(object, QLatin1String("aliasProperty")); // Check there is a binding on these two properties - QVERIFY(QQmlPropertyPrivate::binding(realProperty) != 0); - QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != 0); + QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr); + QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr); // Check that its the same binding on these two properties QCOMPARE(QQmlPropertyPrivate::binding(realProperty), @@ -1881,8 +1882,8 @@ void tst_qqmlproperty::aliasPropertyBindings() // Change the binding object->setProperty("state", QString("switch")); - QVERIFY(QQmlPropertyPrivate::binding(realProperty) != 0); - QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != 0); + QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr); + QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr); QCOMPARE(QQmlPropertyPrivate::binding(realProperty), QQmlPropertyPrivate::binding(aliasProperty)); @@ -1903,8 +1904,8 @@ void tst_qqmlproperty::aliasPropertyBindings() // Revert object->setProperty("state", QString("")); - QVERIFY(QQmlPropertyPrivate::binding(realProperty) != 0); - QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != 0); + QVERIFY(QQmlPropertyPrivate::binding(realProperty) != nullptr); + QVERIFY(QQmlPropertyPrivate::binding(aliasProperty) != nullptr); QCOMPARE(QQmlPropertyPrivate::binding(realProperty), QQmlPropertyPrivate::binding(aliasProperty)); @@ -1946,7 +1947,7 @@ void tst_qqmlproperty::copy() QCOMPARE(p2.propertyTypeCategory(), QQmlProperty::Normal); QCOMPARE(p2.propertyType(), (int)QVariant::Int); - delete property; property = 0; + delete property; property = nullptr; QCOMPARE(p1.name(), QString("defaultProperty")); QCOMPARE(p1.read(), QVariant(10)); @@ -1967,9 +1968,9 @@ void tst_qqmlproperty::noContext() QQmlComponent compB(&engine, testFileUrl("NoContextTypeB.qml")); QObject *a = compA.create(); - QVERIFY(a != 0); + QVERIFY(a != nullptr); QObject *b = compB.create(); - QVERIFY(b != 0); + QVERIFY(b != nullptr); QVERIFY(QQmlProperty::write(b, "myTypeA", QVariant::fromValue(a), &engine)); @@ -2006,15 +2007,15 @@ void tst_qqmlproperty::warnOnInvalidBinding() QString expectedWarning; // V4 error message for property-to-property binding - expectedWarning = testUrl.toString() + QString::fromLatin1(":6:36: Unable to assign QQuickText to QQuickRectangle"); + expectedWarning = testUrl.toString() + QString::fromLatin1(":6:5: Unable to assign QQuickText to QQuickRectangle"); QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); // V8 error message for function-to-property binding - expectedWarning = testUrl.toString() + QString::fromLatin1(":7:36: Unable to assign QQuickText to QQuickRectangle"); + expectedWarning = testUrl.toString() + QString::fromLatin1(":7:5: Unable to assign QQuickText to QQuickRectangle"); QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); // V8 error message for invalid binding to anchor - expectedWarning = testUrl.toString() + QString::fromLatin1(":14:33: Unable to assign QQuickItem_QML_8 to QQuickAnchorLine"); + expectedWarning = testUrl.toString() + QString::fromLatin1(":14:9: Unable to assign QQuickItem_QML_8 to QQuickAnchorLine"); QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); QQmlComponent component(&engine, testUrl); @@ -2037,7 +2038,7 @@ void tst_qqmlproperty::readOnlyDynamicProperties() { QQmlComponent comp(&engine, testFileUrl("readonlyPrimitiveVsVar.qml")); QObject *obj = comp.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QVERIFY(!obj->metaObject()->property(obj->metaObject()->indexOfProperty("r_var")).isWritable()); QVERIFY(!obj->metaObject()->property(obj->metaObject()->indexOfProperty("r_int")).isWritable()); @@ -2047,6 +2048,17 @@ void tst_qqmlproperty::readOnlyDynamicProperties() delete obj; } +void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem() +{ + const QUrl url = testFileUrl("aliasToIdWithMatchingQmlFileName.qml"); + QQmlEngine engine; + QQmlComponent component(&engine, url); + QScopedPointer<QObject> root(component.create()); + + QQmlProperty property(root.data(), "testType.objectName", QQmlEngine::contextForObject(root.data())); + QVERIFY(property.isValid()); +} + void tst_qqmlproperty::floatToStringPrecision_data() { QTest::addColumn<QString>("propertyName"); @@ -2054,19 +2066,19 @@ void tst_qqmlproperty::floatToStringPrecision_data() QTest::addColumn<QString>("qtString"); QTest::addColumn<QString>("jsString"); - QTest::newRow("3.4") << "a" << 3.4 << "3.4" << "3.4"; - QTest::newRow("0.035003945") << "b" << 0.035003945 << "0.035003945" << "0.035003945"; - QTest::newRow("0.0000012345") << "c" << 0.0000012345 << "1.2345e-6" << "0.0000012345"; - QTest::newRow("0.00000012345") << "d" << 0.00000012345 << "1.2345e-7" << "1.2345e-7"; - QTest::newRow("1e20") << "e" << 1e20 << "1e+20" << "100000000000000000000"; - QTest::newRow("1e21") << "f" << 1e21 << "1e+21" << "1e+21"; + QTest::newRow("3.4") << "a" << 3.4 << "3.4" << "3.4"; + QTest::newRow("0.035003945") << "b" << 0.035003945 << "0.035003945" << "0.035003945"; + QTest::newRow("0.0000012345") << "c" << 0.0000012345 << "1.2345e-06" << "0.0000012345"; + QTest::newRow("0.00000012345") << "d" << 0.00000012345 << "1.2345e-07" << "1.2345e-7"; + QTest::newRow("1e20") << "e" << 1e20 << "1e+20" << "100000000000000000000"; + QTest::newRow("1e21") << "f" << 1e21 << "1e+21" << "1e+21"; } void tst_qqmlproperty::floatToStringPrecision() { QQmlComponent comp(&engine, testFileUrl("floatToStringPrecision.qml")); - QObject *obj = comp.create(); - QVERIFY(obj != 0); + QScopedPointer<QObject> obj(comp.create()); + QVERIFY(obj != nullptr); QFETCH(QString, propertyName); QFETCH(double, number); @@ -2084,8 +2096,6 @@ void tst_qqmlproperty::floatToStringPrecision() QByteArray name2 = (propertyName + QLatin1Char('2')).toLatin1(); QCOMPARE(obj->property(name2).toDouble(), number); QCOMPARE(obj->property(name2).toString(), jsString); - - delete obj; } void tst_qqmlproperty::initTestCase() diff --git a/tests/auto/qml/qqmlpropertycache/data/foreignEnums.qml b/tests/auto/qml/qqmlpropertycache/data/foreignEnums.qml new file mode 100644 index 0000000000..b7492e507e --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/foreignEnums.qml @@ -0,0 +1,17 @@ +import QtQml 2.2 +import example 1.0 + +QtObject { + Component.onCompleted: { + var opt1 = MyEnum.Option1A | MyEnum.Option1D // 0x09 + mydata.opt1 = opt1; + mydata.setOpt1(opt1); + mydata.setOption1(opt1); + + var opt2 = mydata.opt2; + opt2 = (opt2 === MyEnum.Short8 ? MyEnum.Short16 : MyEnum.Short0); + mydata.opt2 = opt2; + mydata.setOpt2(opt2); + mydata.setOption2(opt2); + } +} diff --git a/tests/auto/qml/qqmlpropertycache/data/passQGadget.qml b/tests/auto/qml/qqmlpropertycache/data/passQGadget.qml new file mode 100644 index 0000000000..86fdd920ed --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/passQGadget.qml @@ -0,0 +1,12 @@ +import QtQml 2.2 + +QtObject { + property var result; + + property Connections connections: Connections { + target: emitter + onEmitGadget: function(gadget) { + result = gadget.someProperty; + } + } +} diff --git a/tests/auto/qml/qqmlpropertycache/qqmlpropertycache.pro b/tests/auto/qml/qqmlpropertycache/qqmlpropertycache.pro index 9a04c899fe..26d41bbdbf 100644 --- a/tests/auto/qml/qqmlpropertycache/qqmlpropertycache.pro +++ b/tests/auto/qml/qqmlpropertycache/qqmlpropertycache.pro @@ -4,4 +4,8 @@ macx:CONFIG -= app_bundle SOURCES += tst_qqmlpropertycache.cpp +include (../../shared/util.pri) + +TESTDATA = data/* + QT += core-private gui-private qml-private testlib diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 824fe445c0..02b5302a45 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -29,12 +29,14 @@ #include <qtest.h> #include <private/qqmlpropertycache_p.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlcomponent.h> #include <private/qv8engine_p.h> #include <private/qmetaobjectbuilder_p.h> #include <QCryptographicHash> #include "../../shared/util.h" -class tst_qqmlpropertycache : public QObject +class tst_qqmlpropertycache : public QQmlDataTest { Q_OBJECT public: @@ -43,10 +45,13 @@ public: private slots: void properties(); void propertiesDerived(); + void revisionedProperties(); void methods(); void methodsDerived(); void signalHandlers(); void signalHandlersDerived(); + void passForeignEnums(); + void passQGadget(); void metaObjectSize_data(); void metaObjectSize(); void metaObjectChecksum(); @@ -61,7 +66,7 @@ class BaseObject : public QObject Q_PROPERTY(int propertyA READ propertyA NOTIFY propertyAChanged) Q_PROPERTY(QString propertyB READ propertyB NOTIFY propertyBChanged) public: - BaseObject(QObject *parent = 0) : QObject(parent) {} + BaseObject(QObject *parent = nullptr) : QObject(parent) {} int propertyA() const { return 0; } QString propertyB() const { return QString(); } @@ -80,11 +85,13 @@ class DerivedObject : public BaseObject Q_OBJECT Q_PROPERTY(int propertyC READ propertyC NOTIFY propertyCChanged) Q_PROPERTY(QString propertyD READ propertyD NOTIFY propertyDChanged) + Q_PROPERTY(int propertyE READ propertyE NOTIFY propertyEChanged REVISION 1) public: - DerivedObject(QObject *parent = 0) : BaseObject(parent) {} + DerivedObject(QObject *parent = nullptr) : BaseObject(parent) {} int propertyC() const { return 0; } QString propertyD() const { return QString(); } + int propertyE() const { return 0; } public Q_SLOTS: void slotB() {} @@ -92,22 +99,22 @@ public Q_SLOTS: Q_SIGNALS: void propertyCChanged(); void propertyDChanged(); + Q_REVISION(1) void propertyEChanged(); void signalB(); }; -QQmlPropertyData *cacheProperty(QQmlPropertyCache *cache, const char *name) +QQmlPropertyData *cacheProperty(const QQmlRefPointer<QQmlPropertyCache> &cache, const char *name) { - return cache->property(QLatin1String(name), 0, 0); + return cache->property(QLatin1String(name), nullptr, nullptr); } void tst_qqmlpropertycache::properties() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); DerivedObject object; const QMetaObject *metaObject = object.metaObject(); - QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject)); + QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(metaObject)); QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "propertyA"))); @@ -126,11 +133,10 @@ void tst_qqmlpropertycache::properties() void tst_qqmlpropertycache::propertiesDerived() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); DerivedObject object; const QMetaObject *metaObject = object.metaObject(); - QQmlRefPointer<QQmlPropertyCache> parentCache(new QQmlPropertyCache(v4, &BaseObject::staticMetaObject)); + QQmlRefPointer<QQmlPropertyCache> parentCache(new QQmlPropertyCache(&BaseObject::staticMetaObject)); QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; @@ -147,14 +153,30 @@ void tst_qqmlpropertycache::propertiesDerived() QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD")); } +void tst_qqmlpropertycache::revisionedProperties() +{ + // Check that if you create a QQmlPropertyCache from a QMetaObject together + // with an explicit revision, the cache will then, and only then, report a + // property with a matching revision as available. + DerivedObject object; + const QMetaObject *metaObject = object.metaObject(); + + QQmlRefPointer<QQmlPropertyCache> cacheWithoutVersion(new QQmlPropertyCache(metaObject)); + QQmlRefPointer<QQmlPropertyCache> cacheWithVersion(new QQmlPropertyCache(metaObject, 1)); + QQmlPropertyData *data; + + QVERIFY((data = cacheProperty(cacheWithoutVersion, "propertyE"))); + QCOMPARE(cacheWithoutVersion->isAllowedInRevision(data), false); + QCOMPARE(cacheWithVersion->isAllowedInRevision(data), true); +} + void tst_qqmlpropertycache::methods() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); DerivedObject object; const QMetaObject *metaObject = object.metaObject(); - QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject)); + QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(metaObject)); QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "slotA"))); @@ -185,11 +207,10 @@ void tst_qqmlpropertycache::methods() void tst_qqmlpropertycache::methodsDerived() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); DerivedObject object; const QMetaObject *metaObject = object.metaObject(); - QQmlRefPointer<QQmlPropertyCache> parentCache(new QQmlPropertyCache(v4, &BaseObject::staticMetaObject)); + QQmlRefPointer<QQmlPropertyCache> parentCache(new QQmlPropertyCache(&BaseObject::staticMetaObject)); QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; @@ -221,11 +242,10 @@ void tst_qqmlpropertycache::methodsDerived() void tst_qqmlpropertycache::signalHandlers() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); DerivedObject object; const QMetaObject *metaObject = object.metaObject(); - QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject)); + QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(metaObject)); QQmlPropertyData *data; QVERIFY((data = cacheProperty(cache, "onSignalA"))); @@ -250,11 +270,10 @@ void tst_qqmlpropertycache::signalHandlers() void tst_qqmlpropertycache::signalHandlersDerived() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); DerivedObject object; const QMetaObject *metaObject = object.metaObject(); - QQmlRefPointer<QQmlPropertyCache> parentCache(new QQmlPropertyCache(v4, &BaseObject::staticMetaObject)); + QQmlRefPointer<QQmlPropertyCache> parentCache(new QQmlPropertyCache(&BaseObject::staticMetaObject)); QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject())); QQmlPropertyData *data; @@ -277,6 +296,132 @@ void tst_qqmlpropertycache::signalHandlersDerived() QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()")); } +class MyEnum : public QObject + { + Q_OBJECT + public: + enum Option1Flag { + Option10 = 0, + Option1A = 1, + Option1B = 2, + Option1C = 4, + Option1D = 8, + Option1E = 16, + Option1F = 32, + Option1AD = Option1A | Option1D, + }; + Q_DECLARE_FLAGS(Option1, Option1Flag) + Q_FLAG(Option1) + + enum ShortEnum: quint16 { + Short0 = 0, + Short8 = 0xff, + Short16 = 0xffff + }; + Q_ENUM(ShortEnum); +}; + +class MyData : public QObject +{ + Q_OBJECT + Q_PROPERTY(MyEnum::Option1 opt1 READ opt1 WRITE setOpt1 NOTIFY opt1Changed) + Q_PROPERTY(MyEnum::ShortEnum opt2 READ opt2 WRITE setOpt2 NOTIFY opt2Changed) +public: + MyEnum::Option1 opt1() const { return m_opt1; } + MyEnum::ShortEnum opt2() const { return m_opt2; } + +signals: + void opt1Changed(MyEnum::Option1 opt1); + void opt2Changed(MyEnum::ShortEnum opt2); + +public slots: + void setOpt1(MyEnum::Option1 opt1) + { + QCOMPARE(opt1, MyEnum::Option1AD); + if (opt1 != m_opt1) { + m_opt1 = opt1; + emit opt1Changed(opt1); + } + } + + void setOpt2(MyEnum::ShortEnum opt2) + { + QCOMPARE(opt2, MyEnum::Short16); + if (opt2 != m_opt2) { + m_opt2 = opt2; + emit opt2Changed(opt2); + } + } + + void setOption1(MyEnum::Option1 opt1) { setOpt1(opt1); } + void setOption2(MyEnum::ShortEnum opt2) { setOpt2(opt2); } + +private: + MyEnum::Option1 m_opt1 = MyEnum::Option10; + MyEnum::ShortEnum m_opt2 = MyEnum::Short8; +}; + +void tst_qqmlpropertycache::passForeignEnums() +{ + qmlRegisterType<MyEnum>("example", 1, 0, "MyEnum"); + qmlRegisterType<MyData>("example", 1, 0, "MyData"); + + MyEnum myenum; + MyData data; + + engine.rootContext()->setContextProperty("myenum", &myenum); + engine.rootContext()->setContextProperty("mydata", &data); + + QQmlComponent component(&engine, testFile("foreignEnums.qml")); + QVERIFY(component.isReady()); + + QScopedPointer<QObject> obj(component.create(engine.rootContext())); + QVERIFY(!obj.isNull()); + QCOMPARE(data.opt1(), MyEnum::Option1AD); + QCOMPARE(data.opt2(), MyEnum::Short16); +} + +Q_DECLARE_METATYPE(MyEnum::Option1) +Q_DECLARE_METATYPE(MyEnum::ShortEnum) + +QT_BEGIN_NAMESPACE +class SimpleGadget +{ + Q_GADGET + Q_PROPERTY(bool someProperty READ someProperty) +public: + bool someProperty() const { return true; } +}; + +// Avoids NeedsCreation and NeedsDestruction flags +Q_DECLARE_TYPEINFO(SimpleGadget, Q_PRIMITIVE_TYPE); +QT_END_NAMESPACE + +class GadgetEmitter : public QObject +{ + Q_OBJECT +signals: + void emitGadget(SimpleGadget); +}; + +void tst_qqmlpropertycache::passQGadget() +{ + qRegisterMetaType<SimpleGadget>(); + + GadgetEmitter emitter; + engine.rootContext()->setContextProperty("emitter", &emitter); + QQmlComponent component(&engine, testFile("passQGadget.qml")); + QVERIFY(component.isReady()); + + QScopedPointer<QObject> obj(component.create(engine.rootContext())); + QVariant before = obj->property("result"); + QVERIFY(before.isNull()); + emit emitter.emitGadget(SimpleGadget()); + QVariant after = obj->property("result"); + QCOMPARE(QMetaType::Type(after.type()), QMetaType::Bool); + QVERIFY(after.toBool()); +} + class TestClass : public QObject { Q_OBJECT diff --git a/tests/auto/qml/qqmlpropertymap/data/PropertyMapSubType.qml b/tests/auto/qml/qqmlpropertymap/data/PropertyMapSubType.qml new file mode 100644 index 0000000000..90ff179408 --- /dev/null +++ b/tests/auto/qml/qqmlpropertymap/data/PropertyMapSubType.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 +import Test 1.0 + +SimplePropertyMap { + Component.onCompleted: { + console.log("expected output") + newProperty = 42 + } +} diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp index b8ea98df2b..237b65679e 100644 --- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp +++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp @@ -35,7 +35,7 @@ #include <QSignalSpy> #include <QDebug> -class tst_QQmlPropertyMap : public QObject +class tst_QQmlPropertyMap : public QQmlDataTest { Q_OBJECT public: @@ -61,6 +61,7 @@ private slots: void disallowExtending(); void QTBUG_35906(); void QTBUG_48136(); + void lookupsInSubTypes(); }; class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus @@ -71,8 +72,7 @@ class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus Q_PROPERTY(int someFixedProperty READ someFixedProperty WRITE setSomeFixedProperty NOTIFY someFixedPropertyChanged) public: LazyPropertyMap() - : QQmlPropertyMap(this, /*parent*/0) - , value(0) + : QQmlPropertyMap(this, /*parent*/nullptr) {} virtual void classBegin() {} @@ -87,12 +87,21 @@ signals: void someFixedPropertyChanged(); private: - int value; + int value = 0; +}; + +class SimplePropertyMap: public QQmlPropertyMap +{ + Q_OBJECT +public: + SimplePropertyMap() : QQmlPropertyMap(this, nullptr) {} }; void tst_QQmlPropertyMap::initTestCase() { + QQmlDataTest::initTestCase(); qmlRegisterType<LazyPropertyMap>("QTBUG_35233", 1, 0, "LazyPropertyMap"); + qmlRegisterType<SimplePropertyMap>("Test", 1, 0, "SimplePropertyMap"); } void tst_QQmlPropertyMap::insert() @@ -313,7 +322,7 @@ class MyEnhancedPropertyMap : public QQmlPropertyMap { Q_OBJECT public: - MyEnhancedPropertyMap() : QQmlPropertyMap(this, 0), m_testSlotCalled(false) {} + MyEnhancedPropertyMap() : QQmlPropertyMap(this, nullptr) {} bool testSlotCalled() const { return m_testSlotCalled; } signals: @@ -323,7 +332,7 @@ public slots: void testSlot() { m_testSlotCalled = true; } private: - bool m_testSlotCalled; + bool m_testSlotCalled = false; }; void tst_QQmlPropertyMap::metaObjectAccessibility() @@ -497,6 +506,16 @@ void tst_QQmlPropertyMap::QTBUG_48136() QCOMPARE(valueChangedSpy.count(), 1); } +void tst_QQmlPropertyMap::lookupsInSubTypes() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("PropertyMapSubType.qml")); + QTest::ignoreMessage(QtDebugMsg, "expected output"); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QCOMPARE(object->property("newProperty").toInt(), 42); +} + QTEST_MAIN(tst_QQmlPropertyMap) #include "tst_qqmlpropertymap.moc" diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index 0576650d01..2d8115e867 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -168,7 +168,7 @@ void tst_qqmlqt::enums() { QQmlComponent component(&engine, testFileUrl("enums.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toInt(), (int)Qt::Key_Escape); QCOMPARE(object->property("test2").toInt(), (int)Qt::DescendingOrder); @@ -188,7 +188,7 @@ void tst_qqmlqt::rgba() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QColor>(object->property("test1")), QColor::fromRgbF(1, 0, 0, 0.8)); @@ -211,7 +211,7 @@ void tst_qqmlqt::hsla() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QColor>(object->property("test1")), QColor::fromHslF(1, 0, 0, 0.8)); QCOMPARE(qvariant_cast<QColor>(object->property("test2")), QColor::fromHslF(1, 0.5, 0.3, 1)); @@ -233,7 +233,7 @@ void tst_qqmlqt::hsva() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QColor>(object->property("test1")), QColor::fromHsvF(1, 0, 0, 0.8)); QCOMPARE(qvariant_cast<QColor>(object->property("test2")), QColor::fromHsvF(1, 0.5, 0.3, 1)); @@ -261,7 +261,7 @@ void tst_qqmlqt::colorEqual() QTest::ignoreMessage(QtWarningMsg, qPrintable(component.url().toString() + ":35: Error: Qt.colorEqual(): Invalid color name")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1a").toBool(), false); QCOMPARE(object->property("test1b").toBool(), false); @@ -338,7 +338,7 @@ void tst_qqmlqt::rect() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QRectF>(object->property("test1")), QRectF(10, 13, 100, 109)); QCOMPARE(qvariant_cast<QRectF>(object->property("test2")), QRectF(-10, 13, 100, 109.6)); @@ -359,7 +359,7 @@ void tst_qqmlqt::point() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QPointF>(object->property("test1")), QPointF(19, 34)); QCOMPARE(qvariant_cast<QPointF>(object->property("test2")), QPointF(-3, 109.2)); @@ -379,7 +379,7 @@ void tst_qqmlqt::size() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QSizeF>(object->property("test1")), QSizeF(19, 34)); QCOMPARE(qvariant_cast<QSizeF>(object->property("test2")), QSizeF(3, 109.2)); @@ -400,7 +400,7 @@ void tst_qqmlqt::vector2d() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QVector2D>(object->property("test1")), QVector2D(1, 0.9f)); QCOMPARE(qvariant_cast<QVector2D>(object->property("test2")), QVector2D(102, -982.1f)); @@ -420,7 +420,7 @@ void tst_qqmlqt::vector3d() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QVector3D>(object->property("test1")), QVector3D(1, 0, 0.9f)); QCOMPARE(qvariant_cast<QVector3D>(object->property("test2")), QVector3D(102, -10, -982.1f)); @@ -440,7 +440,7 @@ void tst_qqmlqt::vector4d() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QVector4D>(object->property("test1")), QVector4D(1, 0, 0.9f, 0.6f)); QCOMPARE(qvariant_cast<QVector4D>(object->property("test2")), QVector4D(102, -10, -982.1f, 10)); @@ -460,7 +460,7 @@ void tst_qqmlqt::quaternion() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QQuaternion>(object->property("test1")), QQuaternion(2, 17, 0.9f, 0.6f)); QCOMPARE(qvariant_cast<QQuaternion>(object->property("test2")), QQuaternion(102, -10, -982.1f, 10)); @@ -482,7 +482,7 @@ void tst_qqmlqt::matrix4x4() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning3)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QMatrix4x4>(object->property("test1")), QMatrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); QCOMPARE(qvariant_cast<QMatrix4x4>(object->property("test2")), QMatrix4x4(1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4)); @@ -503,10 +503,16 @@ void tst_qqmlqt::font() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); - - QCOMPARE(qvariant_cast<QFont>(object->property("test1")), QFont("Arial", 22)); - QCOMPARE(qvariant_cast<QFont>(object->property("test2")), QFont("Arial", 20, QFont::DemiBold, true)); + QVERIFY(object != nullptr); + + QFont f; + f.setFamily("Arial"); + f.setPointSize(22); + QCOMPARE(qvariant_cast<QFont>(object->property("test1")), f); + f.setPointSize(20); + f.setWeight(QFont::DemiBold); + f.setItalic(true); + QCOMPARE(qvariant_cast<QFont>(object->property("test2")), f); QCOMPARE(qvariant_cast<QFont>(object->property("test3")), QFont()); QCOMPARE(qvariant_cast<QFont>(object->property("test4")), QFont()); @@ -523,7 +529,7 @@ void tst_qqmlqt::lighter() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QColor>(object->property("test1")), QColor::fromRgbF(1, 0.8, 0.3).lighter()); QCOMPARE(qvariant_cast<QColor>(object->property("test2")), QColor()); @@ -545,7 +551,7 @@ void tst_qqmlqt::darker() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QColor>(object->property("test1")), QColor::fromRgbF(1, 0.8, 0.3).darker()); QCOMPARE(qvariant_cast<QColor>(object->property("test2")), QColor()); @@ -568,7 +574,7 @@ void tst_qqmlqt::tint() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QColor>(object->property("test1")), QColor::fromRgbF(0, 0, 1)); QCOMPARE(qvariant_cast<QColor>(object->property("test2")), QColor::fromRgbF(1, 0, 0)); @@ -601,7 +607,7 @@ void tst_qqmlqt::openUrlExternally() QQmlComponent component(&engine, testFileUrl("openUrlExternally.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(handler.called,1); QCOMPARE(handler.last, QUrl("test:url")); @@ -623,7 +629,7 @@ void tst_qqmlqt::openUrlExternally_pragmaLibrary() QQmlComponent component(&engine, testFileUrl("openUrlExternally_lib.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(handler.called,1); QCOMPARE(handler.last, QUrl("test:url")); @@ -644,7 +650,7 @@ void tst_qqmlqt::md5() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test2").toString(), QLatin1String(QCryptographicHash::hash("Hello World", QCryptographicHash::Md5).toHex())); @@ -662,7 +668,7 @@ void tst_qqmlqt::createComponent() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("absoluteUrl").toString(), QString("http://www.example.com/test.qml")); QCOMPARE(object->property("relativeUrl").toString(), testFileUrl("createComponentData.qml").toString()); @@ -676,7 +682,7 @@ void tst_qqmlqt::createComponent() { QQmlComponent component(&engine, testFileUrl("createComponent.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QTRY_VERIFY(object->property("success").toBool()); delete object; } @@ -687,7 +693,7 @@ void tst_qqmlqt::createComponent_pragmaLibrary() // Currently, just loading createComponent_lib.qml causes crash on some platforms QQmlComponent component(&engine, testFileUrl("createComponent_lib.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("status").toInt(), int(QQmlComponent::Ready)); QCOMPARE(object->property("readValue").toInt(), int(1913)); delete object; @@ -711,13 +717,13 @@ void tst_qqmlqt::createQmlObject() QTest::ignoreMessage(QtDebugMsg, qPrintable(warning6)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("emptyArg").toBool(), true); QCOMPARE(object->property("success").toBool(), true); QQuickItem *item = qobject_cast<QQuickItem *>(object); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QCOMPARE(item->childItems().count(), 1); delete object; @@ -790,7 +796,7 @@ void tst_qqmlqt::dateTimeFormatting() QObject *object = component.create(); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(inputProperties.count() > 0); QVariant result; @@ -863,7 +869,7 @@ void tst_qqmlqt::dateTimeFormattingVariants() QObject *object = component.create(); QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVariant result; QVERIFY(QMetaObject::invokeMethod(object, method.toUtf8().constData(), @@ -888,7 +894,8 @@ void tst_qqmlqt::dateTimeFormattingVariants_data() QTest::newRow("formatTime, qtime") << "formatTime" << QVariant::fromValue(time) << (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")); QDate date(2011,5,31); - temporary = QDateTime(date); + // V4 reads the date in UTC but DateObject::toQDateTime() gives it back in local time: + temporary = QDateTime(date, QTime(0, 0, 0), Qt::UTC).toLocalTime(); QTest::newRow("formatDate, qdate") << "formatDate" << QVariant::fromValue(date) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy")); QTest::newRow("formatDateTime, qdate") << "formatDateTime" << QVariant::fromValue(date) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a")); QTest::newRow("formatTime, qdate") << "formatTime" << QVariant::fromValue(date) << (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")); @@ -922,7 +929,7 @@ void tst_qqmlqt::isQtObject() { QQmlComponent component(&engine, testFileUrl("isQtObject.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), false); @@ -941,7 +948,7 @@ void tst_qqmlqt::btoa() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test2").toString(), QString("SGVsbG8gd29ybGQh")); @@ -956,7 +963,7 @@ void tst_qqmlqt::atob() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test2").toString(), QString("Hello world!")); @@ -971,7 +978,7 @@ void tst_qqmlqt::fontFamilies() QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QFontDatabase database; QCOMPARE(object->property("test2"), QVariant::fromValue(database.families())); @@ -985,7 +992,7 @@ void tst_qqmlqt::quit() QSignalSpy spy(&engine, SIGNAL(quit())); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(spy.count(), 1); delete object; @@ -997,7 +1004,7 @@ void tst_qqmlqt::exit() QSignalSpy spy(&engine, &QQmlEngine::exit); QObject *object = component.create(); - QVERIFY(object != Q_NULLPTR); + QVERIFY(object != nullptr); QCOMPARE(spy.count(), 1); QList<QVariant> arguments = spy.takeFirst(); QVERIFY(arguments.at(0).toInt() == object->property("returnCode").toInt()); @@ -1010,7 +1017,7 @@ void tst_qqmlqt::resolvedUrl() QQmlComponent component(&engine, testFileUrl("resolvedUrl.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("result").toString(), component.url().toString()); QCOMPARE(object->property("isString").toBool(), true); @@ -1100,14 +1107,14 @@ void tst_qqmlqt::later() QQmlComponent component(&engine, testFileUrl("later.qml")); QObject *root = component.create(); - QVERIFY(root != 0); + QVERIFY(root != nullptr); if (!function.isEmpty()) QMetaObject::invokeMethod(root, qPrintable(function)); for (int i = 0; i < propNames.size(); ++i) { if (propNames.at(i) == QLatin1String("processEvents")) { - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); } else if (propNames.at(i) == QLatin1String("collectGarbage")) { engine.collectGarbage(); @@ -1130,7 +1137,7 @@ void tst_qqmlqt::qtObjectContents() QQmlComponent component(&engine, testFileUrl("qtObjectContents.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("values").canConvert<QJSValue>()); QVariantMap values = object->property("values").value<QJSValue>().toVariant().toMap(); @@ -1254,7 +1261,7 @@ void tst_qqmlqt::timeRoundtrip() eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp); QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml")); QObject *obj = component.create(); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without // any perturbation (e.g. by DST effects) from converting from QTime to V4's Date and back diff --git a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp index e08389045c..b0be799bd5 100644 --- a/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp +++ b/tests/auto/qml/qqmlsettings/tst_qqmlsettings.cpp @@ -53,6 +53,7 @@ private slots: void categories(); void siblings(); void initial(); + void noApplicationIdentifiersSet(); }; // ### Replace keyValueMap("foo", "bar") with QVariantMap({{"foo", "bar"}}) @@ -87,11 +88,8 @@ class CppObject : public QObject Q_PROPERTY(QFont fontProperty MEMBER m_fontProperty NOTIFY fontPropertyChanged) public: - CppObject(QObject *parent = 0) : QObject(parent), - m_intProperty(123), - m_boolProperty(true), - m_realProperty(1.23), - m_doubleProperty(3.45), + CppObject(QObject *parent = nullptr) : QObject(parent), + m_stringProperty("foo"), m_urlProperty("http://www.qt-project.org"), m_objectProperty(keyValueMap("foo", "bar")), @@ -127,10 +125,10 @@ signals: void fontPropertyChanged(const QFont &arg); private: - int m_intProperty; - bool m_boolProperty; - qreal m_realProperty; - double m_doubleProperty; + int m_intProperty = 123; + bool m_boolProperty = true; + qreal m_realProperty = 1.23; + double m_doubleProperty = 3.45; QString m_stringProperty; QUrl m_urlProperty; QVariant m_varProperty; @@ -150,10 +148,6 @@ void tst_QQmlSettings::initTestCase() { QQmlDataTest::initTestCase(); - QCoreApplication::setApplicationName("tst_QQmlSettings"); - QCoreApplication::setOrganizationName("QtProject"); - QCoreApplication::setOrganizationDomain("qt-project.org"); - qmlRegisterType<CppObject>("Qt.test", 1, 0, "CppObject"); } @@ -161,6 +155,10 @@ void tst_QQmlSettings::init() { QSettings settings; settings.clear(); + + QCoreApplication::setApplicationName("tst_QQmlSettings"); + QCoreApplication::setOrganizationName("QtProject"); + QCoreApplication::setOrganizationDomain("qt-project.org"); } void tst_QQmlSettings::cleanup() @@ -484,6 +482,31 @@ void tst_QQmlSettings::initial() QCOMPARE(settings->property("value").toString(), QStringLiteral("initial")); } +void tst_QQmlSettings::noApplicationIdentifiersSet() +{ +#ifdef Q_OS_MACOS + QSKIP("macOS doesn't complain about empty application identifiers"); +#endif + + QCoreApplication::setApplicationName(QString()); + QCoreApplication::setOrganizationName(QString()); + QCoreApplication::setOrganizationDomain(QString()); + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*QML Settings: Failed to initialize QSettings instance. Status code is: 1")); + // Can't set an empty applicationName because QCoreApplication won't allow it, which is why it's not listed here. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*QML Settings: The following application identifiers have not been set: QVector\\(\"organizationName\", \"organizationDomain\"\\)")); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("basic.qml")); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root.data()); + // The value of the QML property will be true because it defaults to it... + QVERIFY(root->property("success").toBool()); + QSettings settings; + // ... but the settings' value should be false because it was never loaded. + QVERIFY(!settings.value("success").toBool()); +} + QTEST_MAIN(tst_QQmlSettings) #include "tst_qqmlsettings.moc" diff --git a/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml b/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml new file mode 100644 index 0000000000..7d2683d869 --- /dev/null +++ b/tests/auto/qml/qqmlsqldatabase/data/changeVersion.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQml 2.2 +import QtQuick.LocalStorage 2.0 + +QtObject { + property var db; + property string version; + + function create() { + db = LocalStorage.openDatabaseSync("testdb", "2", "stuff for testing", 100000); + version = db.version; + } + + function upgrade() { + db = db.changeVersion(db.version, "22", function(y) {}); + version = db.version; + } +} diff --git a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp index 3b982d1ab7..28f9c9a0c2 100644 --- a/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp +++ b/tests/auto/qml/qqmlsqldatabase/tst_qqmlsqldatabase.cpp @@ -63,6 +63,7 @@ private slots: void testQml_cleanopen_data(); void testQml_cleanopen(); void totalDatabases(); + void upgradeDatabase(); void cleanupTestCase(); @@ -164,7 +165,7 @@ void tst_qqmlsqldatabase::testQml() component.setData(qml.toUtf8(), testFileUrl("empty.qml")); // just a file for relative local imports QVERIFY(!component.isError()); QQuickText *text = qobject_cast<QQuickText*>(component.create()); - QVERIFY(text != 0); + QVERIFY(text != nullptr); QCOMPARE(text->text(),QString("passed")); } @@ -200,6 +201,22 @@ void tst_qqmlsqldatabase::totalDatabases() QCOMPARE(QDir(dbDir()+"/Databases").entryInfoList(QDir::Files|QDir::NoDotAndDotDot).count(), total_databases_created_by_tests*2); } +void tst_qqmlsqldatabase::upgradeDatabase() +{ + QQmlComponent component(engine, testFile("changeVersion.qml")); + QVERIFY(component.isReady()); + + QObject *object = component.create(); + QVERIFY(object); + QVERIFY(object->property("version").toString().isEmpty()); + + QVERIFY(QMetaObject::invokeMethod(object, "create")); + QCOMPARE(object->property("version").toString(), QLatin1String("2")); + + QVERIFY(QMetaObject::invokeMethod(object, "upgrade")); + QCOMPARE(object->property("version").toString(), QLatin1String("22")); +} + QTEST_MAIN(tst_qqmlsqldatabase) #include "tst_qqmlsqldatabase.moc" diff --git a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp index 5231af88be..68ed22c01c 100644 --- a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp +++ b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp @@ -55,10 +55,7 @@ public: }; public: - CppObject() - : QObject() - , m_objectState(State0) - {} + CppObject() {} ObjectState objectState() const { return m_objectState; } void setObjectState(ObjectState objectState) { m_objectState = objectState; emit objectStateChanged();} @@ -68,7 +65,7 @@ signals: void mySignal(int signalState); private: - ObjectState m_objectState; + ObjectState m_objectState = State0; }; tst_qqmlstatemachine::tst_qqmlstatemachine() @@ -86,7 +83,7 @@ void tst_qqmlstatemachine::tst_cppObjectSignal() QQmlContext *ctxt = engine.rootContext(); ctxt->setContextProperty("_cppObject", &cppObject); QScopedPointer<QObject> rootObject(component.create()); - QVERIFY(rootObject != 0); + QVERIFY(rootObject != nullptr); // wait for state machine to start QTRY_VERIFY(rootObject->property("running").toBool()); diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml new file mode 100644 index 0000000000..7aeb5d03f4 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt.labs.qmlmodels 1.0 + +import "TestUtils.js" as TestUtils + +TableModel { + id: testModel + objectName: "testModel" + roleDataProvider: TestUtils.testModelRoleDataProvider + rows: [ + [ + { name: "John" }, + { age: 22 } + ], + [ + { name: "Oliver" }, + { age: 33 } + ] + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js new file mode 100644 index 0000000000..0b92a377bb --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +function testModelRoleDataProvider(index, role, cellData) { + switch (role) { + case "display": + switch (index.column) { + case 0: + return cellData.name + case 1: + return cellData.age + } + break + case "name": + return cellData.name + case "age": + return cellData.age + } + return cellData +} diff --git a/tests/auto/qml/qqmltablemodel/data/builtInRoles.qml b/tests/auto/qml/qqmltablemodel/data/builtInRoles.qml new file mode 100644 index 0000000000..d9882e4dea --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/builtInRoles.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt.labs.qmlmodels 1.0 + +import "TestUtils.js" as TestUtils + +TableModel { + id: testModel + objectName: "testModel" + roleDataProvider: TestUtils.testModelRoleDataProvider + rows: [ + [ + { name: "John", someOtherRole1: "foo" }, // column 0 + { age: 22, someOtherRole2: "foo" } // column 1 + ], + [ + { name: "Oliver", someOtherRole1: "foo" }, // column 0 + { age: 33, someOtherRole2: "foo" } // column 1 + ] + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml new file mode 100644 index 0000000000..aec796bd4f --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/common.qml @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function appendRow(personName, personAge) { + testModel.appendRow([ + { name: personName }, + { age: personAge } + ]) + } + + function appendRowInvalid1() { + testModel.appendRow([ + { name: "Foo" }, + { age: 99 }, + { nonExistentRole: 123 } + ]) + } + + function appendRowInvalid2() { + testModel.appendRow(123) + } + + function appendRowInvalid3() { + testModel.appendRow([ + { name: "Foo" }, + { age: [] } + ]) + } + + function insertRow(personName, personAge, rowIndex) { + testModel.insertRow(rowIndex, [ + { name: personName }, + { age: personAge }] + ) + } + + function insertRowInvalid1() { + testModel.insertRow(0, [ + { name: "Foo" }, + { age: 99 }, + { nonExistentRole: 123 } + ]) + } + + function insertRowInvalid2() { + testModel.insertRow(0, 123) + } + + function insertRowInvalid3() { + testModel.insertRow(0, [ + { name: "Foo" }, + { age: [] } + ]) + } + + function setRow(rowIndex, personName, personAge) { + testModel.setRow(rowIndex, [ + { name: personName }, + { age: personAge }] + ) + } + + function setRowInvalid1() { + testModel.setRow(0, [ + { name: "Foo" }, + { age: 99 }, + { nonExistentRole: 123 } + ]) + } + + function setRowInvalid2() { + testModel.setRow(0, 123) + } + + function setRowInvalid3() { + testModel.setRow(0, [ + { name: "Foo" }, + { age: [] } + ]) + } + + TableView { + id: tableView + anchors.fill: parent + model: TestModel { + id: testModel + } + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml new file mode 100644 index 0000000000..d61c50ba2c --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +TableView { + width: 200; height: 200 + model: TableModel { + id: testModel + objectName: "testModel" + rows: [ + [ + { name: "John", someOtherRole1: "foo" }, // column 0 + { age: 22, someOtherRole2: "foo" } // column 1 + ], + [ + { name: "Oliver", someOtherRole1: "foo" }, // column 0 + { age: 33, someOtherRole2: "foo" } // column 1 + ] + ] + + // This is silly: in real life, store the birthdate instead of the age, + // and let the delegate calculate the age, so it won't need updating + function happyBirthday(dude) { + var row = -1; + for (var r = 0; row < 0 && r < testModel.rowCount; ++r) + if (testModel.data(testModel.index(r, 0), "name") === dude) + row = r; + var index = testModel.index(row, 1) + testModel.setData(index, "age", testModel.data(index, "age") + 1) + } + } + delegate: Text { + id: textItem + text: model.display + TapHandler { + onTapped: testModel.happyBirthday(testModel.data(testModel.index(row, 0), "name")) + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml new file mode 100644 index 0000000000..6e66b99145 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/empty.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +import "TestUtils.js" as TestUtils + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function setRows() { + testModel.rows = [ + [ + { name: "John" }, + { age: 22 } + ], + [ + { name: "Oliver" }, + { age: 33 } + ] + ] + } + + TableModel { + id: testModel + objectName: "testModel" + roleDataProvider: TestUtils.testModelRoleDataProvider + } + TableView { + id: tableView + anchors.fill: parent + model: testModel + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/explicitDisplayRole.qml b/tests/auto/qml/qqmltablemodel/data/explicitDisplayRole.qml new file mode 100644 index 0000000000..510a62e74b --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/explicitDisplayRole.qml @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt.labs.qmlmodels 1.0 + +import "TestUtils.js" as TestUtils + +TableModel { + id: testModel + rows: [ + [ + { name: "John", display: "foo" }, + { age: 22, display: "bar" } + ] + ] +} diff --git a/tests/auto/qml/qqmltablemodel/data/roleDataProvider.qml b/tests/auto/qml/qqmltablemodel/data/roleDataProvider.qml new file mode 100644 index 0000000000..2706ea54fd --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/roleDataProvider.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + + TableModel { + id: testModel + objectName: "testModel" + rows: [ + [ { name: "Rex" }, { age: 3 } ], + [ { name: "Buster" }, { age: 5 } ] + ] + roleDataProvider: function(index, role, cellData) { + if (role === "display") { + // Age will now be in dog years + if (cellData.hasOwnProperty("age")) + return (cellData.age * 7); + else if (index.column === 0) + return (cellData.name); + } + return cellData; + } + } + TableView { + anchors.fill: parent + model: testModel + delegate: Text { + id: textItem + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml new file mode 100644 index 0000000000..5f849c3350 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + + signal shouldModify() + signal shouldModifyInvalidRole() + signal shouldModifyInvalidType() + + function modify() { + shouldModify(); + } + + function modifyInvalidRole() { + shouldModifyInvalidRole(); + } + + function modifyInvalidType() { + shouldModifyInvalidType() + } + + TableView { + anchors.fill: parent + model: TableModel { + id: testModel + objectName: "testModel" + rows: [ + [ + { name: "John" }, + { age: 22 } + ], + [ + { name: "Oliver" }, + { age: 33 } + ] + ] + } + + delegate: Text { + id: textItem + // TODO: this is currently random when no roleDataProvider handles it + // we should allow roleDataProvider to be used to handle specific roles only + text: model.display + + Connections { + target: root + enabled: column === 1 + onShouldModify: model.age = 18 + } + + Connections { + target: root + enabled: column === 0 + // Invalid: should be "name". + onShouldModifyInvalidRole: model.age = 100 + } + + Connections { + target: root + enabled: column === 1 + // Invalid: should be string. + onShouldModifyInvalidType: model.age = "Whoops" + } + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml new file mode 100644 index 0000000000..6aaf79f2d4 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import Qt.labs.qmlmodels 1.0 + +Item { + id: root + width: 200 + height: 200 + + property alias testModel: testModel + property alias tableView: tableView + + function setRowsValid() { + testModel.rows = [ + [ + { name: "Max" }, + { age: 20 } + ], + [ + { name: "Imum" }, + { age: 41 } + ], + [ + { name: "Power" }, + { age: 89 } + ] + ] + } + + function setRowsInvalid() { + testModel.rows = [ + [ + { nope: "Nope" }, + { age: 20 } + ], + [ + { nope: "Nah" }, + { age: 41 } + ], + [ + { nope: "No" }, + { age: 89 } + ] + ] + } + + TableView { + id: tableView + anchors.fill: parent + model: TestModel { + id: testModel + } + delegate: Text { + text: model.display + } + } +} diff --git a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro new file mode 100644 index 0000000000..11b11132aa --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmltablemodel + +SOURCES += tst_qqmltablemodel.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core gui qml-private qml quick-private quick testlib diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp new file mode 100644 index 0000000000..059ce082d9 --- /dev/null +++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp @@ -0,0 +1,947 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> +#include <QtCore/qregularexpression.h> +#include <QtQml/private/qqmlengine_p.h> +#include <QtQml/private/qqmltablemodel_p.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/private/qquicktableview_p.h> + +#include "../../shared/util.h" + +class tst_QQmlTableModel : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QQmlTableModel() {} + +private slots: + void appendRemoveRow(); + void clear(); + void getRow(); + void insertRow(); + void moveRow(); + void setRow(); + void setDataThroughDelegate(); + void setRowsImperatively(); + void setRowsMultipleTimes(); + void builtInRoles_data(); + void builtInRoles(); + void explicitDisplayRole(); + void roleDataProvider(); + void dataAndEditing(); +}; + +static const int builtInRoleCount = 6; + +void tst_QQmlTableModel::appendRemoveRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int heightSignalEmissions = 0; + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 2 + builtInRoleCount); + QVERIFY(roleNames.values().contains("name")); + QVERIFY(roleNames.values().contains("age")); + QVERIFY(roleNames.values().contains("display")); + QVERIFY(roleNames.values().contains("decoration")); + QVERIFY(roleNames.values().contains("edit")); + QVERIFY(roleNames.values().contains("toolTip")); + QVERIFY(roleNames.values().contains("statusTip")); + QVERIFY(roleNames.values().contains("whatsThis")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + + // Call remove() with a negative rowIndex. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, -1))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call remove() with an rowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*removeRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 2))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call remove() with a valid rowIndex but negative rows. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rows\" is less than or equal to zero")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, -1))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call remove() with a valid rowIndex but excessive rows. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*removeRow\\(\\): \"rows\" 3 exceeds available rowCount\\(\\) of 2 when removing from \"rowIndex\" 0")); + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call remove() without specifying the number of rows to remove; it should remove one row. + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0))); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++heightSignalEmissions); + + // Call append() with a row that has a new (and hence unexpected) role. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*appendRow\\(\\): expected 2 columns, but got 3")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid1")); + // Nothing should change. + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call append() with a row that is not an array. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): expected \"row\" argument to be an array, but got int instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid2")); + // Nothing should change. + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call append() with a row with a role that is of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*appendRow\\(\\): expected property with type int at column index 1, but got QVariantList instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid3")); + // Nothing should change. + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Call append() to insert one row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++heightSignalEmissions); + + // Call remove() and specify rowIndex and rows, removing all remaining rows. + QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 2))); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")), QVariant()); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")), QVariant()); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++heightSignalEmissions); +} + +void tst_QQmlTableModel::clear() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("name")); + QVERIFY(roleNames.values().contains("age")); + QCOMPARE(roleNames.size(), 2 + builtInRoleCount); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + QVERIFY(QMetaObject::invokeMethod(model, "clear")); + QCOMPARE(model->rowCount(), 0); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant()); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + // Wait until updatePolish() gets called, which is where the size is recalculated. + QTRY_COMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::getRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + // Call get() with a negative row index. + QVariant returnValue; + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*getRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, -1))); + QVERIFY(!returnValue.isValid()); + + // Call get() with a row index that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*getRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 2))); + QVERIFY(!returnValue.isValid()); + + // Call get() with a valid row index. + QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 0))); + const QVariantList rowAsVariantList = returnValue.toList(); + QCOMPARE(rowAsVariantList.at(0).toMap().value(QLatin1String("name")), QLatin1String("John")); + QCOMPARE(rowAsVariantList.at(1).toMap().value(QLatin1String("age")), 22); +} + +void tst_QQmlTableModel::insertRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int heightSignalEmissions = 0; + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert with a negative index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, -1))); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert past the last allowed index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that has a new (and hence unexpected) role. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*insertRow\\(\\): expected 2 columns, but got 3")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid1")); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is not an array. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): expected \"row\" argument to be an array, but got int instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid2")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row with a role that is of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*insertRow\\(\\): expected property with type int at column index 1, but got QVariantList instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid3")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Insert a row at the bottom of the table. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 2))); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++heightSignalEmissions); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); + + // Insert a row in the middle of the table. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow", + Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30), Q_ARG(QVariant, 1))); + QCOMPARE(model->rowCount(), 4); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++heightSignalEmissions); + QTRY_COMPARE(tableView->rows(), 4); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::moveRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int heightSignalEmissions = 0; + + const QHash<int, QByteArray> roleNames = model->roleNames(); + + // Append some rows. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30))); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Trev")), Q_ARG(QVariant, 48))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + heightSignalEmissions = 3; + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Try to move with a fromRowIndex that is negative. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, -1), Q_ARG(int, 1))); + // Shouldn't have changed. + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Try to move with a fromRowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" 5 is greater than or equal to rowCount\\(\\)")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 5), Q_ARG(int, 1))); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Try to move with a toRowIndex that is negative. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, -1))); + // Shouldn't have changed. + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Try to move with a toRowIndex that is too large. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" 5 is greater than or equal to rowCount\\(\\)")); + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 5))); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Move the first row to the end. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 4))); + // The counts shouldn't have changed. + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Move it back again. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 4), Q_ARG(int, 0))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + + // Move the first row down one by one row. + QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 1))); + QCOMPARE(model->rowCount(), 5); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev")); + QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); +} + +void tst_QQmlTableModel::setRow() +{ + QQuickView view(testFileUrl("common.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->rowCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + int heightSignalEmissions = 0; + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert with a negative index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): \"rowIndex\" cannot be negative")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, -1), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert past the last allowed index. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 3), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that has a new (and hence unexpected) role. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setRow\\(\\): expected 2 columns, but got 3")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid1")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row that is not an array. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): expected \"row\" argument to be an array, but got int instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid2")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Try to insert a row with a role that is of the wrong type. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRow\\(\\): expected property with type int at column index 1, but got QVariantList instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid3")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set the first row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 0), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set the last row. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 1), Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30))); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), heightSignalEmissions); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Append a row by passing an index that is equal to rowCount(). + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow", + Q_ARG(QVariant, 2), Q_ARG(QVariant, QLatin1String("Wot")), Q_ARG(QVariant, 99))); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 40); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 30); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Wot")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 99); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), ++heightSignalEmissions); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::setDataThroughDelegate() +{ + QQuickView view(testFileUrl("setDataThroughDelegate.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 2 + builtInRoleCount); + QVERIFY(roleNames.values().contains("name")); + QVERIFY(roleNames.values().contains("age")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modify")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + // Test setting a role that doesn't exist for a certain column. + const auto invalidRoleRegEx = QRegularExpression(".*setData\\(\\): no role named \"age\" at column index 0. " \ + "The available roles for that column are:[\r\n] - \"name\" \\(QString\\)"); + // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings. + QTest::ignoreMessage(QtWarningMsg, invalidRoleRegEx); + QTest::ignoreMessage(QtWarningMsg, invalidRoleRegEx); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidRole")); + // Should be unchanged. + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); + + // Test setting a role with a value of the wrong type. + // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings. + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \ + "set at row 0 column 1 with role \"age\" to \"int\"")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \ + "set at row 1 column 1 with role \"age\" to \"int\"")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidType")); + // Should be unchanged. + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 18); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 18); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 0); +} + +// Start off with empty rows and append to test widthChanged(). +void tst_QQmlTableModel::setRowsImperatively() +{ + QQuickView view(testFileUrl("empty.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->columnCount(), 0); + QCOMPARE(model->rowCount(), 0); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 0); + QCOMPARE(tableView->columns(), 0); + + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRows")); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33); + QCOMPARE(columnCountSpy.count(), 1); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::setRowsMultipleTimes() +{ + QQuickView view(testFileUrl("setRowsMultipleTimes.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged())); + QVERIFY(columnCountSpy.isValid()); + + QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged())); + QVERIFY(rowCountSpy.isValid()); + + QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>(); + QVERIFY(tableView); + QCOMPARE(tableView->rows(), 2); + QCOMPARE(tableView->columns(), 2); + + // Set valid rows after they've already been declared. + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsValid")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 20); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Imum")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 41); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Power")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 89); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QTRY_COMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); + + // Set invalid rows; we should get a warning and nothing should change. + // TODO: add quotes to the warning message + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + ".*setRows\\(\\): expected property named name at column index 0, but got nope instead")); + QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsInvalid")); + QCOMPARE(model->rowCount(), 3); + QCOMPARE(model->columnCount(), 2); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 20); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Imum")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 41); + QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Power")); + QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 89); + QCOMPARE(columnCountSpy.count(), 0); + QCOMPARE(rowCountSpy.count(), 1); + QCOMPARE(tableView->rows(), 3); + QCOMPARE(tableView->columns(), 2); +} + +void tst_QQmlTableModel::builtInRoles_data() +{ + QTest::addColumn<int>("row"); + QTest::addColumn<int>("column"); + QTest::addColumn<QByteArray>("roleName"); + QTest::addColumn<QVariant>("expectedValue"); + + const QByteArray displayRole = "display"; + + QTest::addRow("display(0,0)") << 0 << 0 << displayRole << QVariant(QLatin1String("John")); + QTest::addRow("display(0,1)") << 0 << 1 << displayRole << QVariant(QLatin1String("22")); + QTest::addRow("display(1,0)") << 1 << 0 << displayRole << QVariant(QLatin1String("Oliver")); + QTest::addRow("display(1,1)") << 1 << 1 << displayRole << QVariant(QLatin1String("33")); +} + +void tst_QQmlTableModel::builtInRoles() +{ + QFETCH(int, row); + QFETCH(int, column); + QFETCH(QByteArray, roleName); + QFETCH(QVariant, expectedValue); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("builtInRoles.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + + QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create())); + QVERIFY(model); + QCOMPARE(model->rowCount(), 2); + QCOMPARE(model->columnCount(), 2); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(roleNames.size(), 4 + builtInRoleCount); + QVERIFY(roleNames.values().contains("display")); + QVERIFY(roleNames.values().contains("decoration")); + QVERIFY(roleNames.values().contains("edit")); + QVERIFY(roleNames.values().contains("toolTip")); + QVERIFY(roleNames.values().contains("statusTip")); + QVERIFY(roleNames.values().contains("whatsThis")); + QVERIFY(roleNames.values().contains("name")); + QVERIFY(roleNames.values().contains("age")); + QVERIFY(roleNames.values().contains("someOtherRole1")); + QVERIFY(roleNames.values().contains("someOtherRole2")); + QCOMPARE(model->data(model->index(row, column, QModelIndex()), roleNames.key(roleName)), expectedValue); +} + +void tst_QQmlTableModel::explicitDisplayRole() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("explicitDisplayRole.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + + QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create())); + QVERIFY(model); + QCOMPARE(model->rowCount(), 1); + QCOMPARE(model->columnCount(), 2); + const QHash<int, QByteArray> roleNames = model->roleNames(); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("foo")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("bar")); +} + +void tst_QQmlTableModel::roleDataProvider() +{ + QQuickView view(testFileUrl("roleDataProvider.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>(); + QVERIFY(model); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Rex")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 3 * 7); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Buster")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 5 * 7); +} + +void tst_QQmlTableModel::dataAndEditing() +{ + QQuickView view(testFileUrl("dataAndSetData.qml")); + QCOMPARE(view.status(), QQuickView::Ready); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>(); + QVERIFY(model); + + const QHash<int, QByteArray> roleNames = model->roleNames(); + QVERIFY(roleNames.values().contains("display")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33); + QVERIFY(QMetaObject::invokeMethod(model, "happyBirthday", Q_ARG(QVariant, QLatin1String("Oliver")))); + QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John")); + QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22); + QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver")); + QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 34); +} + +QTEST_MAIN(tst_QQmlTableModel) + +#include "tst_qqmltablemodel.moc" diff --git a/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp b/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp index 44e7c706bf..4e42d02514 100644 --- a/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp +++ b/tests/auto/qml/qqmltimer/tst_qqmltimer.cpp @@ -89,11 +89,9 @@ class TimerHelper : public QObject { Q_OBJECT public: - TimerHelper() : QObject(), count(0) - { - } + TimerHelper() { } - int count; + int count = 0; public slots: void timeout() { @@ -101,9 +99,7 @@ public slots: } }; -tst_qqmltimer::tst_qqmltimer() -{ -} +tst_qqmltimer::tst_qqmltimer() { } void tst_qqmltimer::initTestCase() { @@ -116,7 +112,7 @@ void tst_qqmltimer::notRepeating() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); QVERIFY(timer->isRunning()); QVERIFY(!timer->isRepeating()); QCOMPARE(timer->interval(), 100); @@ -138,7 +134,7 @@ void tst_qqmltimer::notRepeatingStart() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100 }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); QVERIFY(!timer->isRunning()); TimerHelper helper; @@ -163,7 +159,7 @@ void tst_qqmltimer::repeat() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; repeat: true; running: true }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -205,7 +201,7 @@ void tst_qqmltimer::triggeredOnStart() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); QVERIFY(timer->triggeredOnStart()); TimerHelper helper; @@ -239,7 +235,7 @@ void tst_qqmltimer::triggeredOnStartRepeat() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 100; running: true; triggeredOnStart: true; repeat: true }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -268,7 +264,7 @@ void tst_qqmltimer::noTriggerIfNotRunning() "}" ), QUrl::fromLocalFile("")); QObject *item = component.create(); - QVERIFY(item != 0); + QVERIFY(item != nullptr); consistentWait(200); QCOMPARE(item->property("ok").toBool(), true); @@ -281,7 +277,7 @@ void tst_qqmltimer::changeDuration() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 200; repeat: true; running: true }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -317,7 +313,7 @@ void tst_qqmltimer::restart() QQmlComponent component(&engine); component.setData(QByteArray("import QtQml 2.0\nTimer { interval: 500; repeat: true; running: true }"), QUrl::fromLocalFile("")); QQmlTimer *timer = qobject_cast<QQmlTimer*>(component.create()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -350,7 +346,7 @@ void tst_qqmltimer::restartFromTriggered() " }"), QUrl::fromLocalFile("")); QScopedPointer<QObject> object(component.create()); QQmlTimer *timer = qobject_cast<QQmlTimer*>(object.data()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -378,7 +374,7 @@ void tst_qqmltimer::runningFromTriggered() " }"), QUrl::fromLocalFile("")); QScopedPointer<QObject> object(component.create()); QQmlTimer *timer = qobject_cast<QQmlTimer*>(object.data()); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); TimerHelper helper; connect(timer, SIGNAL(triggered()), &helper, SLOT(timeout())); @@ -401,9 +397,9 @@ void tst_qqmltimer::parentProperty() QQmlComponent component(&engine); component.setData(QByteArray("import QtQuick 2.0\nItem { Timer { objectName: \"timer\"; running: parent.visible } }"), QUrl::fromLocalFile("")); QQuickItem *item = qobject_cast<QQuickItem*>(component.create()); - QVERIFY(item != 0); + QVERIFY(item != nullptr); QQmlTimer *timer = item->findChild<QQmlTimer*>("timer"); - QVERIFY(timer != 0); + QVERIFY(timer != nullptr); QVERIFY(timer->isRunning()); diff --git a/tests/auto/qml/qqmltranslation/data/TranslationChangeBase.qml b/tests/auto/qml/qqmltranslation/data/TranslationChangeBase.qml new file mode 100644 index 0000000000..294fff3284 --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/TranslationChangeBase.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property string baseProperty: qsTr("translate me"); +} diff --git a/tests/auto/qml/qqmltranslation/data/translationChange.qml b/tests/auto/qml/qqmltranslation/data/translationChange.qml new file mode 100644 index 0000000000..ae3231935c --- /dev/null +++ b/tests/auto/qml/qqmltranslation/data/translationChange.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +TranslationChangeBase { + id: root + + ListModel { + id: listModel + ListElement { + text: qsTr("translate me") + } + } + + baseProperty: "do not translate" + property string text1: qsTr("translate me") + function weDoTranslations() { + return qsTr("translate me") + } + property string text2: weDoTranslations() + property string text3 + property string fromListModel: listModel.get(0).text + + states: [ + State { + name: "default" + when: 1 == 1 + PropertyChanges { + target: root + text3: qsTr("translate me") + } + } + ] +} diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 1fc803a395..809a9bd9db 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -44,6 +44,7 @@ private slots: void translation_data(); void translation(); void idTranslation(); + void translationChange(); }; void tst_qqmltranslation::translation_data() @@ -70,12 +71,12 @@ void tst_qqmltranslation::translation() QQmlEngine engine; QQmlComponent component(&engine, testFile); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); if (verifyCompiledData) { QQmlContext *context = qmlContext(object); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); - QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl()); + QQmlRefPointer<QQmlTypeData> typeData = engine->typeLoader.getType(context->baseUrl()); QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); QVERIFY(compilationUnit); @@ -84,11 +85,10 @@ void tst_qqmltranslation::translation() << QStringLiteral("disambiguation") << QStringLiteral("singular") << QStringLiteral("plural"); - const QV4::CompiledData::Unit *unit = compilationUnit->data; - const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { - const QString propertyName = unit->stringAt(binding->propertyNameIndex); + const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); const bool expectCompiledTranslation = compiledTranslations.contains(propertyName); @@ -130,20 +130,19 @@ void tst_qqmltranslation::idTranslation() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("idtranslation.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); { QQmlContext *context = qmlContext(object); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine()); - QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl()); + QQmlRefPointer<QQmlTypeData> typeData = engine->typeLoader.getType(context->baseUrl()); QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit(); QVERIFY(compilationUnit); - const QV4::CompiledData::Unit *unit = compilationUnit->data; - const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { - const QString propertyName = unit->stringAt(binding->propertyNameIndex); + const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); if (propertyName == "idTranslation") { if (binding->type != QV4::CompiledData::Binding::Type_TranslationById) qDebug() << "binding for property" << propertyName << "is not a compiled translation"; @@ -162,6 +161,55 @@ void tst_qqmltranslation::idTranslation() delete object; } +class DummyTranslator : public QTranslator +{ + Q_OBJECT + + QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override + { + Q_UNUSED(context); + Q_UNUSED(disambiguation); + Q_UNUSED(n); + if (!qstrcmp(sourceText, "translate me")) + return QString::fromUtf8("xxx"); + return QString(); + } + + bool isEmpty() const override + { + return false; + } +}; + +void tst_qqmltranslation::translationChange() +{ + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("translationChange.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->property("baseProperty").toString(), QString::fromUtf8("do not translate")); + QCOMPARE(object->property("text1").toString(), QString::fromUtf8("translate me")); + QCOMPARE(object->property("text2").toString(), QString::fromUtf8("translate me")); + QCOMPARE(object->property("text3").toString(), QString::fromUtf8("translate me")); + QCOMPARE(object->property("fromListModel").toString(), QString::fromUtf8("translate me")); + + DummyTranslator translator; + QCoreApplication::installTranslator(&translator); + + QEvent ev(QEvent::LanguageChange); + QCoreApplication::sendEvent(&engine, &ev); + + QCOMPARE(object->property("baseProperty").toString(), QString::fromUtf8("do not translate")); + QCOMPARE(object->property("text1").toString(), QString::fromUtf8("xxx")); + QCOMPARE(object->property("text2").toString(), QString::fromUtf8("xxx")); + QCOMPARE(object->property("text3").toString(), QString::fromUtf8("xxx")); + QCOMPARE(object->property("fromListModel").toString(), QString::fromUtf8("xxx")); + + QCoreApplication::removeTranslator(&translator); +} + QTEST_MAIN(tst_qqmltranslation) #include "tst_qqmltranslation.moc" diff --git a/tests/auto/qml/qqmltypeloader/data/Base.qml b/tests/auto/qml/qqmltypeloader/data/Base.qml new file mode 100644 index 0000000000..431c659424 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Base.qml @@ -0,0 +1,3 @@ +import QtQml 2.0 + +QtObject {} diff --git a/tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml b/tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml new file mode 100644 index 0000000000..b3610831df --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/ComponentWithIncubator.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + Repeater { + model: 3 + Item {} + } +} + diff --git a/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml new file mode 100644 index 0000000000..f4ad5e5f7a --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton1.qml @@ -0,0 +1,6 @@ +pragma Singleton +import QtQuick 2.0 + +Item { + property bool ok: true +} diff --git a/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml new file mode 100644 index 0000000000..55dd57517f --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/CppRegisteredSingleton2.qml @@ -0,0 +1,7 @@ +pragma Singleton +import QtQuick 2.0 +import cppsingletonmodule 1.0 + +Item { + property bool ok: CppRegisteredSingleton1.ok +} diff --git a/tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml b/tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml new file mode 100644 index 0000000000..e879577e10 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Fast/Fast.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 + +QtObject { +} diff --git a/tests/auto/qml/qqmltypeloader/data/Fast/qmldir b/tests/auto/qml/qqmltypeloader/data/Fast/qmldir new file mode 100644 index 0000000000..a6a4c9e6c7 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Fast/qmldir @@ -0,0 +1 @@ +Fast 1.0 Fast.qml diff --git a/tests/auto/qml/qqmltypeloader/data/Intercept.qml b/tests/auto/qml/qqmltypeloader/data/Intercept.qml new file mode 100644 index 0000000000..b557b4b941 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Intercept.qml @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 Fast 1.0 + +Item { + Rectangle { + color: "red" + width: 100 + height: 100 + } + Fast { + + } +} diff --git a/tests/auto/qml/qqmltypeloader/data/Load.qml b/tests/auto/qml/qqmltypeloader/data/Load.qml new file mode 100644 index 0000000000..0b893bb5cd --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Load.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Item { + property int xy: loader.xy + Loader { + id: loader + asynchronous: true + source: 'Base.qml' + property int xy: item.xy + } +} diff --git a/tests/auto/qml/qqmltypeloader/data/Singleton.qml b/tests/auto/qml/qqmltypeloader/data/Singleton.qml new file mode 100644 index 0000000000..3a1b1c1493 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/Singleton.qml @@ -0,0 +1,7 @@ +pragma Singleton +import QtQml 2.0 +import modulewithsingleton 1.0 + +QtObject { + property bool ok: true +} diff --git a/tests/auto/qml/qqmltypeloader/data/ValueSource.qml b/tests/auto/qml/qqmltypeloader/data/ValueSource.qml new file mode 100644 index 0000000000..19e6e730c8 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/ValueSource.qml @@ -0,0 +1,7 @@ +pragma Singleton +import QtQuick 2.6 + +Item { + id: valueSource + property int something: 10 +} diff --git a/tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml b/tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml new file mode 100644 index 0000000000..9cebc88c8b --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/implicitcomponent.qml @@ -0,0 +1,10 @@ +import QtQml 2.2 + +QtObject { + property Component some: QtObject { + property int rrr: 2 + property Component onemore: QtObject { + property int brrrr: -1 + } + } +} diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml new file mode 100644 index 0000000000..34eca59f86 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton1.qml @@ -0,0 +1,7 @@ +pragma Singleton +import QtQuick 2.0 +import "." + +Item { + property bool ok: true +} diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml new file mode 100644 index 0000000000..607d85d7fb --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/Singleton2.qml @@ -0,0 +1,7 @@ +pragma Singleton +import QtQuick 2.0 +import "." + +Item { + property bool ok: Singleton1.ok +} diff --git a/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir new file mode 100644 index 0000000000..71b889a12d --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/imports/multisingletonmodule/qmldir @@ -0,0 +1,2 @@ +singleton Singleton1 1.0 Singleton1.qml +singleton Singleton2 1.0 Singleton2.qml diff --git a/tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml b/tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml new file mode 100644 index 0000000000..b80e2c5223 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/multisingletonuser.qml @@ -0,0 +1,7 @@ +import QtQml 2.0 +import multisingletonmodule 1.0 +import cppsingletonmodule 1.0 + +QtObject { + property bool ok: Singleton2.ok && CppRegisteredSingleton2.ok +} diff --git a/tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml b/tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml new file mode 100644 index 0000000000..62954fe1b2 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/redirected/Imported.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property int xy: 323232 +} diff --git a/tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml b/tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml new file mode 100644 index 0000000000..40fec5ed31 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/redirected/Redirected.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +Imported { + +} diff --git a/tests/auto/qml/qqmltypeloader/data/redirected/qmldir b/tests/auto/qml/qqmltypeloader/data/redirected/qmldir new file mode 100644 index 0000000000..8eb1fa5c18 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/redirected/qmldir @@ -0,0 +1 @@ +Imported 1.0 Imported.qml diff --git a/tests/auto/qml/qqmltypeloader/data/singletonuser.qml b/tests/auto/qml/qqmltypeloader/data/singletonuser.qml new file mode 100644 index 0000000000..79ca47e12f --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/singletonuser.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import modulewithsingleton 1.0 + +QtObject { + property bool ok: Singleton.ok +} diff --git a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml new file mode 100644 index 0000000000..0d64cb7e28 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 + +ListView { + width: 400 + height: 500 + model: 2 + + id: test + property int created: 0 + property int loaded: 0 + + delegate: Loader { + width: ListView.view.width + height: 100 + asynchronous: true + source: index == 0 ? "Intercept.qml" : "GenericView.qml" + + onLoaded: { + test.loaded++ + } + Component.onCompleted: { + test.created++ + } + } +} diff --git a/tests/auto/qml/qqmltypeloader/data/trim_cache3.qml b/tests/auto/qml/qqmltypeloader/data/trim_cache3.qml new file mode 100644 index 0000000000..219c7d3bcb --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/trim_cache3.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + width: 400 + height: 400 + + property alias source: loader.source + + Loader { + id: loader + source: "ComponentWithIncubator.qml" + } +} + diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 3d3a7ff725..0c4abf19f4 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -28,10 +28,15 @@ #include <QtTest/QtTest> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlnetworkaccessmanagerfactory.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> +#if QT_CONFIG(process) +#include <QtCore/qprocess.h> +#endif #include <QtQml/private/qqmlengine_p.h> #include <QtQml/private/qqmltypeloader_p.h> +#include "../../shared/testhttpserver.h" #include "../../shared/util.h" class tst_QQMLTypeLoader : public QQmlDataTest @@ -43,6 +48,14 @@ private slots: void loadComponentSynchronously(); void trimCache(); void trimCache2(); + void trimCache3(); + void keepSingleton(); + void keepRegistrations(); + void intercept(); + void redirect(); + void qmlSingletonWithinModule(); + void multiSingletonModule(); + void implicitComponentModule(); }; void tst_QQMLTypeLoader::testLoadComplete() @@ -56,7 +69,7 @@ void tst_QQMLTypeLoader::testLoadComplete() QVERIFY(QTest::qWaitForWindowExposed(window)); QObject *rootObject = window->rootObject(); - QTRY_VERIFY(rootObject != 0); + QTRY_VERIFY(rootObject != nullptr); QTRY_COMPARE(rootObject->property("created").toInt(), 2); QTRY_COMPARE(rootObject->property("loaded").toInt(), 2); delete window; @@ -68,7 +81,7 @@ void tst_QQMLTypeLoader::loadComponentSynchronously() QTest::ignoreMessage(QtWarningMsg, QRegularExpression( QLatin1String(".*nonprotocol::1:1: QtObject is not a type.*"))); QQmlComponent component(&engine, testFileUrl("load_synchronous.qml")); - QObject *o = component.create(); + QScopedPointer<QObject> o(component.create()); QVERIFY(o); } @@ -80,7 +93,7 @@ void tst_QQMLTypeLoader::trimCache() QUrl url = testFileUrl("trim_cache.qml"); url.setQuery(QString::number(i)); - QQmlTypeData *data = loader.getType(url); + QQmlTypeData *data = loader.getType(url).take(); // Run an event loop to receive the callback that release()es. QTRY_COMPARE(data->count(), 2); @@ -109,7 +122,7 @@ void tst_QQMLTypeLoader::trimCache() void tst_QQMLTypeLoader::trimCache2() { - QQuickView *window = new QQuickView(); + QScopedPointer<QQuickView> window(new QQuickView()); window->setSource(testFileUrl("trim_cache2.qml")); QQmlTypeLoader &loader = QQmlEnginePrivate::get(window->engine())->typeLoader; // in theory if gc has already run this could be false @@ -120,6 +133,376 @@ void tst_QQMLTypeLoader::trimCache2() QCOMPARE(loader.isTypeLoaded(testFileUrl("MyComponent2.qml")), false); } +// test trimming the cache of an item that contains sub-items created via incubation +void tst_QQMLTypeLoader::trimCache3() +{ + QScopedPointer<QQuickView> window(new QQuickView()); + window->setSource(testFileUrl("trim_cache3.qml")); + QQmlTypeLoader &loader = QQmlEnginePrivate::get(window->engine())->typeLoader; + QCOMPARE(loader.isTypeLoaded(testFileUrl("ComponentWithIncubator.qml")), true); + + QQmlProperty::write(window->rootObject(), "source", QString()); + + // handle our deleteLater and cleanup + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + window->engine()->collectGarbage(); + + window->engine()->trimComponentCache(); + + QCOMPARE(loader.isTypeLoaded(testFileUrl("ComponentWithIncubator.qml")), false); +} + +static void checkSingleton(const QString &dataDirectory) +{ + QQmlEngine engine; + engine.addImportPath(dataDirectory); + QQmlComponent component(&engine); + component.setData("import ClusterDemo 1.0\n" + "import QtQuick 2.6\n" + "import \"..\"\n" + "Item { property int t: ValueSource.something }", + QUrl::fromLocalFile(dataDirectory + "/abc/Xyz.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o.data()); + QCOMPARE(o->property("t").toInt(), 10); +} + +void tst_QQMLTypeLoader::keepSingleton() +{ + qmlRegisterSingletonType(testFileUrl("ValueSource.qml"), "ClusterDemo", 1, 0, "ValueSource"); + checkSingleton(dataDirectory()); + QQmlMetaType::freeUnusedTypesAndCaches(); + checkSingleton(dataDirectory()); +} + +class TestObject : public QObject +{ + Q_OBJECT +public: + TestObject(QObject *parent = nullptr) : QObject(parent) {} +}; + +QML_DECLARE_TYPE(TestObject) + +static void verifyTypes(bool shouldHaveTestObject, bool shouldHaveFast) +{ + bool hasTestObject = false; + bool hasFast = false; + for (const QQmlType &type : QQmlMetaType::qmlAllTypes()) { + if (type.elementName() == QLatin1String("Fast")) + hasFast = true; + else if (type.elementName() == QLatin1String("TestObject")) + hasTestObject = true; + } + QCOMPARE(hasTestObject, shouldHaveTestObject); + QCOMPARE(hasFast, shouldHaveFast); +} + +void tst_QQMLTypeLoader::keepRegistrations() +{ + verifyTypes(false, false); + qmlRegisterType<TestObject>("Test", 1, 0, "TestObject"); + verifyTypes(true, false); + + { + QQmlEngine engine; + engine.addImportPath(dataDirectory()); + QQmlComponent component(&engine); + component.setData("import Fast 1.0\nFast {}", QUrl()); + QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8().constData()); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o.data()); + verifyTypes(true, true); + } + + verifyTypes(true, false); // Fast is gone again, even though an event was still scheduled. + QQmlMetaType::freeUnusedTypesAndCaches(); + verifyTypes(true, false); // qmlRegisterType creates an undeletable type. +} + +class NetworkReply : public QNetworkReply +{ +public: + NetworkReply() + { + open(QIODevice::ReadOnly); + } + + void setData(const QByteArray &data) + { + if (isFinished()) + return; + m_buffer = data; + emit readyRead(); + setFinished(true); + emit finished(); + } + + void fail() + { + if (isFinished()) + return; + m_buffer.clear(); + setError(ContentNotFoundError, "content not found"); + emit error(ContentNotFoundError); + setFinished(true); + emit finished(); + } + + qint64 bytesAvailable() const override + { + return m_buffer.size(); + } + + qint64 readData(char *data, qint64 maxlen) override + { + if (m_buffer.length() < maxlen) + maxlen = m_buffer.length(); + std::memcpy(data, m_buffer.data(), maxlen); + m_buffer.remove(0, maxlen); + return maxlen; + } + + void abort() override + { + if (isFinished()) + return; + m_buffer.clear(); + setFinished(true); + emit finished(); + } + +private: + QByteArray m_buffer; +}; + +class NetworkAccessManager : public QNetworkAccessManager +{ + Q_OBJECT +public: + + NetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent) + { + } + + QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, + QIODevice *outgoingData) override + { + QUrl url = request.url(); + QString scheme = url.scheme(); + if (op != GetOperation || !scheme.endsWith("+debug")) + return QNetworkAccessManager::createRequest(op, request, outgoingData); + + scheme.chop(sizeof("+debug") - 1); + url.setScheme(scheme); + + NetworkReply *reply = new NetworkReply; + QString filename = QQmlFile::urlToLocalFileOrQrc(url); + QTimer::singleShot(10, reply, [this, reply, filename]() { + if (filename.isEmpty()) { + reply->fail(); + } else { + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + emit loaded(filename); + reply->setData(transformQmldir(filename, file.readAll())); + } else + reply->fail(); + } + }); + return reply; + } + + QByteArray transformQmldir(const QString &filename, const QByteArray &content) + { + if (!filename.endsWith("/qmldir")) + return content; + + // Make qmldir plugin paths absolute, so that we don't try to load them over the network + QByteArray result; + QByteArray path = filename.toUtf8(); + path.chop(sizeof("qmldir") - 1); + for (QByteArray line : content.split('\n')) { + if (line.isEmpty()) + continue; + QList<QByteArray> segments = line.split(' '); + if (segments.startsWith("plugin")) { + if (segments.length() == 2) { + segments.append(path); + } else if (segments.length() == 3) { + if (!segments[2].startsWith('/')) + segments[2] = path + segments[2]; + } else { + // Invalid plugin declaration. Ignore + } + result.append(segments.join(' ')); + } else { + result.append(line); + } + result.append('\n'); + } + return result; + } + +signals: + void loaded(const QString &filename); +}; + +class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory +{ +public: + QStringList loadedFiles; + + QNetworkAccessManager *create(QObject *parent) override + { + NetworkAccessManager *manager = new NetworkAccessManager(parent); + QObject::connect(manager, &NetworkAccessManager::loaded, [this](const QString &filename) { + loadedFiles.append(filename); + }); + return manager; + } +}; + +class UrlInterceptor : public QQmlAbstractUrlInterceptor +{ +public: + QUrl intercept(const QUrl &path, DataType type) override + { + Q_UNUSED(type); + if (!QQmlFile::isLocalFile(path)) + return path; + + QUrl result = path; + QString scheme = result.scheme(); + if (!scheme.endsWith("+debug")) + result.setScheme(scheme + "+debug"); + return result; + } +}; + +void tst_QQMLTypeLoader::intercept() +{ + qmlClearTypeRegistrations(); + + QQmlEngine engine; + engine.addImportPath(dataDirectory()); + engine.addImportPath(QT_TESTCASE_BUILDDIR); + + UrlInterceptor interceptor; + NetworkAccessManagerFactory factory; + + engine.setUrlInterceptor(&interceptor); + engine.setNetworkAccessManagerFactory(&factory); + + QQmlComponent component(&engine, testFileUrl("test_intercept.qml")); + + QVERIFY(component.status() != QQmlComponent::Ready); + QTRY_VERIFY2(component.status() == QQmlComponent::Ready, + component.errorString().toUtf8().constData()); + + QScopedPointer<QObject> o(component.create()); + QVERIFY(o.data()); + + QTRY_COMPARE(o->property("created").toInt(), 2); + QTRY_COMPARE(o->property("loaded").toInt(), 2); + + QVERIFY(factory.loadedFiles.length() >= 6); + QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/test_intercept.qml")); + QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Intercept.qml")); + QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Fast/qmldir")); + QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Fast/Fast.qml")); + QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/GenericView.qml")); + QVERIFY(factory.loadedFiles.contains(QLatin1String(QT_TESTCASE_BUILDDIR) + "/Slow/qmldir")); +} + +void tst_QQMLTypeLoader::redirect() +{ + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.serveDirectory(dataDirectory())); + server.addRedirect("Base.qml", server.urlString("/redirected/Redirected.qml")); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(server.urlString("/Load.qml"), QQmlComponent::Asynchronous); + QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString())); + + QObject *object = component.create(); + QTRY_COMPARE(object->property("xy").toInt(), 323232); +} + +void tst_QQMLTypeLoader::qmlSingletonWithinModule() +{ + qmlClearTypeRegistrations(); + QQmlEngine engine; + qmlRegisterSingletonType(testFileUrl("Singleton.qml"), "modulewithsingleton", 1, 0, "Singleton"); + + QQmlComponent component(&engine, testFileUrl("singletonuser.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVERIFY(obj->property("ok").toBool()); +} + +static void checkCleanCacheLoad(const QString &testCase) +{ +#if QT_CONFIG(process) + const char *skipKey = "QT_TST_QQMLTYPELOADER_SKIP_MISMATCH"; + if (qEnvironmentVariableIsSet(skipKey)) + return; + for (int i = 0; i < 5; ++i) { + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(testCase)); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("QT_LOGGING_RULES"), QLatin1String("qt.qml.diskcache.debug=true")); + env.insert(QLatin1String(skipKey), QLatin1String("1")); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + QCOMPARE(child.exitCode(), 0); + QVERIFY(!child.readAllStandardOutput().contains("Checksum mismatch for cached version")); + QVERIFY(!child.readAllStandardError().contains("Checksum mismatch for cached version")); + } +#else + Q_UNUSED(testCase); +#endif +} + +void tst_QQMLTypeLoader::multiSingletonModule() +{ + qmlClearTypeRegistrations(); + QQmlEngine engine; + engine.addImportPath(testFile("imports")); + + qmlRegisterSingletonType(testFileUrl("CppRegisteredSingleton1.qml"), "cppsingletonmodule", + 1, 0, "CppRegisteredSingleton1"); + qmlRegisterSingletonType(testFileUrl("CppRegisteredSingleton2.qml"), "cppsingletonmodule", + 1, 0, "CppRegisteredSingleton2"); + + QQmlComponent component(&engine, testFileUrl("multisingletonuser.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QVERIFY(obj->property("ok").toBool()); + + checkCleanCacheLoad(QLatin1String("multiSingletonModule")); +} + +void tst_QQMLTypeLoader::implicitComponentModule() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("implicitcomponent.qml")); + QCOMPARE(component.status(), QQmlComponent::Ready); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + checkCleanCacheLoad(QLatin1String("implicitComponentModule")); +} + 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 3a20e94741..0352561e03 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.pro @@ -3,7 +3,12 @@ TARGET = tst_qqmltypeloader QT += qml testlib qml-private quick macx:CONFIG -= app_bundle -SOURCES += tst_qqmltypeloader.cpp +SOURCES += \ + tst_qqmltypeloader.cpp \ + ../../shared/testhttpserver.cpp + +HEADERS += \ + ../../shared/testhttpserver.h include (../../shared/util.pri) diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp index 1902801c8f..22074602b7 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp @@ -78,7 +78,7 @@ void tst_qqmlvaluetypeproviders::qtqmlValueTypes() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("qtqmlTypeSuccess").toBool()); QVERIFY(object->property("qtquickTypeSuccess").toBool()); delete object; @@ -91,7 +91,7 @@ void tst_qqmlvaluetypeproviders::qtquickValueTypes() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("qtqmlTypeSuccess").toBool()); QVERIFY(object->property("qtquickTypeSuccess").toBool()); delete object; @@ -104,7 +104,7 @@ void tst_qqmlvaluetypeproviders::comparisonSemantics() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("comparisonSuccess").toBool()); delete object; } @@ -116,7 +116,7 @@ void tst_qqmlvaluetypeproviders::cppIntegration() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); // ensure accessing / comparing / assigning cpp-defined props // and qml-defined props works in QML. @@ -156,7 +156,7 @@ void tst_qqmlvaluetypeproviders::jsObjectConversion() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("qtquickTypeSuccess").toBool()); delete object; } @@ -168,7 +168,7 @@ void tst_qqmlvaluetypeproviders::invokableFunctions() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("complete").toBool()); QVERIFY(object->property("success").toBool()); delete object; @@ -233,7 +233,7 @@ public: if (type == qMetaTypeId<TestValue>()) return &TestValueType::staticMetaObject; - return 0; + return nullptr; } }; @@ -284,7 +284,7 @@ void tst_qqmlvaluetypeproviders::userType() QQmlComponent component(&e, testFileUrl("userType.qml")); QScopedPointer<QObject> obj(component.create()); - QVERIFY(obj != 0); + QVERIFY(obj != nullptr); QCOMPARE(obj->property("success").toBool(), true); } @@ -295,7 +295,7 @@ void tst_qqmlvaluetypeproviders::changedSignal() QVERIFY(!component.isError()); QVERIFY(component.errors().isEmpty()); QScopedPointer<QObject> object(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("complete").toBool()); QVERIFY(object->property("success").toBool()); } diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index 163ce11cb8..8a01524b5b 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -89,6 +89,8 @@ private slots: void customValueType(); void customValueTypeInQml(); void gadgetInheritance(); + void gadgetTemplateInheritance(); + void sequences(); void toStringConversion(); void enumerableProperties(); void enumProperties(); @@ -108,7 +110,7 @@ void tst_qqmlvaluetypes::point() { QQmlComponent component(&engine, testFileUrl("point_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("p_x").toInt(), 10); QCOMPARE(object->property("p_y").toInt(), 4); @@ -120,7 +122,7 @@ void tst_qqmlvaluetypes::point() { QQmlComponent component(&engine, testFileUrl("point_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->point(), QPoint(11, 12)); @@ -130,7 +132,7 @@ void tst_qqmlvaluetypes::point() { QQmlComponent component(&engine, testFileUrl("point_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QPoint(10, 4)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -153,7 +155,7 @@ void tst_qqmlvaluetypes::pointf() { QQmlComponent component(&engine, testFileUrl("pointf_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(float(object->property("p_x").toDouble()), float(11.3)); QCOMPARE(float(object->property("p_y").toDouble()), float(-10.9)); @@ -165,7 +167,7 @@ void tst_qqmlvaluetypes::pointf() { QQmlComponent component(&engine, testFileUrl("pointf_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->pointf(), QPointF(6.8, 9.3)); @@ -175,7 +177,7 @@ void tst_qqmlvaluetypes::pointf() { QQmlComponent component(&engine, testFileUrl("pointf_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QPointF(11.3, -10.9)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -198,7 +200,7 @@ void tst_qqmlvaluetypes::size() { QQmlComponent component(&engine, testFileUrl("size_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("s_width").toInt(), 1912); QCOMPARE(object->property("s_height").toInt(), 1913); @@ -210,7 +212,7 @@ void tst_qqmlvaluetypes::size() { QQmlComponent component(&engine, testFileUrl("size_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->size(), QSize(13, 88)); @@ -220,7 +222,7 @@ void tst_qqmlvaluetypes::size() { QQmlComponent component(&engine, testFileUrl("size_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QSize(1912, 1913)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -243,7 +245,7 @@ void tst_qqmlvaluetypes::sizef() { QQmlComponent component(&engine, testFileUrl("sizef_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(float(object->property("s_width").toDouble()), float(0.1)); QCOMPARE(float(object->property("s_height").toDouble()), float(100923.2)); @@ -255,7 +257,7 @@ void tst_qqmlvaluetypes::sizef() { QQmlComponent component(&engine, testFileUrl("sizef_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->sizef(), QSizeF(44.3, 92.8)); @@ -265,7 +267,7 @@ void tst_qqmlvaluetypes::sizef() { QQmlComponent component(&engine, testFileUrl("sizef_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QSizeF(0.1, 100923)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -288,7 +290,7 @@ void tst_qqmlvaluetypes::variant() { QQmlComponent component(&engine, testFileUrl("variant_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(float(object->property("s_width").toDouble()), float(0.1)); QCOMPARE(float(object->property("s_height").toDouble()), float(100923.2)); @@ -300,7 +302,7 @@ void tst_qqmlvaluetypes::variant() { QQmlComponent component(&engine, testFileUrl("variant_write.1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("complete").toBool()); QVERIFY(object->property("success").toBool()); delete object; @@ -309,7 +311,7 @@ void tst_qqmlvaluetypes::variant() { QQmlComponent component(&engine, testFileUrl("variant_write.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("complete").toBool()); QVERIFY(object->property("success").toBool()); delete object; @@ -359,7 +361,7 @@ void tst_qqmlvaluetypes::sizereadonly() { QQmlComponent component(&engine, testFileUrl("sizereadonly_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("s_width").toInt(), 1912); QCOMPARE(object->property("s_height").toInt(), 1913); @@ -403,7 +405,7 @@ void tst_qqmlvaluetypes::rect() { QQmlComponent component(&engine, testFileUrl("rect_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("r_x").toInt(), 2); QCOMPARE(object->property("r_y").toInt(), 3); @@ -421,7 +423,7 @@ void tst_qqmlvaluetypes::rect() { QQmlComponent component(&engine, testFileUrl("rect_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect(), QRect(1234, 7, 56, 63)); @@ -431,7 +433,7 @@ void tst_qqmlvaluetypes::rect() { QQmlComponent component(&engine, testFileUrl("rect_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QRect(2, 3, 109, 102)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -454,7 +456,7 @@ void tst_qqmlvaluetypes::rectf() { QQmlComponent component(&engine, testFileUrl("rectf_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(float(object->property("r_x").toDouble()), float(103.8)); QCOMPARE(float(object->property("r_y").toDouble()), float(99.2)); @@ -472,7 +474,7 @@ void tst_qqmlvaluetypes::rectf() { QQmlComponent component(&engine, testFileUrl("rectf_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rectf(), QRectF(70.1, -113.2, 80924.8, 99.2)); @@ -482,7 +484,7 @@ void tst_qqmlvaluetypes::rectf() { QQmlComponent component(&engine, testFileUrl("rectf_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QRectF(103.8, 99.2, 88.1, 77.6)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -505,7 +507,7 @@ void tst_qqmlvaluetypes::vector2d() { QQmlComponent component(&engine, testFileUrl("vector2d_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE((float)object->property("v_x").toDouble(), (float)32.88); QCOMPARE((float)object->property("v_y").toDouble(), (float)1.3); @@ -517,7 +519,7 @@ void tst_qqmlvaluetypes::vector2d() { QQmlComponent component(&engine, testFileUrl("vector2d_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->vector2(), QVector2D(-0.3f, -12.9f)); @@ -527,7 +529,7 @@ void tst_qqmlvaluetypes::vector2d() { QQmlComponent component(&engine, testFileUrl("vector2d_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QVector2D(32.88, 1.3)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -545,7 +547,7 @@ void tst_qqmlvaluetypes::vector2d() { QQmlComponent component(&engine, testFileUrl("vector2d_invokables.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); delete object; } @@ -556,7 +558,7 @@ void tst_qqmlvaluetypes::vector3d() { QQmlComponent component(&engine, testFileUrl("vector3d_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE((float)object->property("v_x").toDouble(), (float)23.88); QCOMPARE((float)object->property("v_y").toDouble(), (float)3.1); @@ -569,7 +571,7 @@ void tst_qqmlvaluetypes::vector3d() { QQmlComponent component(&engine, testFileUrl("vector3d_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->vector(), QVector3D(-0.3f, -12.9f, 907.4f)); @@ -579,7 +581,7 @@ void tst_qqmlvaluetypes::vector3d() { QQmlComponent component(&engine, testFileUrl("vector3d_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QVector3D(23.88, 3.1, 4.3)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -598,7 +600,7 @@ void tst_qqmlvaluetypes::vector3d() { QQmlComponent component(&engine, testFileUrl("vector3d_invokables.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); delete object; } @@ -609,7 +611,7 @@ void tst_qqmlvaluetypes::vector4d() { QQmlComponent component(&engine, testFileUrl("vector4d_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2); QCOMPARE((float)object->property("v_y").toDouble(), (float)23.88); @@ -623,7 +625,7 @@ void tst_qqmlvaluetypes::vector4d() { QQmlComponent component(&engine, testFileUrl("vector4d_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->vector4(), QVector4D(-0.3f, -12.9f, 907.4f, 88.5f)); @@ -633,7 +635,7 @@ void tst_qqmlvaluetypes::vector4d() { QQmlComponent component(&engine, testFileUrl("vector4d_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QVector4D(54.2, 23.88, 3.1, 4.3)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -651,7 +653,7 @@ void tst_qqmlvaluetypes::vector4d() { QQmlComponent component(&engine, testFileUrl("vector4d_invokables.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); delete object; } @@ -662,7 +664,7 @@ void tst_qqmlvaluetypes::quaternion() { QQmlComponent component(&engine, testFileUrl("quaternion_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE((float)object->property("v_scalar").toDouble(), (float)4.3); QCOMPARE((float)object->property("v_x").toDouble(), (float)54.2); @@ -676,7 +678,7 @@ void tst_qqmlvaluetypes::quaternion() { QQmlComponent component(&engine, testFileUrl("quaternion_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->quaternion(), QQuaternion(88.5f, -0.3f, -12.9f, 907.4f)); @@ -686,7 +688,7 @@ void tst_qqmlvaluetypes::quaternion() { QQmlComponent component(&engine, testFileUrl("quaternion_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QQuaternion(4.3, 54.2, 23.88, 3.1)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -707,7 +709,7 @@ void tst_qqmlvaluetypes::matrix4x4() { QQmlComponent component(&engine, testFileUrl("matrix4x4_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE((float)object->property("v_m11").toDouble(), (float)1); QCOMPARE((float)object->property("v_m12").toDouble(), (float)2); @@ -737,7 +739,7 @@ void tst_qqmlvaluetypes::matrix4x4() { QQmlComponent component(&engine, testFileUrl("matrix4x4_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->matrix(), QMatrix4x4(11, 12, 13, 14, 21, 22, 23, 24, @@ -750,7 +752,7 @@ void tst_qqmlvaluetypes::matrix4x4() { QQmlComponent component(&engine, testFileUrl("matrix4x4_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QMatrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)"); QCOMPARE(object->property("tostring").toString(), tostring); @@ -768,7 +770,7 @@ void tst_qqmlvaluetypes::matrix4x4() { QQmlComponent component(&engine, testFileUrl("matrix4x4_invokables.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); delete object; } @@ -779,7 +781,7 @@ void tst_qqmlvaluetypes::font() { QQmlComponent component(&engine, testFileUrl("font_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("f_family").toString(), object->font().family()); QCOMPARE(object->property("f_bold").toBool(), object->font().bold()); @@ -802,7 +804,7 @@ void tst_qqmlvaluetypes::font() { QQmlComponent component(&engine, testFileUrl("font_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QFont font; font.setFamily("Helvetica"); @@ -835,7 +837,7 @@ void tst_qqmlvaluetypes::font() { QQmlComponent component(&engine, testFileUrl("font_write.2.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().pixelSize(), 10); @@ -847,7 +849,7 @@ void tst_qqmlvaluetypes::font() QQmlComponent component(&engine, testFileUrl("font_write.3.qml")); QTest::ignoreMessage(QtWarningMsg, "Both point size and pixel size set. Using pixel size."); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().pixelSize(), 10); @@ -857,7 +859,7 @@ void tst_qqmlvaluetypes::font() QQmlComponent component(&engine, testFileUrl("font_write.4.qml")); QTest::ignoreMessage(QtWarningMsg, "Both point size and pixel size set. Using pixel size."); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().pixelSize(), 10); @@ -866,11 +868,11 @@ void tst_qqmlvaluetypes::font() { QQmlComponent component(&engine, testFileUrl("font_write.5.qml")); QObject *object = qobject_cast<QObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); MyTypeObject *object1 = object->findChild<MyTypeObject *>("object1"); - QVERIFY(object1 != 0); + QVERIFY(object1 != nullptr); MyTypeObject *object2 = object->findChild<MyTypeObject *>("object2"); - QVERIFY(object2 != 0); + QVERIFY(object2 != nullptr); QCOMPARE(object1->font().pixelSize(), 19); QCOMPARE(object2->font().pointSize(), 14); @@ -881,7 +883,7 @@ void tst_qqmlvaluetypes::font() { QQmlComponent component(&engine, testFileUrl("font_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString tostring = QLatin1String("QFont(") + object->font().toString() + QLatin1Char(')'); QCOMPARE(object->property("tostring").toString(), tostring); @@ -902,7 +904,7 @@ void tst_qqmlvaluetypes::color() { QQmlComponent component(&engine, testFileUrl("color_read.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE((float)object->property("v_r").toDouble(), (float)0.2); QCOMPARE((float)object->property("v_g").toDouble(), (float)0.88); @@ -930,7 +932,7 @@ void tst_qqmlvaluetypes::color() { QQmlComponent component(&engine, testFileUrl("color_write.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QColor newColor; newColor.setRedF(0.5); @@ -945,7 +947,7 @@ void tst_qqmlvaluetypes::color() { QQmlComponent component(&engine, testFileUrl("color_write_HSV.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QColor newColor; newColor.setHsvF(0.43, 0.77, 0.88, 0.7); @@ -957,7 +959,7 @@ void tst_qqmlvaluetypes::color() { QQmlComponent component(&engine, testFileUrl("color_write_HSL.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QColor newColor; newColor.setHslF(0.43, 0.74, 0.54, 0.7); @@ -969,7 +971,7 @@ void tst_qqmlvaluetypes::color() { QQmlComponent component(&engine, testFileUrl("color_compare.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QString colorString("#5733e199"); QCOMPARE(object->property("colorToString").toString(), colorString); QCOMPARE(object->property("colorEqualsIdenticalRgba").toBool(), true); @@ -1004,7 +1006,7 @@ void tst_qqmlvaluetypes::bindingAssignment() { QQmlComponent component(&engine, testFileUrl("bindingAssignment.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 10); QCOMPARE(object->rect().y(), 15); @@ -1019,13 +1021,13 @@ void tst_qqmlvaluetypes::bindingAssignment() // function assignment should fail without crashing { - QString warning1 = testFileUrl("bindingAssignment.2.qml").toString() + QLatin1String(":6:13: Invalid use of Qt.binding() in a binding declaration."); + QString warning1 = testFileUrl("bindingAssignment.2.qml").toString() + QLatin1String(":6:5: Invalid use of Qt.binding() in a binding declaration."); QString warning2 = testFileUrl("bindingAssignment.2.qml").toString() + QLatin1String(":10: Cannot assign JavaScript function to value-type property"); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); QQmlComponent component(&engine, testFileUrl("bindingAssignment.2.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 5); object->setProperty("value", QVariant(92)); QCOMPARE(object->rect().x(), 5); @@ -1038,7 +1040,7 @@ void tst_qqmlvaluetypes::bindingRead() { QQmlComponent component(&engine, testFileUrl("bindingRead.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 2); @@ -1054,7 +1056,7 @@ void tst_qqmlvaluetypes::staticAssignment() { QQmlComponent component(&engine, testFileUrl("staticAssignment.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 9); @@ -1066,7 +1068,7 @@ void tst_qqmlvaluetypes::scriptAccess() { QQmlComponent component(&engine, testFileUrl("scriptAccess.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("valuePre").toInt(), 2); QCOMPARE(object->rect().x(), 19); @@ -1081,7 +1083,7 @@ void tst_qqmlvaluetypes::autoBindingRemoval() { QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 10); @@ -1103,7 +1105,7 @@ void tst_qqmlvaluetypes::autoBindingRemoval() { QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.2.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 10); @@ -1124,10 +1126,10 @@ void tst_qqmlvaluetypes::autoBindingRemoval() { QQmlComponent component(&engine, testFileUrl("autoBindingRemoval.3.qml")); - QString warning = component.url().toString() + ":6:11: Unable to assign [undefined] to QRect"; + QString warning = component.url().toString() + ":6:5: Unable to assign [undefined] to QRect"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); object->setProperty("value", QVariant(QRect(9, 22, 33, 44))); @@ -1150,7 +1152,7 @@ void tst_qqmlvaluetypes::valueSources() { QQmlComponent component(&engine, testFileUrl("valueSources.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 3345); @@ -1174,7 +1176,7 @@ void tst_qqmlvaluetypes::valueInterceptors() QQmlComponent component(&engine, testFileUrl("valueInterceptors.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); checkNoErrors(component); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect().x(), 13); @@ -1208,10 +1210,10 @@ void tst_qqmlvaluetypes::deletedObject() QQmlComponent component(&engine, testFileUrl("deletedObject.qml")); QTest::ignoreMessage(QtDebugMsg, "Test: 2"); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QObject *dObject = qvariant_cast<QObject *>(object->property("object")); - QVERIFY(dObject != 0); + QVERIFY(dObject != nullptr); delete dObject; QTest::ignoreMessage(QtDebugMsg, "Test: undefined"); @@ -1225,7 +1227,7 @@ void tst_qqmlvaluetypes::bindingVariantCopy() { QQmlComponent component(&engine, testFileUrl("bindingVariantCopy.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect(), QRect(19, 33, 5, 99)); @@ -1237,7 +1239,7 @@ void tst_qqmlvaluetypes::scriptVariantCopy() { QQmlComponent component(&engine, testFileUrl("scriptVariantCopy.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->rect(), QRect(2, 3, 109, 102)); @@ -1253,7 +1255,7 @@ void tst_qqmlvaluetypes::enums() { QQmlComponent component(&engine, testFileUrl("enums.1.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); delete object; } @@ -1261,7 +1263,7 @@ void tst_qqmlvaluetypes::enums() { QQmlComponent component(&engine, testFileUrl("enums.2.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); delete object; } @@ -1269,7 +1271,7 @@ void tst_qqmlvaluetypes::enums() { QQmlComponent component(&engine, testFileUrl("enums.3.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); delete object; } @@ -1277,7 +1279,7 @@ void tst_qqmlvaluetypes::enums() { QQmlComponent component(&engine, testFileUrl("enums.4.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); delete object; } @@ -1285,7 +1287,7 @@ void tst_qqmlvaluetypes::enums() { QQmlComponent component(&engine, testFileUrl("enums.5.qml")); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->font().capitalization(), QFont::AllUppercase); delete object; } @@ -1298,7 +1300,7 @@ void tst_qqmlvaluetypes::conflictingBindings() { QQmlComponent component(&engine, testFileUrl("conflicting.1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); @@ -1316,7 +1318,7 @@ void tst_qqmlvaluetypes::conflictingBindings() { QQmlComponent component(&engine, testFileUrl("conflicting.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 6); @@ -1334,7 +1336,7 @@ void tst_qqmlvaluetypes::conflictingBindings() { QQmlComponent component(&engine, testFileUrl("conflicting.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(qvariant_cast<QFont>(object->property("font")).pixelSize(), 12); @@ -1354,7 +1356,7 @@ void tst_qqmlvaluetypes::returnValues() { QQmlComponent component(&engine, testFileUrl("returnValues.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); @@ -1367,7 +1369,7 @@ void tst_qqmlvaluetypes::varAssignment() { QQmlComponent component(&engine, testFileUrl("varAssignment.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("x").toInt(), 1); QCOMPARE(object->property("y").toInt(), 2); @@ -1382,7 +1384,7 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly() { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.1.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1392,7 +1394,7 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly() { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.2.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1403,7 +1405,7 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly() { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.3.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1413,7 +1415,7 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly() { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.4.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1423,7 +1425,7 @@ void tst_qqmlvaluetypes::bindingsSpliceCorrectly() { QQmlComponent component(&engine, testFileUrl("bindingsSpliceCorrectly.5.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1435,7 +1437,7 @@ void tst_qqmlvaluetypes::nonValueTypeComparison() { QQmlComponent component(&engine, testFileUrl("nonValueTypeComparison.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); @@ -1447,7 +1449,7 @@ void tst_qqmlvaluetypes::initializeByWrite() { QQmlComponent component(&engine, testFileUrl("initializeByWrite.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(), true); @@ -1486,7 +1488,7 @@ void tst_qqmlvaluetypes::groupedInterceptors() QQmlComponent component(&engine, testFileUrl(qmlfile)); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY2(object != nullptr, qPrintable(component.errorString())); QColor initialColor = object->property("color").value<QColor>(); QVERIFY(fuzzyCompare(initialColor.redF(), expectedInitialColor.redF())); @@ -1589,6 +1591,7 @@ struct BaseGadget Q_PROPERTY(int baseProperty READ baseProperty WRITE setBaseProperty) public: BaseGadget() : m_baseProperty(0) {} + BaseGadget(int initValue) : m_baseProperty(initValue) {} int baseProperty() const { return m_baseProperty; } void setBaseProperty(int value) { m_baseProperty = value; } @@ -1613,6 +1616,19 @@ public: Q_INVOKABLE void functionInDerivedGadget(int value) { m_derivedProperty = value; } }; +// QTBUG-66744: we want a Q_GADGET giving us generic type safety in C++ and property access in Qml +template <typename T> +struct DerivedTypedGadget : public BaseGadget +{ + // cannot use Q_GADGET here +public: + DerivedTypedGadget() {} +}; + +class DerivedTypedGadgetDummyType {}; + +Q_DECLARE_METATYPE(DerivedTypedGadget<DerivedTypedGadgetDummyType>) + class TypeWithCustomValueType : public QObject { Q_OBJECT @@ -1657,6 +1673,62 @@ void tst_qqmlvaluetypes::gadgetInheritance() QCOMPARE(value.property("baseProperty").toInt(), 42); } +void tst_qqmlvaluetypes::gadgetTemplateInheritance() +{ + QJSEngine engine; + + QJSValue value = engine.toScriptValue(DerivedTypedGadget<DerivedTypedGadgetDummyType>()); + + QCOMPARE(value.property("baseProperty").toInt(), 0); + value.setProperty("baseProperty", 10); + QCOMPARE(value.property("baseProperty").toInt(), 10); + + QJSValue method = value.property("functionInBaseGadget"); + method.call(QJSValueList() << QJSValue(42)); + QCOMPARE(value.property("baseProperty").toInt(), 42); +} + +void tst_qqmlvaluetypes::sequences() +{ + QJSEngine engine; + { + QList<BaseGadget> gadgetList{1, 4, 7, 8, 15}; + QJSValue value = engine.toScriptValue(gadgetList); + QCOMPARE(value.property("length").toInt(), gadgetList.length()); + for (int i = 0; i < gadgetList.length(); ++i) + QCOMPARE(value.property(i).property("baseProperty").toInt(), gadgetList.at(i).baseProperty()); + } + { + std::vector<BaseGadget> container{1, 4, 7, 8, 15}; + QJSValue value = engine.toScriptValue(container); + QCOMPARE(value.property("length").toInt(), int(container.size())); + for (size_t i = 0; i < container.size(); ++i) + QCOMPARE(value.property(i).property("baseProperty").toInt(), container.at(i).baseProperty()); + } + { + QVector<QChar> qcharVector{1, 4, 42, 8, 15}; + QJSValue value = engine.toScriptValue(qcharVector); + QCOMPARE(value.property("length").toInt(), qcharVector.length()); + for (int i = 0; i < qcharVector.length(); ++i) + QCOMPARE(value.property(i).toString(), qcharVector.at(i)); + } + { + MyTypeObject a, b, c; + QSet<QObject*> objSet{&a, &b, &c}; + QJSValue value = engine.toScriptValue(objSet); + QCOMPARE(value.property("length").toInt(), objSet.size()); + for (int i = 0; i < objSet.size(); ++i) + QCOMPARE(value.property(i).property("point").property("x").toInt(), a.point().x()); + } + { + MyTypeObject a, b, c; + QSet<MyTypeObject*> container{&a, &b, &c}; + QJSValue value = engine.toScriptValue(container); + QCOMPARE(value.property("length").toInt(), container.size()); + for (int i = 0; i < container.size(); ++i) + QCOMPARE(value.property(i).property("point").property("x").toInt(), a.point().x()); + } +} struct StringLessGadget { Q_GADGET }; diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js new file mode 100644 index 0000000000..adb7269310 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js @@ -0,0 +1,11 @@ +(function(url, resultCollector) { + var x = new XMLHttpRequest; + x.open("GET", url); + x.setRequestHeader("Accept-Language","en-US"); + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + resultCollector.responseText = x.responseText + } + } + x.send() +}) diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect Binary files differnew file mode 100644 index 0000000000..6d34e1d2bb --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.expect diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml new file mode 100644 index 0000000000..ba9761201e --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_data.11.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +QtObject { + property string url + + property bool dataOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("POST", url); + x.setRequestHeader("Accept-Language","en-US"); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + dataOK = (x.responseText == "QML Rocks!\n"); + } + } + + var data = new Uint8Array([1, 2, 3, 0, 3, 2, 1]) + x.send(data.buffer); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/status.qml b/tests/auto/qml/qqmlxmlhttprequest/data/status.qml index 22c45c099d..94908f63c0 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/status.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/status.qml @@ -11,6 +11,9 @@ QtObject { property bool headersReceived: false property bool loading: false property bool done: false + property bool onloadCalled: false + property bool onerrorCalled: false + property bool onloadendCalled: false property bool resetException: false @@ -62,6 +65,16 @@ QtObject { } } + x.onload = function() { + // test also that it was called after onreadystatechanged(DONE) + onloadCalled = (done === true) && (onerrorCalled === false); + } + x.onloadend = function() { + onloadendCalled = (done === true) && (onloadCalled === true || onerrorCalled === true); + } + x.onerror = function() { + onerrorCalled = (done === true) && (onloadCalled === false); + } x.send() diff --git a/tests/auto/qml/qqmlxmlhttprequest/qqmlxmlhttprequest.pro b/tests/auto/qml/qqmlxmlhttprequest/qqmlxmlhttprequest.pro index 44b2963918..572ab1d572 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/qqmlxmlhttprequest.pro +++ b/tests/auto/qml/qqmlxmlhttprequest/qqmlxmlhttprequest.pro @@ -2,7 +2,6 @@ CONFIG += testcase TARGET = tst_qqmlxmlhttprequest macx:CONFIG -= app_bundle -INCLUDEPATH += ../../shared/ HEADERS += ../../shared/testhttpserver.h SOURCES += tst_qqmlxmlhttprequest.cpp \ diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 1ce07ecdab..6cf80ccfdb 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -108,6 +108,8 @@ private slots: void text(); void cdata(); + void noQmlContext(); + // Crashes // void outstanding_request_at_shutdown(); @@ -291,7 +293,7 @@ class TestThreadedHTTPServer : public QObject Q_OBJECT public: TestThreadedHTTPServer(const QUrl &expectUrl, const QUrl &replyUrl, const QUrl &bodyUrl) - : m_server(Q_NULLPTR) { + : m_server(nullptr) { QMutexLocker locker(&m_lock); moveToThread(&m_thread); m_thread.start(); @@ -597,6 +599,7 @@ void tst_qqmlxmlhttprequest::send_withdata_data() QTest::newRow("Incorrect content-type - out of order") << "send_data.4.expect" << "send_data.5.qml"; QTest::newRow("PUT") << "send_data.6.expect" << "send_data.6.qml"; QTest::newRow("Correct content-type - no charset") << "send_data.1.expect" << "send_data.7.qml"; + QTest::newRow("ArrayBuffer") << "send_data.11.expect" << "send_data.11.qml"; } void tst_qqmlxmlhttprequest::send_options() @@ -914,6 +917,9 @@ void tst_qqmlxmlhttprequest::status() QCOMPARE(object->property("loading").toBool(), true); QCOMPARE(object->property("done").toBool(), true); QCOMPARE(object->property("resetException").toBool(), true); + QCOMPARE(object->property("onloadCalled").toBool(), true); + QCOMPARE(object->property("onloadendCalled").toBool(), true); + QCOMPARE(object->property("onerrorCalled").toBool(), false); } void tst_qqmlxmlhttprequest::status_data() @@ -1242,6 +1248,30 @@ void tst_qqmlxmlhttprequest::cdata() QCOMPARE(object->property("status").toInt(), 200); } +void tst_qqmlxmlhttprequest::noQmlContext() +{ + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("open_network.expect"), + testFileUrl("open_network.reply"), + testFileUrl("testdocument.html"))); + QUrl url = server.urlString(QStringLiteral("/testdocument.html")); + + QQmlEngine engine; + + QFile f(testFile("noqmlcontext.js")); + QVERIFY(f.open(QIODevice::ReadOnly)); + QString script = QString::fromUtf8(f.readAll()); + QJSValue testFunction = engine.evaluate(script); + QVERIFY(testFunction.isCallable()); + + QJSValue resultCollector = engine.newObject(); + + testFunction.call(QJSValueList() << url.toString() << resultCollector); + + QTRY_COMPARE(resultCollector.property("responseText").toString(), "QML Rocks!\n"); + } + void tst_qqmlxmlhttprequest::stateChangeCallingContext() { #ifdef Q_OS_WIN diff --git a/tests/auto/qml/qquickfolderlistmodel/data/sortdir/Uppercase.txt b/tests/auto/qml/qquickfolderlistmodel/data/sortdir/Uppercase.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/qquickfolderlistmodel/data/sortdir/Uppercase.txt diff --git a/tests/auto/qml/qquickfolderlistmodel/data/sortdir/lowercase.txt b/tests/auto/qml/qquickfolderlistmodel/data/sortdir/lowercase.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/qquickfolderlistmodel/data/sortdir/lowercase.txt diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp index f19e82032a..4b2ae45bae 100644 --- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp +++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp @@ -42,12 +42,13 @@ // From qquickfolderlistmodel.h const int FileNameRole = Qt::UserRole+1; enum SortField { Unsorted, Name, Time, Size, Type }; +enum Status { Null, Ready, Loading }; class tst_qquickfolderlistmodel : public QQmlDataTest { Q_OBJECT public: - tst_qquickfolderlistmodel() : removeStart(0), removeEnd(0) {} + tst_qquickfolderlistmodel() {} public slots: void removed(const QModelIndex &, int start, int end) { @@ -58,6 +59,7 @@ public slots: private slots: void initTestCase(); void basicProperties(); + void status(); void showFiles(); void resetFiltering(); void nameFilters(); @@ -71,13 +73,14 @@ private slots: void showDotAndDotDot_data(); void sortReversed(); void introspectQrc(); - + void sortCaseSensitive_data(); + void sortCaseSensitive(); private: void checkNoErrors(const QQmlComponent& component); QQmlEngine engine; - int removeStart; - int removeEnd; + int removeStart = 0; + int removeEnd = 0; }; void tst_qquickfolderlistmodel::checkNoErrors(const QQmlComponent& component) @@ -113,7 +116,7 @@ void tst_qquickfolderlistmodel::basicProperties() checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); QCOMPARE(flm->property("nameFilters").toStringList(), QStringList() << "*.qml"); // from basic.qml QCOMPARE(flm->property("folder").toUrl(), QUrl::fromLocalFile(QDir::currentPath())); @@ -124,7 +127,7 @@ void tst_qquickfolderlistmodel::basicProperties() QSignalSpy folderChangedSpy(flm, SIGNAL(folderChanged())); flm->setProperty("folder", dataDirectoryUrl()); QVERIFY(folderChangedSpy.wait()); - QCOMPARE(flm->property("count").toInt(), 8); + QCOMPARE(flm->property("count").toInt(), 9); QCOMPARE(flm->property("folder").toUrl(), dataDirectoryUrl()); QCOMPARE(flm->property("parentFolder").toUrl(), QUrl::fromLocalFile(QDir(directory()).canonicalPath())); QCOMPARE(flm->property("sortField").toInt(), int(Name)); @@ -141,21 +144,35 @@ void tst_qquickfolderlistmodel::basicProperties() QCOMPARE(flm->property("folder").toUrl(), QUrl::fromLocalFile("")); } +void tst_qquickfolderlistmodel::status() +{ + QQmlComponent component(&engine, testFileUrl("basic.qml")); + checkNoErrors(component); + + QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); + QVERIFY(flm != nullptr); + QTRY_COMPARE(flm->property("status").toInt(), int(Ready)); + flm->setProperty("folder", QUrl::fromLocalFile("")); + QTRY_COMPARE(flm->property("status").toInt(), int(Null)); + flm->setProperty("folder", QUrl::fromLocalFile(QDir::currentPath())); + QTRY_COMPARE(flm->property("status").toInt(), int(Ready)); +} + void tst_qquickfolderlistmodel::showFiles() { QQmlComponent component(&engine, testFileUrl("basic.qml")); checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); flm->setProperty("folder", dataDirectoryUrl()); - QTRY_COMPARE(flm->property("count").toInt(), 8); // wait for refresh + QTRY_COMPARE(flm->property("count").toInt(), 9); // wait for refresh QCOMPARE(flm->property("showFiles").toBool(), true); flm->setProperty("showFiles", false); QCOMPARE(flm->property("showFiles").toBool(), false); - QTRY_COMPARE(flm->property("count").toInt(), 2); // wait for refresh + QTRY_COMPARE(flm->property("count").toInt(), 3); // wait for refresh } void tst_qquickfolderlistmodel::resetFiltering() @@ -165,7 +182,7 @@ void tst_qquickfolderlistmodel::resetFiltering() checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); flm->setProperty("folder", testFileUrl("resetfiltering")); // _q_directoryUpdated may be triggered if model was empty before, but there won't be a rowsRemoved signal @@ -187,7 +204,7 @@ void tst_qquickfolderlistmodel::nameFilters() checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); connect(flm, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(removed(QModelIndex,int,int))); @@ -219,10 +236,10 @@ void tst_qquickfolderlistmodel::refresh() checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); flm->setProperty("folder", dataDirectoryUrl()); - QTRY_COMPARE(flm->property("count").toInt(),8); // wait for refresh + QTRY_COMPARE(flm->property("count").toInt(), 9); // wait for refresh int count = flm->rowCount(); @@ -242,7 +259,7 @@ void tst_qquickfolderlistmodel::cdUp() checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); const QUrl startFolder = flm->property("folder").toUrl(); QVERIFY(startFolder.isValid()); @@ -320,13 +337,13 @@ void tst_qquickfolderlistmodel::showDotAndDotDot() checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); flm->setProperty("folder", folder); flm->setProperty("rootFolder", rootFolder); flm->setProperty("showDotAndDotDot", showDotAndDotDot); - int count = 9; + int count = 10; if (showDot) count++; if (showDotDot) count++; QTRY_COMPARE(flm->property("count").toInt(), count); // wait for refresh @@ -355,9 +372,9 @@ void tst_qquickfolderlistmodel::sortReversed() QQmlComponent component(&engine, testFileUrl("sortReversed.qml")); checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); flm->setProperty("folder", dataDirectoryUrl()); - QTRY_COMPARE(flm->property("count").toInt(), 9); // wait for refresh + QTRY_COMPARE(flm->property("count").toInt(), 10); // wait for refresh QCOMPARE(flm->data(flm->index(0),FileNameRole).toString(), QLatin1String("txtdir")); } @@ -366,11 +383,41 @@ void tst_qquickfolderlistmodel::introspectQrc() QQmlComponent component(&engine, testFileUrl("qrc.qml")); checkNoErrors(component); QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); - QVERIFY(flm != 0); + QVERIFY(flm != nullptr); QTRY_COMPARE(flm->property("count").toInt(), 1); // wait for refresh QCOMPARE(flm->data(flm->index(0),FileNameRole).toString(), QLatin1String("hello.txt")); } +void tst_qquickfolderlistmodel::sortCaseSensitive_data() +{ + QTest::addColumn<bool>("sortCaseSensitive"); + QTest::addColumn<QStringList>("expectedOrder"); + + const QString upperFile = QLatin1String("Uppercase.txt"); + const QString lowerFile = QLatin1String("lowercase.txt"); + + QTest::newRow("caseSensitive") << true << (QStringList() << upperFile << lowerFile); + QTest::newRow("caseInsensitive") << false << (QStringList() << lowerFile << upperFile); +} + +void tst_qquickfolderlistmodel::sortCaseSensitive() +{ + QFETCH(bool, sortCaseSensitive); + QFETCH(QStringList, expectedOrder); + QQmlComponent component(&engine); + component.setData("import Qt.labs.folderlistmodel 1.0\n" + "FolderListModel { }", QUrl()); + checkNoErrors(component); + + QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); + QVERIFY(flm != 0); + flm->setProperty("folder", QUrl::fromLocalFile(dataDirectoryUrl().path() + QLatin1String("/sortdir"))); + flm->setProperty("sortCaseSensitive", sortCaseSensitive); + QTRY_COMPARE(flm->property("count").toInt(), 2); // wait for refresh + for (int i = 0; i < 2; ++i) + QTRY_COMPARE(flm->data(flm->index(i),FileNameRole).toString(), expectedOrder.at(i)); +} + QTEST_MAIN(tst_qquickfolderlistmodel) #include "tst_qquickfolderlistmodel.moc" diff --git a/tests/auto/qml/qquickworkerscript/data/messagehandler.mjs b/tests/auto/qml/qquickworkerscript/data/messagehandler.mjs new file mode 100644 index 0000000000..749ff561da --- /dev/null +++ b/tests/auto/qml/qquickworkerscript/data/messagehandler.mjs @@ -0,0 +1,4 @@ + +export function messageHandler(msg) { + WorkerScript.sendMessage("Hello from the module") +} diff --git a/tests/auto/qml/qquickworkerscript/data/script_global.js b/tests/auto/qml/qquickworkerscript/data/script_global.js deleted file mode 100644 index cce4f2ceca..0000000000 --- a/tests/auto/qml/qquickworkerscript/data/script_global.js +++ /dev/null @@ -1,5 +0,0 @@ -WorkerScript.onMessage = function(msg) { - world = "World" - WorkerScript.sendMessage(msg + " " + world) -} - diff --git a/tests/auto/qml/qquickworkerscript/data/script_global2.js b/tests/auto/qml/qquickworkerscript/data/script_global2.js deleted file mode 100644 index 0867f7ee76..0000000000 --- a/tests/auto/qml/qquickworkerscript/data/script_global2.js +++ /dev/null @@ -1,6 +0,0 @@ -world = "World" - -WorkerScript.onMessage = function(msg) { - WorkerScript.sendMessage(msg + " " + world) -} - diff --git a/tests/auto/qml/qquickworkerscript/data/script_module.mjs b/tests/auto/qml/qquickworkerscript/data/script_module.mjs new file mode 100644 index 0000000000..eaa191c5a7 --- /dev/null +++ b/tests/auto/qml/qquickworkerscript/data/script_module.mjs @@ -0,0 +1,5 @@ + +import { messageHandler as handler } from "./messagehandler.mjs"; + +WorkerScript.onMessage = handler + diff --git a/tests/auto/qml/qquickworkerscript/data/worker_global.qml b/tests/auto/qml/qquickworkerscript/data/worker_global.qml deleted file mode 100644 index 546afd2f39..0000000000 --- a/tests/auto/qml/qquickworkerscript/data/worker_global.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick 2.0 - -BaseWorker { - source: "script_global.js" -} diff --git a/tests/auto/qml/qquickworkerscript/data/worker_global2.qml b/tests/auto/qml/qquickworkerscript/data/worker_global2.qml deleted file mode 100644 index 42cad3852b..0000000000 --- a/tests/auto/qml/qquickworkerscript/data/worker_global2.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick 2.0 - -BaseWorker { - source: "script_global2.js" -} diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp index 49135ca920..dfaeca67f1 100644 --- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp +++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp @@ -30,6 +30,7 @@ #include <QtCore/qtimer.h> #include <QtCore/qdir.h> #include <QtCore/qfileinfo.h> +#include <QtCore/qregularexpression.h> #include <QtQml/qjsengine.h> #include <QtQml/qqmlcomponent.h> @@ -57,7 +58,6 @@ private slots: void scriptError_onCall(); void script_function(); void script_var(); - void script_global(); void stressDispose(); private: @@ -78,24 +78,30 @@ private: void tst_QQuickWorkerScript::source() { QQmlComponent component(&m_engine, testFileUrl("worker.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QScopedPointer<QQuickWorkerScript>worker(qobject_cast<QQuickWorkerScript*>(component.create())); + QVERIFY(worker != nullptr); const QMetaObject *mo = worker->metaObject(); QVariant value(100); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), value); + QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.data()); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), value); QUrl source = testFileUrl("script_fixed_return.js"); worker->setSource(source); QCOMPARE(worker->source(), source); - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - waitForEchoMessage(worker); - QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(), qVariantFromValue(QString("Hello_World"))); + QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.data()); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello_World"))); + + source = testFileUrl("script_module.mjs"); + worker->setSource(source); + QCOMPARE(worker->source(), source); + QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value))); + waitForEchoMessage(worker.data()); + QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello from the module"))); qApp->processEvents(); - delete worker; } void tst_QQuickWorkerScript::messaging() @@ -104,7 +110,7 @@ void tst_QQuickWorkerScript::messaging() QQmlComponent component(&m_engine, testFileUrl("worker.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); waitForEchoMessage(worker); @@ -113,7 +119,18 @@ void tst_QQuickWorkerScript::messaging() QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>(); if (response.userType() == qMetaTypeId<QJSValue>()) response = response.value<QJSValue>().toVariant(); - QCOMPARE(response, value); + + if (value.type() == QMetaType::QRegExp && response.type() == QMetaType::QRegularExpression) { + // toVariant() doesn't know if we want QRegExp or QRegularExpression. It always creates + // a QRegularExpression from a JavaScript regular expression. + const QRegularExpression responseRegExp = response.toRegularExpression(); + const QRegExp valueRegExp = value.toRegExp(); + QCOMPARE(responseRegExp.pattern(), valueRegExp.pattern()); + QCOMPARE(bool(responseRegExp.patternOptions() & QRegularExpression::CaseInsensitiveOption), + bool(valueRegExp.caseSensitivity() == Qt::CaseInsensitive)); + } else { + QCOMPARE(response, value); + } qApp->processEvents(); delete worker; @@ -130,10 +147,10 @@ void tst_QQuickWorkerScript::messaging_data() QTest::newRow("string") << qVariantFromValue(QString("More cheeeese, Gromit!")); QTest::newRow("variant list") << qVariantFromValue((QVariantList() << "a" << "b" << "c")); QTest::newRow("date time") << qVariantFromValue(QDateTime::currentDateTime()); -#ifndef QT_NO_REGEXP - // Qt Script's QScriptValue -> QRegExp uses RegExp2 pattern syntax - QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, QRegExp::RegExp2)); -#endif + QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, + QRegExp::RegExp2)); + QTest::newRow("regularexpression") << qVariantFromValue(QRegularExpression( + "^\\d\\d?$", QRegularExpression::CaseInsensitiveOption)); } void tst_QQuickWorkerScript::messaging_sendQObjectList() @@ -144,7 +161,7 @@ void tst_QQuickWorkerScript::messaging_sendQObjectList() QQmlComponent component(&m_engine, testFileUrl("worker.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QVariantList objects; for (int i=0; i<3; i++) @@ -165,7 +182,7 @@ void tst_QQuickWorkerScript::messaging_sendJsObject() { QQmlComponent component(&m_engine, testFileUrl("worker.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); // Properties are in alphabetical order to enable string-based comparison after // QVariant roundtrip, since the properties will be stored in a QVariantMap. @@ -204,7 +221,7 @@ void tst_QQuickWorkerScript::script_with_pragma() QQmlComponent component(&m_engine, testFileUrl("worker_pragma.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); waitForEchoMessage(worker); @@ -220,7 +237,7 @@ void tst_QQuickWorkerScript::script_included() { QQmlComponent component(&m_engine, testFileUrl("worker_include.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QString value("Hello"); @@ -247,7 +264,7 @@ void tst_QQuickWorkerScript::scriptError_onLoad() QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QTRY_COMPARE(qquickworkerscript_lastWarning, testFileUrl("script_error_onLoad.js").toString() + QLatin1String(":3:10: SyntaxError: Expected token `,'")); @@ -261,7 +278,7 @@ void tst_QQuickWorkerScript::scriptError_onCall() { QQmlComponent component(&m_engine, testFileUrl("worker_error_onCall.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler); QVariant value; @@ -279,7 +296,7 @@ void tst_QQuickWorkerScript::script_function() { QQmlComponent component(&m_engine, testFileUrl("worker_function.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QString value("Hello"); @@ -297,7 +314,7 @@ void tst_QQuickWorkerScript::script_var() { QQmlComponent component(&m_engine, testFileUrl("worker_var.qml")); QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); + QVERIFY(worker != nullptr); QString value("Hello"); @@ -311,47 +328,6 @@ void tst_QQuickWorkerScript::script_var() delete worker; } -void tst_QQuickWorkerScript::script_global() -{ - { - QQmlComponent component(&m_engine, testFileUrl("worker_global.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); - - QString value("Hello"); - - QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler); - - QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, value))); - - QTRY_COMPARE(qquickworkerscript_lastWarning, - testFileUrl("script_global.js").toString() + QLatin1String(":2: Invalid write to global property \"world\"")); - - qInstallMessageHandler(previousMsgHandler); - - qApp->processEvents(); - delete worker; - } - - qquickworkerscript_lastWarning = QString(); - - { - QtMessageHandler previousMsgHandler = qInstallMessageHandler(qquickworkerscript_warningsHandler); - - QQmlComponent component(&m_engine, testFileUrl("worker_global2.qml")); - QQuickWorkerScript *worker = qobject_cast<QQuickWorkerScript*>(component.create()); - QVERIFY(worker != 0); - - QTRY_COMPARE(qquickworkerscript_lastWarning, - testFileUrl("script_global2.js").toString() + QLatin1String(":1: Invalid write to global property \"world\"")); - - qInstallMessageHandler(previousMsgHandler); - - qApp->processEvents(); - delete worker; - } -} - // Rapidly create and destroy worker scripts to test resources are being disposed // in the correct isolate void tst_QQuickWorkerScript::stressDispose() diff --git a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp index eab3837245..90b6feee28 100644 --- a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp +++ b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp @@ -49,7 +49,7 @@ void tst_qtqmlmodules::baseTypes() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("base.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); delete object; @@ -60,7 +60,7 @@ void tst_qtqmlmodules::modelsTypes() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("models.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); delete object; @@ -71,7 +71,7 @@ void tst_qtqmlmodules::unavailableTypes() QQmlEngine engine; QQmlComponent component(&engine, testFileUrl("unavailable.qml")); QObject *object = component.create(); - QVERIFY(object != 0); + QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); delete object; diff --git a/tests/auto/qml/qv4assembler/data/crash.qml b/tests/auto/qml/qv4assembler/data/crash.qml new file mode 100644 index 0000000000..dfdb9ceba5 --- /dev/null +++ b/tests/auto/qml/qv4assembler/data/crash.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 2.2 +import Crash 1.0 + +QtObject { + property Crash crash: Crash { + id: crash + } + + // Recursion makes the crash more reliable + // With a single frame the unwinder might guess + // the next frame by chance. + function recurse(x) { + if (x > 32) + crash.crash(); + else + recurse(x + 1); + } + + property Timer timer: Timer { + interval: 10 + running: true + onTriggered: recurse(0) + } +} + diff --git a/tests/auto/qml/qv4assembler/qv4assembler.pro b/tests/auto/qml/qv4assembler/qv4assembler.pro new file mode 100644 index 0000000000..895e241cc9 --- /dev/null +++ b/tests/auto/qml/qv4assembler/qv4assembler.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qv4assembler + +include (../../shared/util.pri) + +macos:CONFIG -= app_bundle + +TESTDATA = data/* + +SOURCES += tst_qv4assembler.cpp + +QT += qml-private testlib diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp new file mode 100644 index 0000000000..4916cb4cc0 --- /dev/null +++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 <util.h> + +#include <QtTest/QtTest> +#include <QtCore/qprocess.h> +#include <QtCore/qtemporaryfile.h> +#include <QtQml/qqml.h> +#include <QtQml/qqmlapplicationengine.h> + +#ifdef Q_OS_WIN +#include <windows.h> +#endif + +class tst_QV4Assembler : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void initTestCase() override; + void perfMapFile(); + void functionTable(); +}; + +void tst_QV4Assembler::initTestCase() +{ + qputenv("QV4_JIT_CALL_THRESHOLD", "0"); + QQmlDataTest::initTestCase(); +} + +void tst_QV4Assembler::perfMapFile() +{ +#if !defined(Q_OS_LINUX) + QSKIP("perf map files are only generated on linux"); +#else + const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs"; + QProcess process; + + QTemporaryFile infile; + QVERIFY(infile.open()); + infile.write("'use strict'; function foo() { return 42 }; foo();"); + infile.close(); + + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + environment.insert("QV4_PROFILE_WRITE_PERF_MAP", "1"); + environment.insert("QV4_JIT_CALL_THRESHOLD", "0"); + + process.setProcessEnvironment(environment); + process.start(qmljs, QStringList({infile.fileName()})); + QVERIFY(process.waitForStarted()); + const qint64 pid = process.processId(); + QVERIFY(pid != 0); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + + QFile file(QString::fromLatin1("/tmp/perf-%1.map").arg(pid)); + QVERIFY(file.exists()); + QVERIFY(file.open(QIODevice::ReadOnly)); + QList<QByteArray> functions; + while (!file.atEnd()) { + const QByteArray contents = file.readLine(); + QVERIFY(contents.endsWith('\n')); + QList<QByteArray> fields = contents.split(' '); + QCOMPARE(fields.length(), 3); + bool ok = false; + const qulonglong address = fields[0].toULongLong(&ok, 16); + QVERIFY(ok); + QVERIFY(address > 0); + const ulong size = fields[1].toULong(&ok, 16); + QVERIFY(ok); + QVERIFY(size > 0); + functions.append(fields[2]); + } + QVERIFY(functions.contains("foo\n")); +#endif +} + +#ifdef Q_OS_WIN +class Crash : public QObject +{ + Q_OBJECT +public: + explicit Crash(QObject *parent = nullptr) : QObject(parent) { } + Q_INVOKABLE static void crash(); +}; + +static bool crashHandlerHit = false; + +static LONG WINAPI crashHandler(EXCEPTION_POINTERS*) +{ + crashHandlerHit = true; + return EXCEPTION_CONTINUE_EXECUTION; +} + +void Crash::crash() +{ + SetUnhandledExceptionFilter(crashHandler); + RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr); +} +#endif + +void tst_QV4Assembler::functionTable() +{ +#ifndef Q_OS_WIN + QSKIP("Function tables only exist on windows."); +#else + QQmlApplicationEngine engine; + qmlRegisterType<Crash>("Crash", 1, 0, "Crash"); + engine.load(testFileUrl("crash.qml")); + QTRY_VERIFY(crashHandlerHit); +#endif +} + +QTEST_MAIN(tst_QV4Assembler) + +#include "tst_qv4assembler.moc" + diff --git a/tests/auto/qml/qv4identifiertable/qv4identifiertable.pro b/tests/auto/qml/qv4identifiertable/qv4identifiertable.pro new file mode 100644 index 0000000000..64dc822367 --- /dev/null +++ b/tests/auto/qml/qv4identifiertable/qv4identifiertable.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qv4identifiertable +macos:CONFIG -= app_bundle + +SOURCES += tst_qv4identifiertable.cpp + +QT += qml qml-private testlib + diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp new file mode 100644 index 0000000000..095943cdc7 --- /dev/null +++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2016 basysKom GmbH. +** 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 <qtest.h> +#include <QQmlEngine> +#include <private/qv4identifiertable_p.h> + +class tst_qv4identifiertable : public QObject +{ + Q_OBJECT + +private slots: + void sweepFirstEntryInBucket(); + void sweepCenterEntryInBucket(); + void sweepLastEntryInBucket(); + void sweepFirstEntryInSameBucketWithDifferingHash(); + void dontSweepAcrossBucketBoundaries(); + void sweepAcrossBucketBoundariesIfFirstBucketFull(); + void sweepBucketGap(); +}; + +void tst_qv4identifiertable::sweepFirstEntryInBucket() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/1); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + auto entry3 = engine.newString(QStringLiteral("three")); + + entry1->createHashValue(); + entry2->createHashValue(); + entry3->createHashValue(); + + // All strings go into the same bucket + entry1->stringHash = 0; + entry2->stringHash = 0; + entry3->stringHash = 0; + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + table.asPropertyKey(entry3); + + QCOMPARE(table.size, 3); + QCOMPARE(table.alloc, 5); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], entry3); + QCOMPARE(table.entriesByHash[3], nullptr); + + // first entry not marked + entry2->setMarkBit(); + entry3->setMarkBit(); + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], entry2); + QCOMPARE(table.entriesByHash[1], entry3); + QCOMPARE(table.entriesByHash[2], nullptr); + QCOMPARE(table.entriesByHash[3], nullptr); +} + +void tst_qv4identifiertable::sweepCenterEntryInBucket() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/1); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + auto entry3 = engine.newString(QStringLiteral("three")); + + entry1->createHashValue(); + entry2->createHashValue(); + entry3->createHashValue(); + + // All strings go into the same bucket + entry1->stringHash = 0; + entry2->stringHash = 0; + entry3->stringHash = 0; + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + table.asPropertyKey(entry3); + + QCOMPARE(table.size, 3); + QCOMPARE(table.alloc, 5); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], entry3); + QCOMPARE(table.entriesByHash[3], nullptr); + + entry1->setMarkBit(); + // second entry not marked + entry3->setMarkBit(); + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry3); + QCOMPARE(table.entriesByHash[2], nullptr); + QCOMPARE(table.entriesByHash[3], nullptr); +} + +void tst_qv4identifiertable::sweepLastEntryInBucket() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/1); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + auto entry3 = engine.newString(QStringLiteral("three")); + + entry1->createHashValue(); + entry2->createHashValue(); + entry3->createHashValue(); + + // All strings go into the same bucket + entry1->stringHash = 0; + entry2->stringHash = 0; + entry3->stringHash = 0; + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + table.asPropertyKey(entry3); + + QCOMPARE(table.size, 3); + QCOMPARE(table.alloc, 5); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], entry3); + QCOMPARE(table.entriesByHash[3], nullptr); + + entry1->setMarkBit(); + entry2->setMarkBit(); + // third entry not marked + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], nullptr); + QCOMPARE(table.entriesByHash[3], nullptr); +} + +void tst_qv4identifiertable::sweepFirstEntryInSameBucketWithDifferingHash() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/1); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + + entry1->createHashValue(); + entry2->createHashValue(); + + // First and second entry have differing hash but end up in the + // same bucket after modulo alloc. + entry1->stringHash = 0; + entry2->stringHash = 5; + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + + QCOMPARE(table.size, 2); + QCOMPARE(table.alloc, 5); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], nullptr); + + // first entry not marked + entry2->setMarkBit(); + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], entry2); + QCOMPARE(table.entriesByHash[1], nullptr); + QCOMPARE(table.entriesByHash[2], nullptr); +} + +void tst_qv4identifiertable::dontSweepAcrossBucketBoundaries() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/1); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + auto entry3 = engine.newString(QStringLiteral("three")); + + entry1->createHashValue(); + entry2->createHashValue(); + entry3->createHashValue(); + + // Different buckets for both entries. + entry1->stringHash = 0; + entry2->stringHash = 1; + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + + QCOMPARE(table.size, 2); + QCOMPARE(table.alloc, 5); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], nullptr); + + // first entry not marked + entry2->setMarkBit(); + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], nullptr); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], nullptr); +} + +void tst_qv4identifiertable::sweepAcrossBucketBoundariesIfFirstBucketFull() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/3); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + auto entry3 = engine.newString(QStringLiteral("three")); + auto entry4 = engine.newString(QStringLiteral("four")); + + entry1->createHashValue(); + entry2->createHashValue(); + entry3->createHashValue(); + entry4->createHashValue(); + + // First, third and fourth entry have the same bucket (after modulo) and + // would typically end up in order [entry1, entry3, entry4, entry2]. However + // since null termination isn't guaranteed, an insertion order of + // entry1, entry2, entry3 and entry4 results in a + // table [entry1, entry2, entry3, entry4]. + entry1->stringHash = 0; + entry2->stringHash = 1; + entry3->stringHash = 11; + entry4->stringHash = 11; + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + table.asPropertyKey(entry3); + table.asPropertyKey(entry4); + + QCOMPARE(table.size, 4); + QCOMPARE(table.alloc, 11); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], entry3); + QCOMPARE(table.entriesByHash[3], entry4); + QCOMPARE(table.entriesByHash[4], nullptr); + + // first entry not marked + entry2->setMarkBit(); + entry3->setMarkBit(); + entry4->setMarkBit(); + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], entry3); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], entry4); + QCOMPARE(table.entriesByHash[3], nullptr); +} + +void tst_qv4identifiertable::sweepBucketGap() +{ + QV4::ExecutionEngine engine; + QV4::IdentifierTable table(&engine, /*numBits*/3); + + auto entry1 = engine.newString(QStringLiteral("one")); + auto entry2 = engine.newString(QStringLiteral("two")); + auto entry3 = engine.newString(QStringLiteral("three")); + auto entry4 = engine.newString(QStringLiteral("four")); + + entry1->createHashValue(); + entry2->createHashValue(); + entry3->createHashValue(); + entry4->createHashValue(); + + // We have two buckets where the second entry in the first bucket + // flows into the second bucket. So insertion into the second bucket + // will shift by one and create + // [entry1][entry2 (would map to first but overflows into second), entry3, entry4] + // Garbage collecting the first entry should not only move the second entry + // into its own first bucket (where there is space now), it is also critical to + // not leave a gap but move the third and fourth entries to the beginning of + // their bucket: + // [entry2][entry3, entry4] + entry1->stringHash = 0; // % 11 -> 0 + entry2->stringHash = 11; // % 11 -> 0, but ends up at idx 1 because 0 taken + entry3->stringHash = 12; // % 11 -> 1, but ends up at idx 2 because 1 taken + entry4->stringHash = 12; // % 11 -> 1, but ends up at idx 3 because 1+2 taken + + // trigger insertion + table.asPropertyKey(entry1); + table.asPropertyKey(entry2); + table.asPropertyKey(entry3); + table.asPropertyKey(entry4); + + QCOMPARE(table.size, 4); + QCOMPARE(table.alloc, 11); + + QCOMPARE(table.entriesByHash[0], entry1); + QCOMPARE(table.entriesByHash[1], entry2); + QCOMPARE(table.entriesByHash[2], entry3); + QCOMPARE(table.entriesByHash[3], entry4); + QCOMPARE(table.entriesByHash[4], nullptr); + + // first entry not marked + entry2->setMarkBit(); + entry3->setMarkBit(); + entry4->setMarkBit(); + + table.sweep(); + + QCOMPARE(table.entriesByHash[0], entry2); + QCOMPARE(table.entriesByHash[1], entry3); + QCOMPARE(table.entriesByHash[2], entry4); + QCOMPARE(table.entriesByHash[3], nullptr); +} + +QTEST_MAIN(tst_qv4identifiertable) + +#include "tst_qv4identifiertable.moc" diff --git a/tests/auto/qml/qv4mm/data/createdestroy.qml b/tests/auto/qml/qv4mm/data/createdestroy.qml new file mode 100644 index 0000000000..5a4dd0f520 --- /dev/null +++ b/tests/auto/qml/qv4mm/data/createdestroy.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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 2.2 + +QtObject { + property int creations: 0 + property int destructions: 0 + property int iterations: 0 + + property Component itemComponent: Component { + QtObject { + property var parent; + Component.onCompleted: ++parent.creations + Component.onDestruction: ++parent.destructions + } + } + + property QtObject item: null; + property Timer timer: Timer { + running: true + repeat: true + interval: 1 + onTriggered: { + if (parent.iterations === 100) { + item = null; + running = false; + } else { + ++parent.iterations; + item = itemComponent.createObject(null, { parent : parent }); + } + gc(); + } + } +} diff --git a/tests/auto/qml/qv4mm/qv4mm.pro b/tests/auto/qml/qv4mm/qv4mm.pro index d9b749af4a..375c5cea75 100644 --- a/tests/auto/qml/qv4mm/qv4mm.pro +++ b/tests/auto/qml/qv4mm/qv4mm.pro @@ -1,6 +1,10 @@ CONFIG += testcase TARGET = tst_qv4mm -osx:CONFIG -= app_bundle +include (../../shared/util.pri) + +macos:CONFIG -= app_bundle + +TESTDATA = data/* SOURCES += tst_qv4mm.cpp diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index d4ba363d00..578a47d5fa 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -28,29 +28,84 @@ #include <qtest.h> #include <QQmlEngine> +#include <QLoggingCategory> +#include <QQmlComponent> + #include <private/qv4mm_p.h> +#include <private/qv4qobjectwrapper_p.h> + +#include "../../shared/util.h" + +#include <memory> -class tst_qv4mm : public QObject +class tst_qv4mm : public QQmlDataTest { Q_OBJECT private slots: void gcStats(); - void tweaks(); + void multiWrappedQObjects(); + void accessParentOnDestruction(); }; void tst_qv4mm::gcStats() { - qputenv(QV4_MM_STATS, "1"); + QLoggingCategory::setFilterRules("qt.qml.gc.*=true"); QQmlEngine engine; engine.collectGarbage(); } -void tst_qv4mm::tweaks() +void tst_qv4mm::multiWrappedQObjects() +{ + QV4::ExecutionEngine engine1; + QV4::ExecutionEngine engine2; + { + QObject object; + for (int i = 0; i < 10; ++i) + QV4::QObjectWrapper::wrap(i % 2 ? &engine1 : &engine2, &object); + + QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); + QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); + { + QV4::WeakValue value; + value.set(&engine1, QV4::QObjectWrapper::wrap(&engine1, &object)); + } + + QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1); + QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); + + // Moves the additional WeakValue from m_multiplyWrappedQObjects to + // m_pendingFreedObjectWrapperValue. It's still alive after all. + engine1.memoryManager->runGC(); + QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 2); + + // engine2 doesn't own the object as engine1 was the first to wrap it above. + // Therefore, no effect here. + engine2.memoryManager->runGC(); + QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); + } + + // Clears m_pendingFreedObjectWrapperValue. Now it's really dead. + engine1.memoryManager->runGC(); + QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); + + engine2.memoryManager->runGC(); + QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); +} + +void tst_qv4mm::accessParentOnDestruction() { - qputenv(QV4_MM_MAXBLOCK_SHIFT, "5"); - qputenv(QV4_MM_MAX_CHUNK_SIZE, "65536"); + QLoggingCategory::setFilterRules("qt.qml.gc.*=false"); QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("createdestroy.qml")); + std::unique_ptr<QObject> obj(component.create()); + QVERIFY(obj); + QPointer<QObject> timer = qvariant_cast<QObject *>(obj->property("timer")); + QVERIFY(timer); + QTRY_VERIFY(!timer->property("running").toBool()); + QCOMPARE(obj->property("iterations").toInt(), 100); + QCOMPARE(obj->property("creations").toInt(), 100); + QCOMPARE(obj->property("destructions").toInt(), 100); } QTEST_MAIN(tst_qv4mm) diff --git a/tests/auto/qml/qv4regexp/qv4regexp.pro b/tests/auto/qml/qv4regexp/qv4regexp.pro new file mode 100644 index 0000000000..f4ab6941f5 --- /dev/null +++ b/tests/auto/qml/qv4regexp/qv4regexp.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qv4regexp +macos:CONFIG -= app_bundle + +SOURCES += tst_qv4regexp.cpp + +QT += qml qml-private testlib + diff --git a/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp new file mode 100644 index 0000000000..cbf9c3e47d --- /dev/null +++ b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** 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 <qtest.h> +#include <QtQml/qjsengine.h> + +class tst_qv4regexp : public QObject +{ + Q_OBJECT + +private slots: + void catchJitFail(); +}; + +void tst_qv4regexp::catchJitFail() +{ + QJSEngine engine; + QJSValue result = engine.evaluate(QLatin1String( + "var prevString = \" ok\";" + "var r = /^(\\s*)(([\\)\\]}]?\\s*)*([\\)\\]]\\s*))?;/.exec(prevString);" + "r === null;"), QLatin1String("regexptest.js")); + QVERIFY(result.isBool()); + QVERIFY(result.toBool()); +} + +QTEST_MAIN(tst_qv4regexp) + +#include "tst_qv4regexp.moc" diff --git a/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro b/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro new file mode 100644 index 0000000000..c86365d5ea --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/qwidgetsinqml.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +CONFIG += parallel_test +TARGET = tst_qwidgetsinqml +macos:CONFIG -= app_bundle +QT += qml widgets testlib gui-private +SOURCES += tst_qwidgetsinqml.cpp +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp new file mode 100644 index 0000000000..c7bbb55474 --- /dev/null +++ b/tests/auto/qml/qwidgetsinqml/tst_qwidgetsinqml.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $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 <QQmlEngine> +#include <QtQml> +#include <QWidget> + +class tst_QWidgetsInQml : public QObject +{ + Q_OBJECT +public: + tst_QWidgetsInQml(); + +private slots: + void instantiateWidget(); + void instantiateWidgetWithoutParentWidget(); + void widgetAsDefaultPropertyCollected(); + void widgetAsDefaultPropertyKept(); + void widgetAsDefaultPropertyKeptDuringCreation(); +}; + +static void gc(QQmlEngine &engine) +{ + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); +} + +// Like QtObject, but with default property +class QObjectContainer : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "data"); + Q_PROPERTY(QQmlListProperty<QObject> data READ data DESIGNABLE false); +public: + QObjectContainer() + : widgetParent(0) + , gcOnAppend(false) + {} + + QQmlListProperty<QObject> data() { + return QQmlListProperty<QObject>(this, 0, children_append, children_count, children_at, children_clear); + } + + static void children_append(QQmlListProperty<QObject> *prop, QObject *o) + { + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + that->dataChildren.append(o); + QObject::connect(o, SIGNAL(destroyed(QObject*)), prop->object, SLOT(childDestroyed(QObject*))); + QWidget *widget = qobject_cast<QWidget*>(o); + if (widget && that->widgetParent) + widget->setParent(that->widgetParent); + + if (that->gcOnAppend) { + QQmlEngine *engine = qmlEngine(that); + gc(*engine); + } + } + + static int children_count(QQmlListProperty<QObject> *prop) + { + return static_cast<QObjectContainer*>(prop->object)->dataChildren.count(); + } + + static QObject *children_at(QQmlListProperty<QObject> *prop, int index) + { + return static_cast<QObjectContainer*>(prop->object)->dataChildren.at(index); + } + + static void children_clear(QQmlListProperty<QObject> *prop) + { + QObjectContainer *that = static_cast<QObjectContainer*>(prop->object); + foreach (QObject *c, that->dataChildren) + QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*))); + that->dataChildren.clear(); + } + + QList<QObject*> dataChildren; + QWidget *widgetParent; + bool gcOnAppend; + +protected slots: + void childDestroyed(QObject *child) { + dataChildren.removeAll(child); + } +}; + +class QWidgetContainer : public QObjectContainer +{ + Q_OBJECT +public: + QWidgetContainer() + { + widgetParent = new QWidget; + QQmlEngine::setObjectOwnership(widgetParent, QQmlEngine::CppOwnership); + } + virtual ~QWidgetContainer() + { + delete widgetParent; + widgetParent = 0; + } +}; + +class QObjectContainerWithGCOnAppend : public QObjectContainer +{ + Q_OBJECT +public: + QObjectContainerWithGCOnAppend() + { + gcOnAppend = true; + } +}; + +tst_QWidgetsInQml::tst_QWidgetsInQml() +{ + qmlRegisterType<QWidget>("Qt.Widgets", 1, 0, "QWidget"); + qmlRegisterType<QObjectContainer>("Qt.Widgets", 1, 0, "QObjectContainer"); + qmlRegisterType<QWidgetContainer>("Qt.Widgets", 1, 0, "QWidgetContainer"); + qmlRegisterType<QObjectContainerWithGCOnAppend>("Qt.Widgets", 1, 0, "QObjectContainerWithGCOnAppend"); +} + +void tst_QWidgetsInQml::instantiateWidget() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\nQWidget { property QWidget child: QWidget { objectName: 'child' } }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + QWidget *rootWidget = qobject_cast<QWidget*>(object.data()); + QVERIFY(rootWidget != 0); + QCOMPARE(rootWidget->children().count(), 1); + QWidget *firstChildWidget = qobject_cast<QWidget*>(rootWidget->children().first()); + QVERIFY(firstChildWidget != 0); + + QWidget *widgetProperty = qvariant_cast<QWidget*>(object->property("child")); + QVERIFY(widgetProperty != 0); + QCOMPARE(firstChildWidget, widgetProperty); + QCOMPARE(firstChildWidget->objectName(), QString("child")); +} + +void tst_QWidgetsInQml::instantiateWidgetWithoutParentWidget() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QtObject { property QtObject child: QWidget { objectName: 'child' } }", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QPointer<QWidget> widgetProperty = qvariant_cast<QWidget*>(object->property("child")); + QVERIFY(!widgetProperty.isNull()); + QCOMPARE(widgetProperty->objectName(), QString("child")); + + QVERIFY(!widgetProperty->parent()); + gc(engine); + // Don't collect, the property reference should keep it alive + QVERIFY(!widgetProperty.isNull()); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyCollected() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QObjectContainer {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " }\n" + " property var widgetHolder;\n" + " Component.onCompleted: {\n" + " widgetHolder = parentLessChild;\n" + " }\n" + "}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast<QObjectContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QJSValue holder = qvariant_cast<QJSValue>(object->property("widgetHolder")); + QVERIFY(!holder.isNull()); + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); + + holder = QJSValue(); + object->setProperty("widgetHolder", QVariant::fromValue(holder)); + + gc(engine); + // The QWidget is without a parent and nobody is referencing it anymore (the children + // list in QObjectContainer is weak!), so it should get collected. + QCOMPARE(container->dataChildren.count(), 0); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyKept() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QWidgetContainer {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " }\n" + " property var widgetHolder;\n" + " Component.onCompleted: {\n" + " widgetHolder = parentLessChild;\n" + " }\n" + "}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QWidgetContainer *container = qobject_cast<QWidgetContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + QJSValue holder = qvariant_cast<QJSValue>(object->property("widgetHolder")); + QVERIFY(!holder.isNull()); + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); + + holder = QJSValue(); + object->setProperty("widgetHolder", QVariant::fromValue(holder)); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 1); +} + +void tst_QWidgetsInQml::widgetAsDefaultPropertyKeptDuringCreation() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import Qt.Widgets 1.0;\n" + "import QtQml 2.0;\n" + "QObjectContainerWithGCOnAppend {\n" + " QWidget {\n" + " id: parentLessChild;\n" + " objectName: 'child'\n" + " property var blah;\n" // Ensures that we have a JS wrapper + " }\n" + "}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QObjectContainer *container = qobject_cast<QObjectContainer*>(object.data()); + QCOMPARE(container->dataChildren.count(), 1); + + gc(engine); + QCOMPARE(container->dataChildren.count(), 0); + +} + +QTEST_MAIN(tst_QWidgetsInQml) + +#include "tst_qwidgetsinqml.moc" diff --git a/tests/auto/qml/v4misc/tst_v4misc.cpp b/tests/auto/qml/v4misc/tst_v4misc.cpp index 55a1f65608..2412ca7f92 100644 --- a/tests/auto/qml/v4misc/tst_v4misc.cpp +++ b/tests/auto/qml/v4misc/tst_v4misc.cpp @@ -26,207 +26,177 @@ ** ****************************************************************************/ -#include <qhashfunctions.h> #include <qtest.h> - -#define V4_AUTOTEST -#include <private/qv4ssa_p.h> +#include <private/qv4instr_moth_p.h> +#include <private/qv4script_p.h> class tst_v4misc: public QObject { Q_OBJECT private slots: - void initTestCase(); + void tdzOptimizations_data(); + void tdzOptimizations(); + + void parserMisc_data(); + void parserMisc(); - void rangeSplitting_1(); - void rangeSplitting_2(); - void rangeSplitting_3(); + void subClassing_data(); + void subClassing(); - void moveMapping_1(); - void moveMapping_2(); + void nestingDepth(); }; -using namespace QT_PREPEND_NAMESPACE(QV4::IR); +void tst_v4misc::tdzOptimizations_data() +{ + QTest::addColumn<QString>("scriptToCompile"); + + QTest::newRow("access-after-let") << QString("let x; x = 10;"); + QTest::newRow("access-after-const") << QString("const x = 10; print(x);"); + QTest::newRow("access-after-let") << QString("for (let x of y) print(x);"); +} -void tst_v4misc::initTestCase() +void tst_v4misc::tdzOptimizations() { - qSetGlobalQHashSeed(0); - QCOMPARE(qGlobalQHashSeed(), 0); + QFETCH(QString, scriptToCompile); + + QV4::ExecutionEngine v4; + QV4::Script script(&v4, nullptr, /*parse as binding*/false, scriptToCompile); + script.parse(); + QVERIFY(!v4.hasException); + + const auto function = script.compilationUnit->unitData()->functionAt(0); + const auto *code = function->code(); + const auto len = function->codeSize; + const char *end = code + len; + + const auto decodeInstruction = [&code]() { + QV4::Moth::Instr::Type type = QV4::Moth::Instr::Type(static_cast<uchar>(*code)); + dispatch: + switch (type) { + case QV4::Moth::Instr::Type::Nop: + ++code; + type = QV4::Moth::Instr::Type(static_cast<uchar>(*code)); + goto dispatch; + case QV4::Moth::Instr::Type::Nop_Wide: /* wide prefix */ + ++code; + type = QV4::Moth::Instr::Type(0x100 | static_cast<uchar>(*code)); + goto dispatch; + +#define CASE_AND_GOTO_INSTRUCTION(name, nargs, ...) \ + case QV4::Moth::Instr::Type::name: \ + MOTH_ADJUST_CODE(qint8, nargs); \ + break; + +#define CASE_AND_GOTO_WIDE_INSTRUCTION(name, nargs, ...) \ + case QV4::Moth::Instr::Type::name##_Wide: \ + MOTH_ADJUST_CODE(int, nargs); \ + type = QV4::Moth::Instr::Type::name; \ + break; + +#define MOTH_DECODE_WITHOUT_ARGS(instr) \ + INSTR_##instr(CASE_AND_GOTO) \ + INSTR_##instr(CASE_AND_GOTO_WIDE) + + FOR_EACH_MOTH_INSTR(MOTH_DECODE_WITHOUT_ARGS) + } + return type; + }; + + while (code < end) { + QV4::Moth::Instr::Type type = decodeInstruction(); + QVERIFY(type != QV4::Moth::Instr::Type::DeadTemporalZoneCheck); + } + } -// split between two ranges -void tst_v4misc::rangeSplitting_1() +void tst_v4misc::parserMisc_data() { - LifeTimeInterval interval; - interval.addRange(59, 59); - interval.addRange(61, 62); - interval.addRange(64, 64); - interval.addRange(69, 71); - interval.validate(); - QCOMPARE(interval.end(), 71); - - LifeTimeInterval newInterval = interval.split(66, 70); - interval.validate(); - newInterval.validate(); - QVERIFY(newInterval.isSplitFromInterval()); - - QCOMPARE(interval.ranges().size(), 3); - QCOMPARE(interval.ranges()[0].start, 59); - QCOMPARE(interval.ranges()[0].end, 59); - QCOMPARE(interval.ranges()[1].start, 61); - QCOMPARE(interval.ranges()[1].end, 62); - QCOMPARE(interval.ranges()[2].start, 64); - QCOMPARE(interval.ranges()[2].end, 64); - QCOMPARE(interval.end(), 70); - - QCOMPARE(newInterval.ranges().size(), 1); - QCOMPARE(newInterval.ranges()[0].start, 70); - QCOMPARE(newInterval.ranges()[0].end, 71); - QCOMPARE(newInterval.end(), 71); + QTest::addColumn<QString>("error"); + + QTest::newRow("8[++i][+++i]") << QString("ReferenceError: Prefix ++ operator applied to value that is not a reference."); + QTest::newRow("`a${1++}`") << QString("ReferenceError: Invalid left-hand side expression in postfix operation"); + QTest::newRow("for (var f in ++!binaryMathg) ;") << QString("ReferenceError: Prefix ++ operator applied to value that is not a reference."); + QTest::newRow("for (va() in obj) {}") << QString("ReferenceError: Invalid left-hand side expression for 'in' expression"); + QTest::newRow("[1]=7[A=8=9]") << QString("ReferenceError: left-hand side of assignment operator is not an lvalue"); + QTest::newRow("var asmvalsLen = asmvals{{{{{ngth}}}}};") << QString("SyntaxError: Expected token `;'"); + QTest::newRow("T||9[---L6i]") << QString("ReferenceError: Prefix ++ operator applied to value that is not a reference."); + QTest::newRow("a?b:[---Hi]") << QString("ReferenceError: Prefix ++ operator applied to value that is not a reference."); + QTest::newRow("[``]=1") << QString("ReferenceError: Binding target is not a reference."); } -// split in the middle of a range -void tst_v4misc::rangeSplitting_2() +void tst_v4misc::parserMisc() { - LifeTimeInterval interval; - interval.addRange(59, 59); - interval.addRange(61, 64); - interval.addRange(69, 71); - interval.validate(); - QCOMPARE(interval.end(), 71); - - LifeTimeInterval newInterval = interval.split(62, 64); - interval.validate(); - newInterval.validate(); - QVERIFY(newInterval.isSplitFromInterval()); - - QCOMPARE(interval.ranges().size(), 2); - QCOMPARE(interval.ranges()[0].start, 59); - QCOMPARE(interval.ranges()[0].end, 59); - QCOMPARE(interval.ranges()[1].start, 61); - QCOMPARE(interval.ranges()[1].end, 62); - QCOMPARE(interval.end(), 64); - - QCOMPARE(newInterval.ranges().size(), 2); - QCOMPARE(newInterval.ranges()[0].start, 64); - QCOMPARE(newInterval.ranges()[0].end, 64); - QCOMPARE(newInterval.ranges()[1].start, 69); - QCOMPARE(newInterval.ranges()[1].end, 71); - QCOMPARE(newInterval.end(), 71); + QFETCH(QString, error); + + QJSEngine engine; + QJSValue result = engine.evaluate(QString::fromUtf8(QTest::currentDataTag())); + QVERIFY(result.isError()); + QCOMPARE(result.toString(), error); } -// split in the middle of a range, and let it never go back to active again -void tst_v4misc::rangeSplitting_3() +void tst_v4misc::subClassing_data() { - LifeTimeInterval interval; - interval.addRange(59, 59); - interval.addRange(61, 64); - interval.addRange(69, 71); - interval.validate(); - QCOMPARE(interval.end(), 71); - - LifeTimeInterval newInterval = interval.split(64, LifeTimeInterval::InvalidPosition); - interval.validate(); - newInterval.validate(); - QVERIFY(!newInterval.isValid()); - - QCOMPARE(interval.ranges().size(), 2); - QCOMPARE(interval.ranges()[0].start, 59); - QCOMPARE(interval.ranges()[0].end, 59); - QCOMPARE(interval.ranges()[1].start, 61); - QCOMPARE(interval.ranges()[1].end, 64); - QCOMPARE(interval.end(), 71); + QTest::addColumn<QString>("script"); + + QString code( + "class Foo extends %1 {" + " constructor() { super(); this.reset(); }" + " reset() { }" + "}" + "new Foo();"); + + + QTest::newRow("Array") << code.arg("Array"); + QTest::newRow("Boolean") << code.arg("Boolean"); + QTest::newRow("Date") << code.arg("Date"); + QTest::newRow("Function") << code.arg("Function"); + QTest::newRow("Number") << code.arg("Number"); + QTest::newRow("Map") << code.arg("Map"); + QTest::newRow("Promise") << QString( + "class Foo extends Promise {" + " constructor() { super(Function()); this.reset(); }" + " reset() { }" + "}" + "new Foo();"); + QTest::newRow("RegExp") << code.arg("RegExp"); + QTest::newRow("Set") << code.arg("Set"); + QTest::newRow("String") << code.arg("String"); + QTest::newRow("WeakMap") << code.arg("WeakMap"); + QTest::newRow("WeakSet") << code.arg("WeakSet"); } -void tst_v4misc::moveMapping_1() +void tst_v4misc::subClassing() { - Temp fp2(DoubleType, Temp::PhysicalRegister, 2); - Temp fp3(DoubleType, Temp::PhysicalRegister, 3); - Temp fp4(DoubleType, Temp::PhysicalRegister, 4); - Temp fp5(DoubleType, Temp::PhysicalRegister, 5); - - MoveMapping mapping; - mapping.add(&fp2, &fp3); - mapping.add(&fp2, &fp4); - mapping.add(&fp2, &fp5); - mapping.add(&fp3, &fp2); - - mapping.order(); -// mapping.dump(); - - QCOMPARE(mapping._moves.size(), 3); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp4, false))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp5, false))); - QVERIFY(mapping._moves.last() == MoveMapping::Move(&fp2, &fp3, true) || - mapping._moves.last() == MoveMapping::Move(&fp3, &fp2, true)); + QFETCH(QString, script); + + QJSEngine engine; + QJSValue result = engine.evaluate(script); + QVERIFY(!result.isError()); } -void tst_v4misc::moveMapping_2() +void tst_v4misc::nestingDepth() { - Temp fp1(DoubleType, Temp::PhysicalRegister, 1); - Temp fp2(DoubleType, Temp::PhysicalRegister, 2); - Temp fp3(DoubleType, Temp::PhysicalRegister, 3); - Temp fp4(DoubleType, Temp::PhysicalRegister, 4); - Temp fp5(DoubleType, Temp::PhysicalRegister, 5); - Temp fp6(DoubleType, Temp::PhysicalRegister, 6); - Temp fp7(DoubleType, Temp::PhysicalRegister, 7); - Temp fp8(DoubleType, Temp::PhysicalRegister, 8); - Temp fp9(DoubleType, Temp::PhysicalRegister, 9); - Temp fp10(DoubleType, Temp::PhysicalRegister, 10); - Temp fp11(DoubleType, Temp::PhysicalRegister, 11); - Temp fp12(DoubleType, Temp::PhysicalRegister, 12); - Temp fp13(DoubleType, Temp::PhysicalRegister, 13); - - MoveMapping mapping; - mapping.add(&fp2, &fp1); - mapping.add(&fp2, &fp3); - mapping.add(&fp3, &fp2); - mapping.add(&fp3, &fp4); - - mapping.add(&fp9, &fp8); - mapping.add(&fp8, &fp7); - mapping.add(&fp7, &fp6); - mapping.add(&fp7, &fp5); - - mapping.add(&fp10, &fp11); - mapping.add(&fp11, &fp12); - mapping.add(&fp12, &fp13); - mapping.add(&fp13, &fp10); - - mapping.order(); -// mapping.dump(); - - QCOMPARE(mapping._moves.size(), 10); - - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp1, false))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp3, &fp4, false))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp7, &fp6, false))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp7, &fp5, false))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp8, &fp7, false))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp9, &fp8, false))); - - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp3, true)) || - mapping._moves.contains(MoveMapping::Move(&fp3, &fp2, true))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp10, &fp13, true)) || - mapping._moves.contains(MoveMapping::Move(&fp13, &fp10, true))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp12, &fp13, true)) || - mapping._moves.contains(MoveMapping::Move(&fp13, &fp12, true))); - QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp12, &fp11, true)) || - mapping._moves.contains(MoveMapping::Move(&fp11, &fp12, true))); - - QVERIFY(!mapping._moves.at(0).needsSwap); - QVERIFY(!mapping._moves.at(1).needsSwap); - QVERIFY(!mapping._moves.at(2).needsSwap); - QVERIFY(!mapping._moves.at(3).needsSwap); - QVERIFY(!mapping._moves.at(4).needsSwap); - QVERIFY(!mapping._moves.at(5).needsSwap); - QVERIFY(mapping._moves.at(6).needsSwap); - QVERIFY(mapping._moves.at(7).needsSwap); - QVERIFY(mapping._moves.at(8).needsSwap); - QVERIFY(mapping._moves.at(9).needsSwap); + { // left recursive + QString s(40000, '`'); + + QJSEngine engine; + QJSValue result = engine.evaluate(s); + QVERIFY(result.isError()); + QCOMPARE(result.toString(), "SyntaxError: Maximum statement or expression depth exceeded"); + } + + { // right recursive + QString s(200000, '-'); + s += "\nd"; + + QJSEngine engine; + QJSValue result = engine.evaluate(s); + QVERIFY(result.isError()); + QCOMPARE(result.toString(), "SyntaxError: Maximum statement or expression depth exceeded"); + } } -QTEST_MAIN(tst_v4misc) +QTEST_MAIN(tst_v4misc); #include "tst_v4misc.moc" diff --git a/tests/auto/qml/v4traced/tst_v4traced.cpp b/tests/auto/qml/v4traced/tst_v4traced.cpp new file mode 100644 index 0000000000..f82cc0ed5e --- /dev/null +++ b/tests/auto/qml/v4traced/tst_v4traced.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QJSEngine> +#include <private/qjsvalue_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4script_p.h> + +class EnvVarSaver +{ +public: + EnvVarSaver(const char *name, const QByteArray &newValue) + : _name(name) + { + _wasSet = qEnvironmentVariableIsSet(name); + if (_wasSet) + _oldValue = qgetenv(name); + qputenv(name, newValue); + } + + ~EnvVarSaver() + { + if (_wasSet) + qputenv(_name, _oldValue); + else + qunsetenv(_name); + } + +private: + const char *_name; + bool _wasSet; + QByteArray _oldValue; +}; + +class tst_v4traced : public QObject +{ + Q_OBJECT + +private slots: + void collectTraces_data(); + void collectTraces(); + + void binopI32deopt_data(); + void binopI32deopt(); + + void calls_data(); + void calls(); + + void setLookup(); + void construct(); +}; + +void tst_v4traced::collectTraces_data() +{ + QTest::addColumn<QString>("code"); + QTest::addColumn<int>("tracePointCount"); + QTest::addColumn<int>("interestingTracePoint"); + QTest::addColumn<quint8>("expectedBits"); + + QTest::newRow("int+") << "var a = 4; a + 2" << 2 << 1 << quint8(QV4::ObservedTraceValues::Integer); + QTest::newRow("double+") << "var a = 4.1; a + 1.9" << 2 << 1 << quint8(QV4::ObservedTraceValues::Double); + QTest::newRow("object+") << "var a = '4'; a + '2'" << 2 << 1 << quint8(QV4::ObservedTraceValues::Other); +} + +void tst_v4traced::collectTraces() +{ + QFETCH(QString, code); + QFETCH(int, tracePointCount); + QFETCH(int, interestingTracePoint); + QFETCH(quint8, expectedBits); + + EnvVarSaver forceInterpreter("QV4_FORCE_INTERPRETER", "1"); + EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); + + QV4::ExecutionEngine vm; + QV4::Scope scope(&vm); + QV4::ScopedContext ctx(scope, vm.rootContext()); + QV4::ScopedValue result(scope); + QScopedPointer<QV4::Script> script; + script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "collectTraces")); + script->parseAsBinding = false; + + QVERIFY(!scope.engine->hasException); + script->parse(); + QVERIFY(!scope.engine->hasException); + + QVERIFY(script->function()->tracingEnabled()); + result = script->run(); + QVERIFY(!scope.engine->hasException); + + QCOMPARE(int(script->function()->compiledFunction->nTraceInfos), tracePointCount); + QCOMPARE(*script->function()->traceInfo(interestingTracePoint), expectedBits); +} + +void tst_v4traced::binopI32deopt_data() +{ + QTest::addColumn<QString>("operand"); + QTest::addColumn<int>("int_arg1"); + QTest::addColumn<int>("int_arg2"); + QTest::addColumn<int>("int_result"); + QTest::addColumn<QString>("other_arg1"); + QTest::addColumn<QString>("other_arg2"); + QTest::addColumn<double>("other_result"); + + QTest::newRow("+") << "+" << 1 << 2 << 3 << "1.1" << "1.9" << 3.0; + QTest::newRow("-") << "-" << 3 << 2 << 1 << "3.1" << "2.1" << 1.0; + QTest::newRow("*") << "*" << 2 << 3 << 6 << "2.1" << "1.9" << 3.99; + QTest::newRow("/") << "/" << 6 << 3 << 2 << "6.6" << "3.3" << 2.0; + + QTest::newRow("&") << "&" << 6 << 3 << 2 << "'6'" << "'3'" << 2.0; + QTest::newRow("|") << "|" << 6 << 3 << 7 << "'6'" << "'3'" << 7.0; + QTest::newRow("^") << "^" << 6 << 3 << 5 << "'6'" << "'3'" << 5.0; + + QTest::newRow("<<") << "<<" << 5 << 1 << 10 << "'5'" << "'1'" << 10.0; + QTest::newRow(">>") << ">>" << -1 << 1 << -1 << "'-1'" << "'1'" << -1.0; + QTest::newRow(">>>") << ">>>" << -1 << 1 << 0x7FFFFFFF << "'-1'" << "'1'" << 2147483647.0; + + QTest::newRow("==") << "==" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0; + QTest::newRow("!=") << "!=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0; + QTest::newRow("<" ) << "<" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0; + QTest::newRow("<=") << "<=" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0; + QTest::newRow(">" ) << ">" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0; + QTest::newRow(">=") << ">=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0; +} + +void tst_v4traced::binopI32deopt() +{ + QFETCH(QString, operand); + QFETCH(int, int_arg1); + QFETCH(int, int_arg2); + QFETCH(int, int_result); + QFETCH(QString, other_arg1); + QFETCH(QString, other_arg2); + QFETCH(double, other_result); + + QString func = QStringLiteral("function binopI32(a, b) { return a %1 b }").arg(operand); + QString intCall = QStringLiteral("binopI32(%1, %2)").arg(int_arg1).arg(int_arg2); + QString otherCall = QStringLiteral("binopI32(%1, %2)").arg(other_arg1).arg(other_arg2); + + QJSEngine engine; + engine.evaluate(func); + + QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // interpret + trace + QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // jit + QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // deopt + QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // retrace + QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // rejit +} + +void tst_v4traced::calls_data() +{ + QTest::addColumn<QString>("call"); + + QTest::newRow("callGlobalLookup") << "globalLookup"; + QTest::newRow("callPropertyLookup") << "obj.propertyLookup"; +} + +class Calls +{ +public: + static int callCount; + + static QV4::ReturnedValue doSomething(const QV4::FunctionObject */*o*/, + const QV4::Value */*thiz*/, + const QV4::Value *argv, int argc) + { + ++callCount; + + if (argc == 0) + return QV4::Encode(42); + + int prod = 1; + for (int i = 0; i < argc; ++i) { + Q_ASSERT(argv[i].isInteger()); + prod *= argv[i].int_32(); + } + return QV4::Encode(prod); + } +}; + +int Calls::callCount = 0; + +void tst_v4traced::calls() +{ + QFETCH(QString, call); + + EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); + EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1"); + + QV4::ExecutionEngine vm; + QV4::Scope scope(&vm); + QV4::ScopedContext ctx(scope, vm.rootContext()); + vm.globalObject->defineDefaultProperty(QLatin1String("globalLookup"), + Calls::doSomething); + QV4::ScopedObject obj(scope, vm.newObject()); + vm.globalObject->defineDefaultProperty(QLatin1String("obj"), obj); + obj->defineDefaultProperty("propertyLookup", Calls::doSomething); + + QString code = QStringLiteral( + "function doCalls() {\n" + " if (%1() != 42) return false\n" + " if (%1(21, 2) != 42) return false\n" + " if (%1(2, 3, 7) != 42) return false\n" + " return true\n" + "}\n" + "var result = true\n" + "for (var i = 0; i < 10; ++i) {\n" + " if (!doCalls()) { result = false; break }" + "}\n" + "result\n").arg(call); + QScopedPointer<QV4::Script> script; + script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "call")); + script->parseAsBinding = false; + + QVERIFY(!scope.engine->hasException); + script->parse(); + QVERIFY(!scope.engine->hasException); + + Calls::callCount = 0; + QV4::ScopedValue result(scope, script->run()); + QVERIFY(!scope.engine->hasException); + + QVERIFY(result->isBoolean()); + QVERIFY(result->booleanValue()); + QCOMPARE(Calls::callCount, 30); +} + +void tst_v4traced::setLookup() +{ + EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); + EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1"); + + QV4::ExecutionEngine vm; + QV4::Scope scope(&vm); + QV4::ScopedContext ctx(scope, vm.rootContext()); + QV4::ScopedObject obj(scope, vm.newObject()); + vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj); + obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32)); + + QString code = QStringLiteral( + "function doit() {\n" + " ++oracle.answer\n" + "}\n" + "for (var i = 0; i < 10; ++i) doit()\n" + "oracle.answer\n"); + QScopedPointer<QV4::Script> script; + script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup")); + script->parseAsBinding = false; + + QVERIFY(!scope.engine->hasException); + script->parse(); + QVERIFY(!scope.engine->hasException); + + QV4::ScopedValue result(scope, script->run()); + QVERIFY(!scope.engine->hasException); + + QVERIFY(result->isInteger()); + QCOMPARE(result->int_32(), 42); +} + +void tst_v4traced::construct() +{ + EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1"); + EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1"); + + QV4::ExecutionEngine vm; + QV4::Scope scope(&vm); + QV4::ScopedContext ctx(scope, vm.rootContext()); + QV4::ScopedObject obj(scope, vm.newObject()); + vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj); + obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32)); + + QString code = QStringLiteral( + "function doit() {\n" + " this.arr = new Array()\n" + " this.arr[0] = 0\n" + " this.arr[1] = 1\n" + " this.arr[2] = 2\n" + "}\n" + "var o\n" + "for (var i = 0; i < 10; ++i) o = new doit()\n" + "o.arr\n"); + QScopedPointer<QV4::Script> script; + script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup")); + script->parseAsBinding = false; + + QVERIFY(!scope.engine->hasException); + script->parse(); + QVERIFY(!scope.engine->hasException); + + QV4::ScopedValue result(scope, script->run()); + QVERIFY(!scope.engine->hasException); + + QVERIFY(result->as<QV4::ArrayObject>()); +} + +QTEST_MAIN(tst_v4traced) + +#include "tst_v4traced.moc" diff --git a/tests/auto/qml/v4traced/v4traced.pro b/tests/auto/qml/v4traced/v4traced.pro new file mode 100644 index 0000000000..cbc8f38046 --- /dev/null +++ b/tests/auto/qml/v4traced/v4traced.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_v4traced +macos:CONFIG -= app_bundle +QT += core-private qml-private testlib +SOURCES += tst_v4traced.cpp |