qtdeclarative/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp

1398 lines
50 KiB
C++
Raw Normal View History

/****************************************************************************
**
** 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 <QtQuickTestUtils/private/qmlutils_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlboundsignal_p.h>
#include <private/qqmldebugservice_p.h>
#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>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlproperty.h>
#include <QtQml/qqmlincubator.h>
#include <QtQuick/qquickitem.h>
#include <QtNetwork/qhostaddress.h>
#include <QtCore/qtimer.h>
#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 QQmlEngineDebugObjectReference();\
}\
} while (0)
class NonScriptProperty : public QObject {
Q_OBJECT
Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false)
public:
int nonScriptProp() const { return 0; }
void setNonScriptProp(int) {}
signals:
void nonScriptPropChanged();
};
QML_DECLARE_TYPE(NonScriptProperty)
class CustomTypes : public QObject
{
Q_OBJECT
Q_PROPERTY(QModelIndex modelIndex READ modelIndex)
public:
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 = nullptr) : 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 QQmlDataTest
{
Q_OBJECT
public:
tst_QQmlEngineDebugService()
: QQmlDataTest(QT_QMLTEST_DATADIR)
, m_conn(nullptr)
, m_dbg(nullptr)
, m_engine(nullptr)
, m_rootItem(nullptr) {}
private:
QQmlEngineDebugObjectReference findRootObject(int context = 0,
bool recursive = false);
QQmlEngineDebugPropertyReference findProperty(
const QList<QQmlEngineDebugPropertyReference> &props,
const QString &name) const;
void recursiveObjectTest(QObject *o,
const QQmlEngineDebugObjectReference &oref,
bool recursive) const;
void getContexts();
QQmlDebugConnection *m_conn;
QQmlEngineDebugClient *m_dbg;
QQmlEngine *m_engine;
QQuickItem *m_rootItem;
QObjectList m_components;
private slots:
void initTestCase() override;
void cleanupTestCase();
void watch_property();
void watch_object();
void watch_expression();
void watch_expression_data();
void watch_context();
void watch_file();
void queryAvailableEngines();
void queryRootContexts();
void queryObject();
void queryObject_data();
void queryObjectsForLocation();
void queryObjectsForLocation_data();
void queryExpressionResult();
void queryExpressionResult_data();
void queryExpressionResultInRootContext();
void queryExpressionResultBC();
void queryExpressionResultBC_data();
void setBindingForObject();
void resetBindingForObject();
void setMethodBody();
void queryObjectTree();
void setBindingInStates();
void regression_QTCREATORBUG_7451();
void queryObjectWithNonStreamableTypes();
void jsonData();
void asynchronousCreate();
void invalidContexts();
void createObjectOnDestruction();
};
QQmlEngineDebugObjectReference tst_QQmlEngineDebugService::findRootObject(
int context, bool recursive)
{
bool success = false;
m_dbg->queryAvailableEngines(&success);
QVERIFYOBJECT(success);
QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QVERIFYOBJECT(m_dbg->engines().count());
m_dbg->queryRootContexts(m_dbg->engines()[0], &success);
QVERIFYOBJECT(success);
QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QVERIFYOBJECT(m_dbg->rootContext().contexts.count());
QVERIFYOBJECT(m_dbg->rootContext().contexts.last().objects.count());
int count = m_dbg->rootContext().contexts.count();
recursive ? m_dbg->queryObjectRecursive(m_dbg->rootContext().contexts[count - context - 1].objects[0],
&success) :
m_dbg->queryObject(m_dbg->rootContext().contexts[count - context - 1].objects[0], &success);
QVERIFYOBJECT(success);
QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
return m_dbg->object();
}
QQmlEngineDebugPropertyReference tst_QQmlEngineDebugService::findProperty(
const QList<QQmlEngineDebugPropertyReference> &props, const QString &name) const
{
foreach (const QQmlEngineDebugPropertyReference &p, props) {
if (p.name == name)
return p;
}
return QQmlEngineDebugPropertyReference();
}
void tst_QQmlEngineDebugService::recursiveObjectTest(
QObject *o, const QQmlEngineDebugObjectReference &oref, bool recursive) const
{
const QMetaObject *meta = o->metaObject();
QCOMPARE(oref.debugId, QQmlDebugService::idForObject(o));
QCOMPARE(oref.name, o->objectName());
QCOMPARE(oref.className, QQmlMetaType::prettyTypeName(o));
QCOMPARE(oref.contextDebugId, QQmlDebugService::idForObject(
qmlContext(o)));
const QObjectList &children = o->children();
for (int i=0; i<children.count(); i++) {
QObject *child = children[i];
if (!qmlContext(child))
continue;
int debugId = QQmlDebugService::idForObject(child);
QVERIFY(debugId >= 0);
QQmlEngineDebugObjectReference cref;
foreach (const QQmlEngineDebugObjectReference &ref, oref.children) {
QVERIFY(!ref.className.isEmpty());
if (ref.debugId == debugId) {
cref = ref;
break;
}
}
QVERIFY(cref.debugId >= 0);
if (recursive)
recursiveObjectTest(child, cref, true);
}
foreach (const QQmlEngineDebugPropertyReference &p, oref.properties) {
QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o));
// signal properties are fake - they are generated from QQmlAbstractBoundSignal children
if (p.name.startsWith("on") && p.name.length() > 2 && p.name[2].isUpper()) {
QString signal = p.value.toString();
QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name));
QVERIFY(expr && expr->expression() == signal);
QVERIFY(p.valueTypeName.isEmpty());
QVERIFY(p.binding.isEmpty());
QVERIFY(!p.hasNotifySignal);
continue;
}
QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name.toUtf8().constData()));
QCOMPARE(p.name, QString::fromUtf8(pmeta.name()));
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.userType() < QMetaType::User && pmeta.userType() != QMetaType::QVariant) {
const QVariant expected = pmeta.read(o);
QVariant value = p.value;
QMetaType expectedType = expected.metaType();
if (value != expected && p.value.canConvert(expectedType))
value.convert(expectedType);
QVERIFY2(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.userType()).arg(pmeta.userType()).toUtf8());
}
if (p.name == "parent")
QVERIFY(p.valueTypeName == "QGraphicsObject*" ||
p.valueTypeName == "QQuickItem*");
else
QCOMPARE(p.valueTypeName, QString::fromUtf8(pmeta.typeName()));
QQmlAbstractBinding *binding =
QQmlPropertyPrivate::binding(
QQmlProperty(o, p.name));
if (binding)
QCOMPARE(binding->expression(), p.binding);
QCOMPARE(p.hasNotifySignal, pmeta.hasNotifySignal());
QVERIFY(pmeta.isValid());
}
}
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()
{
QQmlDataTest::initTestCase();
qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement");
QTest::ignoreMessage(QtDebugMsg, "QML Debugger: Waiting for connection on port 3768...");
m_engine = new QQmlEngine(this);
qmlRegisterType<CustomTypes>("Backend", 1, 0, "CustomTypes");
qmlRegisterType<JsonTest>("JsonTest", 1, 0, "JsonTest");
// The contents of these files was previously hardcoded as QList<QByteArray>
// directly in this test, but that fails on Android, because the required
// dependencies are not deployed. When the contents is moved to separate
// files, qmlimportscanner is capable of providing all the necessary
// dependencies.
// Note that the order of the files in this list matters! The test-cases
// expect Qml components to be created is certain order.
constexpr const char *fileNames[] = {
"complexItem.qml",
"emptyItem.qml",
"itemWithFunctions.qml",
"rectangleWithTransitions.qml",
"customTypes.qml",
"jsonTest.qml"
};
for (auto file : fileNames) {
QQmlComponent component(m_engine, testFileUrl(file));
QVERIFY(component.isReady()); // fails if bad syntax
m_components << qobject_cast<QQuickItem*>(component.create());
}
m_rootItem = qobject_cast<QQuickItem*>(m_components.first());
// add an extra context to test for multiple contexts
QQmlContext *context = new QQmlContext(m_engine->rootContext(), this);
context->setObjectName("tst_QQmlDebug_childContext");
m_conn = new QQmlDebugConnection(this);
m_conn->connectToHost("127.0.0.1", 3768);
bool ok = m_conn->waitForConnected();
QVERIFY(ok);
m_dbg = new QQmlEngineDebugClient(m_conn);
QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_conn);
QTRY_COMPARE(m_dbg->state(), QQmlEngineDebugClient::Enabled);
foreach (QQmlDebugClient *other, others)
QCOMPARE(other->state(), QQmlDebugClient::Unavailable);
qDeleteAll(others);
}
void tst_QQmlEngineDebugService::cleanupTestCase()
{
delete m_conn;
qDeleteAll(m_components);
delete m_engine;
}
void tst_QQmlEngineDebugService::setMethodBody()
{
bool success;
QQmlEngineDebugObjectReference obj = findRootObject(2);
QVERIFY(!obj.className.isEmpty());
QObject *root = m_components.at(2);
// Without args
{
QVariant rv;
QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv)));
QCOMPARE(rv, QVariant(qreal(3)));
QVERIFY(m_dbg->setMethodBody(obj.debugId, "myMethodNoArgs", "return 7",
&success));
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv)));
QCOMPARE(rv, QVariant(qreal(7)));
}
// With args
{
QVariant rv;
QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
QCOMPARE(rv, QVariant(qreal(28)));
QVERIFY(m_dbg->setMethodBody(obj.debugId, "myMethod", "return a + 7",
&success));
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
QCOMPARE(rv, QVariant(qreal(26)));
}
}
void tst_QQmlEngineDebugService::watch_property()
{
QQmlEngineDebugObjectReference obj = findRootObject();
QVERIFY(!obj.className.isEmpty());
QQmlEngineDebugPropertyReference prop = findProperty(obj.properties, "width");
bool success;
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->addWatch(prop, &success);
QVERIFY(!success);
delete unconnected;
m_dbg->addWatch(QQmlEngineDebugPropertyReference(), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), false);
quint32 id = m_dbg->addWatch(prop, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
int origWidth = m_rootItem->property("width").toInt();
m_rootItem->setProperty("width", origWidth*2);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
QCOMPARE(spy.count(), 1);
m_dbg->removeWatch(id, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
// restore original value and verify spy doesn't get additional signal since watch has been removed
m_rootItem->setProperty("width", origWidth);
QTest::qWait(100);
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name.toUtf8());
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QCOMPARE(spy.at(0).at(1).value<QVariant>(), QVariant::fromValue(origWidth*2));
}
void tst_QQmlEngineDebugService::watch_object()
{
QQmlEngineDebugObjectReference obj = findRootObject();
QVERIFY(!obj.className.isEmpty());
bool success;
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->addWatch(obj, &success);
QVERIFY(!success);
delete unconnected;
m_dbg->addWatch(QQmlEngineDebugObjectReference(), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), false);
quint32 id = m_dbg->addWatch(obj, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
int origWidth = m_rootItem->property("width").toInt();
int origHeight = m_rootItem->property("height").toInt();
m_rootItem->setProperty("width", origWidth*2);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
m_rootItem->setProperty("height", origHeight*2);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
QVERIFY(spy.count() > 0);
int newWidth = -1;
int newHeight = -1;
for (int i=0; i<spy.count(); i++) {
const QVariantList &values = spy[i];
if (values[0].value<QByteArray>() == "width")
newWidth = values[1].value<QVariant>().toInt();
else if (values[0].value<QByteArray>() == "height")
newHeight = values[1].value<QVariant>().toInt();
}
m_dbg->removeWatch(id, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
// since watch has been removed, restoring the original values should not trigger a valueChanged()
spy.clear();
m_rootItem->setProperty("width", origWidth);
m_rootItem->setProperty("height", origHeight);
QTest::qWait(100);
QCOMPARE(spy.count(), 0);
QCOMPARE(newWidth, origWidth * 2);
QCOMPARE(newHeight, origHeight * 2);
}
void tst_QQmlEngineDebugService::watch_expression()
{
QFETCH(QString, expr);
QFETCH(int, increment);
QFETCH(int, incrementCount);
int origWidth = m_rootItem->property("width").toInt();
QQmlEngineDebugObjectReference obj = findRootObject();
QVERIFY(!obj.className.isEmpty());
bool success;
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->addWatch(obj, expr, &success);
QVERIFY(!success);
delete unconnected;
m_dbg->addWatch(QQmlEngineDebugObjectReference(), expr, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), false);
quint32 id = m_dbg->addWatch(obj, expr, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
int width = origWidth;
for (int i=0; i<incrementCount+1; i++) {
if (i > 0) {
width += increment;
m_rootItem->setProperty("width", width);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
}
}
m_dbg->removeWatch(id, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
// restore original value and verify spy doesn't get a signal since watch has been removed
m_rootItem->setProperty("width", origWidth);
QTest::qWait(100);
QCOMPARE(spy.count(), incrementCount);
width = origWidth + increment;
for (int i=0; i<spy.count(); i++) {
width += increment;
QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
}
}
void tst_QQmlEngineDebugService::watch_expression_data()
{
QTest::addColumn<QString>("expr");
QTest::addColumn<int>("increment");
QTest::addColumn<int>("incrementCount");
QTest::newRow("width") << "width" << 0 << 0;
QTest::newRow("width+10") << "width + 10" << 10 << 5;
}
void tst_QQmlEngineDebugService::watch_context()
{
QQmlEngineDebugContextReference c;
QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented");
bool success;
m_dbg->addWatch(c, QString(), &success);
QVERIFY(!success);
}
void tst_QQmlEngineDebugService::watch_file()
{
QQmlEngineDebugFileReference f;
QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented");
bool success;
m_dbg->addWatch(f, &success);
QVERIFY(!success);
}
void tst_QQmlEngineDebugService::queryAvailableEngines()
{
bool success;
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->queryAvailableEngines(&success);
QVERIFY(!success);
delete unconnected;
m_dbg->queryAvailableEngines(&success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
// TODO test multiple engines
QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines();
QCOMPARE(engines.count(), 1);
foreach (const QQmlEngineDebugEngineReference &e, engines) {
QCOMPARE(e.debugId, QQmlDebugService::idForObject(m_engine));
QCOMPARE(e.name, m_engine->objectName());
}
}
void tst_QQmlEngineDebugService::queryRootContexts()
{
bool success;
m_dbg->queryAvailableEngines(&success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QVERIFY(m_dbg->engines().count());
const QQmlEngineDebugEngineReference engine = m_dbg->engines()[0];
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->queryRootContexts(engine, &success);
QVERIFY(!success);
delete unconnected;
m_dbg->queryRootContexts(engine, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QQmlContext *actualContext = m_engine->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(), 7);
QVERIFY(context.contexts[0].debugId >= 0);
QCOMPARE(context.contexts[0].name, QString("tst_QQmlDebug_childContext"));
}
void tst_QQmlEngineDebugService::queryObject()
{
QFETCH(bool, recursive);
bool success;
QQmlEngineDebugObjectReference rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success);
QVERIFY(!success);
delete unconnected;
recursive ? m_dbg->queryObjectRecursive(rootObject, &success) : m_dbg->queryObject(rootObject, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QQmlEngineDebugObjectReference obj = m_dbg->object();
QVERIFY(!obj.className.isEmpty());
// check source as defined in main()
QQmlEngineDebugFileReference source = obj.source;
QCOMPARE(source.url, testFileUrl("complexItem.qml"));
QCOMPARE(source.lineNumber, 31); // because of license header
QCOMPARE(source.columnNumber, 1);
// generically test all properties, children and childrens' properties
recursiveObjectTest(m_rootItem, obj, recursive);
if (recursive) {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
QVERIFY(child.properties.count() > 0);
}
QQmlEngineDebugObjectReference rect;
QQmlEngineDebugObjectReference text;
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
if (child.className == "Rectangle")
rect = child;
else if (child.className == "Text")
text = child;
}
// test specific property values
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
QVariant expected = findProperty(rect.properties, "color").value;
expected.convert(QMetaType::fromType<QColor>());
QCOMPARE(expected , QVariant::fromValue(QColor("blue")));
} else {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
QCOMPARE(child.properties.count(), 0);
}
}
}
void tst_QQmlEngineDebugService::queryObject_data()
{
QTest::addColumn<bool>("recursive");
QTest::newRow("non-recursive") << false;
QTest::newRow("recursive") << true;
}
void tst_QQmlEngineDebugService::queryObjectsForLocation()
{
QFETCH(bool, recursive);
bool success;
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(nullptr);
recursive ? unconnected->queryObjectsForLocationRecursive(fileName, lineNumber,
columnNumber, &success)
: unconnected->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(!success);
delete unconnected;
recursive ? m_dbg->queryObjectsForLocationRecursive(fileName, lineNumber,
columnNumber, &success)
: m_dbg->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->objects().count(), 1);
QQmlEngineDebugObjectReference obj = m_dbg->objects().first();
QVERIFY(!obj.className.isEmpty());
// check source as defined in main()
QQmlEngineDebugFileReference source = obj.source;
QCOMPARE(source.url, testFileUrl(fileName));
QCOMPARE(source.lineNumber, lineNumber);
QCOMPARE(source.columnNumber, columnNumber);
// generically test all properties, children and childrens' properties
recursiveObjectTest(m_rootItem, obj, recursive);
if (recursive) {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
QVERIFY(child.properties.count() > 0);
}
QQmlEngineDebugObjectReference rect;
QQmlEngineDebugObjectReference text;
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
if (child.className == "Rectangle")
rect = child;
else if (child.className == "Text")
text = child;
}
// test specific property values
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
QVariant expected = findProperty(rect.properties, "color").value;
QMetaType colorMetatype = QMetaType::fromType<QColor>();
QVERIFY(expected.canConvert(colorMetatype));
expected.convert(colorMetatype);
QCOMPARE(expected , QVariant::fromValue(QColor("blue")));
} else {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
QCOMPARE(child.properties.count(), 0);
}
}
}
void tst_QQmlEngineDebugService::queryObjectsForLocation_data()
{
QTest::addColumn<bool>("recursive");
QTest::newRow("non-recursive") << false;
QTest::newRow("recursive") << true;
}
void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451()
{
QQmlEngineDebugObjectReference rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
int contextId = rootObject.contextDebugId;
QQmlContext *context = qobject_cast<QQmlContext *>(QQmlDebugService::objectForId(contextId));
QQmlComponent component(context->engine());
QByteArray content;
content.append("import QtQuick 2.0\n"
"Text {"
"y: 10\n"
"text: \"test\"\n"
"}");
component.setData(content, rootObject.source.url);
QObject *object = component.create(context);
QVERIFY(object);
int idNew = QQmlDebugService::idForObject(object);
QVERIFY(idNew >= 0);
const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
int lineNumber = rootObject.source.lineNumber;
int columnNumber = rootObject.source.columnNumber;
bool success = false;
m_dbg->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
foreach (QQmlEngineDebugObjectReference child, rootObject.children) {
QVERIFY(!child.className.isEmpty());
success = false;
lineNumber = child.source.lineNumber;
columnNumber = child.source.columnNumber;
m_dbg->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
}
delete object;
QObject *deleted = QQmlDebugService::objectForId(idNew);
QVERIFY(!deleted);
lineNumber = rootObject.source.lineNumber;
columnNumber = rootObject.source.columnNumber;
success = false;
m_dbg->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
foreach (QQmlEngineDebugObjectReference child, rootObject.children) {
QVERIFY(!child.className.isEmpty());
success = false;
lineNumber = child.source.lineNumber;
columnNumber = child.source.columnNumber;
m_dbg->queryObjectsForLocation(fileName, lineNumber,
columnNumber, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
}
}
void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes()
{
bool success;
QQmlEngineDebugObjectReference rootObject = findRootObject(4, true);
QVERIFY(!rootObject.className.isEmpty());
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->queryObject(rootObject, &success);
QVERIFY(!success);
delete unconnected;
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, "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()
{
QFETCH(QString, expr);
QFETCH(QVariant, result);
int objectId = findRootObject().debugId;
bool success;
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->queryExpressionResult(objectId, expr, &success);
QVERIFY(!success);
delete unconnected;
m_dbg->queryExpressionResult(objectId, expr, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->resultExpr(), result);
}
void tst_QQmlEngineDebugService::queryExpressionResult_data()
{
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60);
QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500);
QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>"));
QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>"));
QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>")));
QVariantMap map;
map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map);
QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05);
}
void tst_QQmlEngineDebugService::queryExpressionResultInRootContext()
{
bool success;
const QString exp = QLatin1String("1");
m_dbg->queryExpressionResult(-1, exp, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->resultExpr().toString(), exp);
}
void tst_QQmlEngineDebugService::queryExpressionResultBC()
{
QFETCH(QString, expr);
QFETCH(QVariant, result);
int objectId = findRootObject().debugId;
bool success;
QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
unconnected->queryExpressionResultBC(objectId, expr, &success);
QVERIFY(!success);
delete unconnected;
m_dbg->queryExpressionResultBC(objectId, expr, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->resultExpr(), result);
}
void tst_QQmlEngineDebugService::queryExpressionResultBC_data()
{
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60);
QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500);
QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>"));
QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>"));
QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>")));
QVariantMap map;
map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
Fix deprecation warnings about QVariant API Fix warnings like: sruntime/qv4serialize.cpp:378:45: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlListModelWorkerAgent::VariantRef]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qml/qqmlvmemetaobject.cpp:597:61: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1319:66: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] jsruntime/qv4engine.cpp:1350:60: warning: 'QVariant qVariantFromValue(const T&) [with T = QList<QObject*>]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8396:78: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickitem.cpp:8693:80: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:126:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickgenericshadereffect.cpp:127:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:713:69: warning: 'QVariant qVariantFromValue(const T&) [with T = QObject*]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] items/qquickopenglshadereffect.cpp:714:55: warning: 'QVariant qVariantFromValue(const T&) [with T = QSize]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qquickcustomparticle.cpp:416:89: warning: 'QVariant qVariantFromValue(const T&) [with T = double]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] qqmlenginedebugclient.cpp:403:47: warning: 'QVariant qVariantFromValue(const T&) [with T = QQmlEngineDebugObjectReference]' is deprecated: Use QVariant::fromValue() instead. [-Wdeprecated-declarations] Task-number: QTBUG-74043 Change-Id: I14cb7d7c1fb8dc6321e32208a7de15f6bdb19065 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-04-05 07:42:17 +00:00
QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map);
QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05);
}
void tst_QQmlEngineDebugService::setBindingForObject()
{
QQmlEngineDebugObjectReference rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
QVERIFY(rootObject.debugId != -1);
QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width");
QCOMPARE(widthPropertyRef.value, QVariant(10));
QCOMPARE(widthPropertyRef.binding, QString());
bool success;
//
// set literal
//
m_dbg->setBindingForObject(rootObject.debugId, "width", "15", true,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
widthPropertyRef = findProperty(rootObject.properties, "width");
QCOMPARE(widthPropertyRef.value, QVariant(15));
QCOMPARE(widthPropertyRef.binding, QString());
//
// set expression
//
m_dbg->setBindingForObject(rootObject.debugId, "width", "height", false,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
widthPropertyRef = findProperty(rootObject.properties, "width");
QCOMPARE(widthPropertyRef.value, QVariant(20));
QEXPECT_FAIL("", "Cannot retrieve text for a binding (QTBUG-37273)", Continue);
QCOMPARE(widthPropertyRef.binding, QString("height"));
//
// set handler
//
rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
QCOMPARE(rootObject.children.size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement
QQmlEngineDebugObjectReference mouseAreaObject = rootObject.children.at(2);
QVERIFY(!mouseAreaObject.className.isEmpty());
m_dbg->queryObjectRecursive(mouseAreaObject, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
mouseAreaObject = m_dbg->object();
QCOMPARE(mouseAreaObject.className, QString("MouseArea"));
QQmlEngineDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered");
QCOMPARE(onEnteredRef.name, QString("onEntered"));
Compile binding expressions in the QQmlCompiler This is done by re-using the JS code generator from the new compiler. A few bugs were fixed on the way: * The index into the compiledData->runtimeFunctions array is not the same as the function index when they are collected (from the AST), as for example binding expressions may create extra V4IR::Function objects that break the 1:1 mapping. Therefore the JS code gen will return a mapping from incoming function index to V4IR::Module::Function (and thus runtimeFunction) * Binding expressions in the old backend get usually unpacked from their ExpressionStatement node. The reference to that node is lost, and instead of trying to preserve it, we simply synthesize it again. This won't be necessary anymore with the new compiler in the future. * Commit 1c29d63d6045cf9d58cbc0f850de8fa50bf75d09 ensured to always look up locals by name, and so we have to do the same when initializing the closures of nested functions inside binding expressions (in qv4codegen.cpp) * Had to change the Qml debugger service auto-test, which does toString() on a function that is now compiled. Even if we implemented FunctionPrototype::toString() to do what v8 does by extracting the string from the file, it wouldn't help in this test, because it feeds the input from a string instead of a file. * In tst_parserstress we now end up compiling all JS code, which previously was only parsed. This triggers some bugs in the SSA handling. Those tests are skipped and tracked in QTBUG-34047 Change-Id: I44df51085510da0fd3d99eb5f1c7d4d17bcffdcf Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-10-08 09:44:57 +00:00
// Sorry, can't do that anymore: QCOMPARE(onEnteredRef.value, QVariant("{ console.log('hello') }"));
QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }"));
m_dbg->setBindingForObject(mouseAreaObject.debugId, "onEntered",
"{console.log('hello, world') }", false,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
mouseAreaObject = rootObject.children.at(2);
QVERIFY(!mouseAreaObject.className.isEmpty());
m_dbg->queryObjectRecursive(mouseAreaObject, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
mouseAreaObject = m_dbg->object();
QVERIFY(!mouseAreaObject.className.isEmpty());
onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered");
QCOMPARE(onEnteredRef.name, QString("onEntered"));
QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }"));
}
void tst_QQmlEngineDebugService::resetBindingForObject()
{
QQmlEngineDebugObjectReference rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
QVERIFY(rootObject.debugId != -1);
QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width");
bool success = false;
m_dbg->setBindingForObject(rootObject.debugId, "width", "15", true,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
//
// reset
//
m_dbg->resetBindingForObject(rootObject.debugId, "width", &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
widthPropertyRef = findProperty(rootObject.properties, "width");
QCOMPARE(widthPropertyRef.value, QVariant(0));
QCOMPARE(widthPropertyRef.binding, QString());
//
// reset nested property
//
success = false;
m_dbg->resetBindingForObject(rootObject.debugId, "font.bold", &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
rootObject = findRootObject();
QVERIFY(!rootObject.className.isEmpty());
QQmlEngineDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold");
QCOMPARE(boldPropertyRef.value.toBool(), false);
QCOMPARE(boldPropertyRef.binding, QString());
}
void tst_QQmlEngineDebugService::setBindingInStates()
{
// Check if changing bindings of propertychanges works
const int sourceIndex = 3;
QQmlEngineDebugObjectReference obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QVERIFY(obj.debugId != -1);
QVERIFY(obj.children.count() >= 2);
bool success;
// We are going to switch state a couple of times, we need to get rid of the transition before
m_dbg->queryExpressionResult(obj.debugId,QString("transitions = []"), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
// check initial value of the property that is changing
m_dbg->queryExpressionResult(obj.debugId,QString("state=\"state1\""), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(),200);
m_dbg->queryExpressionResult(obj.debugId,QString("state=\"\""),
&success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
obj = findRootObject(sourceIndex, true);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100);
// change the binding
QQmlEngineDebugObjectReference state = obj.children[1];
QCOMPARE(state.className, QString("State"));
QVERIFY(state.children.count() > 0);
QQmlEngineDebugObjectReference propertyChange = state.children[0];
QVERIFY(!propertyChange.className.isEmpty());
QVERIFY(propertyChange.debugId != -1);
m_dbg->setBindingForObject(propertyChange.debugId, "width",QVariant(300),true,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
// check properties changed in state
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100);
m_dbg->queryExpressionResult(obj.debugId,QString("state=\"state1\""), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300);
// check changing properties of base state from within a state
m_dbg->setBindingForObject(obj.debugId,"width","height*2",false,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
m_dbg->setBindingForObject(obj.debugId,"height","200",true,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300);
m_dbg->queryExpressionResult(obj.debugId,QString("state=\"\""), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400);
// reset binding while in a state
m_dbg->queryExpressionResult(obj.debugId,QString("state=\"state1\""), &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300);
m_dbg->resetBindingForObject(propertyChange.debugId, "width", &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400);
// re-add binding
m_dbg->setBindingForObject(propertyChange.debugId, "width", "300", true,
QString(), -1, &success);
QVERIFY(success);
QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
QCOMPARE(m_dbg->valid(), true);
obj = findRootObject(sourceIndex);
QVERIFY(!obj.className.isEmpty());
QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300);
}
void tst_QQmlEngineDebugService::queryObjectTree()
{
const int sourceIndex = 3;
QQmlEngineDebugObjectReference obj = findRootObject(sourceIndex, true);
QVERIFY(!obj.className.isEmpty());
QVERIFY(obj.debugId != -1);
QVERIFY(obj.children.count() >= 2);
// check state
QQmlEngineDebugObjectReference state = obj.children[1];
QCOMPARE(state.className, QString("State"));
QVERIFY(state.children.count() > 0);
QQmlEngineDebugObjectReference propertyChange = state.children[0];
QVERIFY(!propertyChange.className.isEmpty());
QVERIFY(propertyChange.debugId != -1);
QQmlEngineDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target");
QCOMPARE(propertyChangeTarget.objectDebugId, propertyChange.debugId);
QQmlEngineDebugObjectReference targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(propertyChangeTarget.value);
QVERIFY(!targetReference.className.isEmpty());
QCOMPARE(targetReference.debugId, -1);
QCOMPARE(targetReference.name, QString("<unnamed object>"));
// check transition
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);
QQmlEngineDebugObjectReference animation = transition.children[0];
QVERIFY(!animation.className.isEmpty());
QVERIFY(animation.debugId != -1);
QQmlEngineDebugPropertyReference animationTarget = findProperty(animation.properties,"target");
QCOMPARE(animationTarget.objectDebugId, animation.debugId);
targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(animationTarget.value);
QVERIFY(!targetReference.className.isEmpty());
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() {
QQmlEngineDebugObjectReference object;
auto connection = connect(m_dbg, &QQmlEngineDebugClient::newObject, this, [&](int objectId) {
object.debugId = objectId;
});
QByteArray asynchronousComponent = "import QtQuick 2.5\n"
"Rectangle { id: asyncRect }";
QQmlComponent component(m_engine);
component.setData(asynchronousComponent, QUrl::fromLocalFile(""));
QVERIFY(component.isReady()); // fails if bad syntax
QQmlIncubator incubator(QQmlIncubator::Asynchronous);
component.create(incubator);
QVERIFY(m_dbg->object().idString != QLatin1String("asyncRect"));
QTRY_VERIFY(object.debugId != -1);
disconnect(connection);
bool success = false;
m_dbg->queryObject(object, &success);
QVERIFY(success);
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);
QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(&context);
contextData->invalidate();
getContexts();
QCOMPARE(m_dbg->rootContext().contexts.count(), base);
QQmlRefPointer<QQmlContextData> rootData = QQmlContextData::get(m_engine->rootContext());
rootData->invalidate();
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;
QScopedArrayPointer<char *>_argv(new char*[_argc]);
for (int i = 0; i < argc; ++i)
_argv[i] = argv[i];
char arg[] = "-qmljsdebugger=port:3768,services:QmlDebugger";
_argv[_argc - 1] = arg;
QGuiApplication app(_argc, _argv.data());
tst_QQmlEngineDebugService tc;
return QTest::qExec(&tc, _argc, _argv.data());
}
#include "tst_qqmlenginedebugservice.moc"