|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the test suite of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 #include <qtest.h> |
|
42 #include <QSignalSpy> |
|
43 #include <QTimer> |
|
44 #include <QHostAddress> |
|
45 #include <QDebug> |
|
46 #include <QThread> |
|
47 |
|
48 #include <QtDeclarative/qdeclarativeengine.h> |
|
49 #include <QtDeclarative/qdeclarativecontext.h> |
|
50 #include <QtDeclarative/qdeclarativecomponent.h> |
|
51 #include <QtDeclarative/qdeclarativeexpression.h> |
|
52 #include <QtDeclarative/qdeclarativeproperty.h> |
|
53 |
|
54 #include <private/qdeclarativebinding_p.h> |
|
55 #include <private/qdeclarativedebug_p.h> |
|
56 #include <private/qdeclarativeenginedebug_p.h> |
|
57 #include <private/qdeclarativedebugclient_p.h> |
|
58 #include <private/qdeclarativedebugservice_p.h> |
|
59 #include <private/qdeclarativerectangle_p.h> |
|
60 #include <private/qdeclarativemetatype_p.h> |
|
61 #include <private/qdeclarativeproperty_p.h> |
|
62 |
|
63 #include "../../../shared/util.h" |
|
64 #include "../shared/debugutil_p.h" |
|
65 |
|
66 Q_DECLARE_METATYPE(QDeclarativeDebugWatch::State) |
|
67 |
|
68 |
|
69 class tst_QDeclarativeDebug : public QObject |
|
70 { |
|
71 Q_OBJECT |
|
72 |
|
73 private: |
|
74 QDeclarativeDebugObjectReference findRootObject(); |
|
75 QDeclarativeDebugPropertyReference findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const; |
|
76 void waitForQuery(QDeclarativeDebugQuery *query); |
|
77 |
|
78 void recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const; |
|
79 |
|
80 void recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const; |
|
81 void recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const; |
|
82 void compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const; |
|
83 |
|
84 QDeclarativeDebugConnection *m_conn; |
|
85 QDeclarativeEngineDebug *m_dbg; |
|
86 QDeclarativeEngine *m_engine; |
|
87 QDeclarativeItem *m_rootItem; |
|
88 |
|
89 QObjectList m_components; |
|
90 |
|
91 private slots: |
|
92 void initTestCase(); |
|
93 void cleanupTestCase(); |
|
94 |
|
95 void watch_property(); |
|
96 void watch_object(); |
|
97 void watch_expression(); |
|
98 void watch_expression_data(); |
|
99 void watch_context(); |
|
100 void watch_file(); |
|
101 |
|
102 void queryAvailableEngines(); |
|
103 void queryRootContexts(); |
|
104 void queryObject(); |
|
105 void queryObject_data(); |
|
106 void queryExpressionResult(); |
|
107 void queryExpressionResult_data(); |
|
108 |
|
109 void tst_QDeclarativeDebugFileReference(); |
|
110 void tst_QDeclarativeDebugEngineReference(); |
|
111 void tst_QDeclarativeDebugObjectReference(); |
|
112 void tst_QDeclarativeDebugContextReference(); |
|
113 void tst_QDeclarativeDebugPropertyReference(); |
|
114 }; |
|
115 |
|
116 QDeclarativeDebugObjectReference tst_QDeclarativeDebug::findRootObject() |
|
117 { |
|
118 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
119 waitForQuery(q_engines); |
|
120 |
|
121 if (q_engines->engines().count() == 0) |
|
122 return QDeclarativeDebugObjectReference(); |
|
123 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); |
|
124 waitForQuery(q_context); |
|
125 |
|
126 if (q_context->rootContext().objects().count() == 0) |
|
127 return QDeclarativeDebugObjectReference(); |
|
128 QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this); |
|
129 waitForQuery(q_obj); |
|
130 |
|
131 QDeclarativeDebugObjectReference result = q_obj->object(); |
|
132 |
|
133 delete q_engines; |
|
134 delete q_context; |
|
135 delete q_obj; |
|
136 |
|
137 return result; |
|
138 } |
|
139 |
|
140 QDeclarativeDebugPropertyReference tst_QDeclarativeDebug::findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const |
|
141 { |
|
142 foreach(const QDeclarativeDebugPropertyReference &p, props) { |
|
143 if (p.name() == name) |
|
144 return p; |
|
145 } |
|
146 return QDeclarativeDebugPropertyReference(); |
|
147 } |
|
148 |
|
149 void tst_QDeclarativeDebug::waitForQuery(QDeclarativeDebugQuery *query) |
|
150 { |
|
151 QVERIFY(query); |
|
152 QCOMPARE(query->parent(), qobject_cast<QObject*>(this)); |
|
153 QVERIFY(query->state() == QDeclarativeDebugQuery::Waiting); |
|
154 if (!QDeclarativeDebugTest::waitForSignal(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)))) |
|
155 QFAIL("query timed out"); |
|
156 } |
|
157 |
|
158 void tst_QDeclarativeDebug::recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const |
|
159 { |
|
160 const QMetaObject *meta = o->metaObject(); |
|
161 |
|
162 QDeclarativeType *type = QDeclarativeMetaType::qmlType(o->metaObject()); |
|
163 QString className = type ? type->qmlTypeName() : QString(); |
|
164 className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1); |
|
165 |
|
166 QCOMPARE(oref.debugId(), QDeclarativeDebugService::idForObject(o)); |
|
167 QCOMPARE(oref.name(), o->objectName()); |
|
168 QCOMPARE(oref.className(), className); |
|
169 QCOMPARE(oref.contextDebugId(), QDeclarativeDebugService::idForObject(qmlContext(o))); |
|
170 |
|
171 const QObjectList &children = o->children(); |
|
172 for (int i=0; i<children.count(); i++) { |
|
173 QObject *child = children[i]; |
|
174 if (!qmlContext(child)) |
|
175 continue; |
|
176 int debugId = QDeclarativeDebugService::idForObject(child); |
|
177 QVERIFY(debugId >= 0); |
|
178 |
|
179 QDeclarativeDebugObjectReference cref; |
|
180 foreach (const QDeclarativeDebugObjectReference &ref, oref.children()) { |
|
181 if (ref.debugId() == debugId) { |
|
182 cref = ref; |
|
183 break; |
|
184 } |
|
185 } |
|
186 QVERIFY(cref.debugId() >= 0); |
|
187 |
|
188 if (recursive) |
|
189 recursiveObjectTest(child, cref, true); |
|
190 } |
|
191 |
|
192 foreach (const QDeclarativeDebugPropertyReference &p, oref.properties()) { |
|
193 QCOMPARE(p.objectDebugId(), QDeclarativeDebugService::idForObject(o)); |
|
194 |
|
195 // signal properties are fake - they are generated from QDeclarativeBoundSignal children |
|
196 if (p.name().startsWith("on") && p.name().length() > 2 && p.name()[2].isUpper()) { |
|
197 QVERIFY(p.value().toString().startsWith('{') && p.value().toString().endsWith('}')); |
|
198 QVERIFY(p.valueTypeName().isEmpty()); |
|
199 QVERIFY(p.binding().isEmpty()); |
|
200 QVERIFY(!p.hasNotifySignal()); |
|
201 continue; |
|
202 } |
|
203 |
|
204 QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name().toUtf8().constData())); |
|
205 |
|
206 QCOMPARE(p.name(), QString::fromUtf8(pmeta.name())); |
|
207 |
|
208 if (pmeta.type() < QVariant::UserType) // TODO test complex types |
|
209 QCOMPARE(p.value(), pmeta.read(o)); |
|
210 |
|
211 if (p.name() == "parent") |
|
212 QVERIFY(p.valueTypeName() == "QGraphicsObject*" || p.valueTypeName() == "QDeclarativeItem*"); |
|
213 else |
|
214 QCOMPARE(p.valueTypeName(), QString::fromUtf8(pmeta.typeName())); |
|
215 |
|
216 QDeclarativeAbstractBinding *binding = |
|
217 QDeclarativePropertyPrivate::binding(QDeclarativeProperty(o, p.name())); |
|
218 if (binding) |
|
219 QCOMPARE(binding->expression(), p.binding()); |
|
220 |
|
221 QCOMPARE(p.hasNotifySignal(), pmeta.hasNotifySignal()); |
|
222 |
|
223 QVERIFY(pmeta.isValid()); |
|
224 } |
|
225 } |
|
226 |
|
227 void tst_QDeclarativeDebug::recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const |
|
228 { |
|
229 QCOMPARE(a.debugId(), b.debugId()); |
|
230 QCOMPARE(a.className(), b.className()); |
|
231 QCOMPARE(a.name(), b.name()); |
|
232 QCOMPARE(a.contextDebugId(), b.contextDebugId()); |
|
233 |
|
234 QCOMPARE(a.source().url(), b.source().url()); |
|
235 QCOMPARE(a.source().lineNumber(), b.source().lineNumber()); |
|
236 QCOMPARE(a.source().columnNumber(), b.source().columnNumber()); |
|
237 |
|
238 QCOMPARE(a.properties().count(), b.properties().count()); |
|
239 QCOMPARE(a.children().count(), b.children().count()); |
|
240 |
|
241 QList<QDeclarativeDebugPropertyReference> aprops = a.properties(); |
|
242 QList<QDeclarativeDebugPropertyReference> bprops = b.properties(); |
|
243 |
|
244 for (int i=0; i<aprops.count(); i++) |
|
245 compareProperties(aprops[i], bprops[i]); |
|
246 |
|
247 for (int i=0; i<a.children().count(); i++) |
|
248 recursiveCompareObjects(a.children()[i], b.children()[i]); |
|
249 } |
|
250 |
|
251 void tst_QDeclarativeDebug::recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const |
|
252 { |
|
253 QCOMPARE(a.debugId(), b.debugId()); |
|
254 QCOMPARE(a.name(), b.name()); |
|
255 QCOMPARE(a.objects().count(), b.objects().count()); |
|
256 QCOMPARE(a.contexts().count(), b.contexts().count()); |
|
257 |
|
258 for (int i=0; i<a.objects().count(); i++) |
|
259 recursiveCompareObjects(a.objects()[i], b.objects()[i]); |
|
260 |
|
261 for (int i=0; i<a.contexts().count(); i++) |
|
262 recursiveCompareContexts(a.contexts()[i], b.contexts()[i]); |
|
263 } |
|
264 |
|
265 void tst_QDeclarativeDebug::compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const |
|
266 { |
|
267 QCOMPARE(a.objectDebugId(), b.objectDebugId()); |
|
268 QCOMPARE(a.name(), b.name()); |
|
269 QCOMPARE(a.value(), b.value()); |
|
270 QCOMPARE(a.valueTypeName(), b.valueTypeName()); |
|
271 QCOMPARE(a.binding(), b.binding()); |
|
272 QCOMPARE(a.hasNotifySignal(), b.hasNotifySignal()); |
|
273 } |
|
274 |
|
275 void tst_QDeclarativeDebug::initTestCase() |
|
276 { |
|
277 qRegisterMetaType<QDeclarativeDebugWatch::State>(); |
|
278 |
|
279 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Waiting for connection on port 3768..."); |
|
280 qputenv("QML_DEBUG_SERVER_PORT", "3768"); |
|
281 m_engine = new QDeclarativeEngine(this); |
|
282 |
|
283 QList<QByteArray> qml; |
|
284 qml << "import Qt 4.7\n" |
|
285 "Item {" |
|
286 "width: 10; height: 20; scale: blueRect.scale;" |
|
287 "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }" |
|
288 "Text { color: blueRect.color; }" |
|
289 "MouseArea {" |
|
290 "onEntered: { console.log('hello') }" |
|
291 "}" |
|
292 "}"; |
|
293 // add second component to test multiple root contexts |
|
294 qml << "import Qt 4.7\n" |
|
295 "Item {}"; |
|
296 |
|
297 for (int i=0; i<qml.count(); i++) { |
|
298 QDeclarativeComponent component(m_engine); |
|
299 component.setData(qml[i], QUrl::fromLocalFile("")); |
|
300 Q_ASSERT(component.isReady()); // fails if bad syntax |
|
301 m_components << qobject_cast<QDeclarativeItem*>(component.create()); |
|
302 } |
|
303 m_rootItem = qobject_cast<QDeclarativeItem*>(m_components.first()); |
|
304 |
|
305 // add an extra context to test for multiple contexts |
|
306 QDeclarativeContext *context = new QDeclarativeContext(m_engine->rootContext(), this); |
|
307 context->setObjectName("tst_QDeclarativeDebug_childContext"); |
|
308 |
|
309 m_conn = new QDeclarativeDebugConnection(this); |
|
310 m_conn->connectToHost("127.0.0.1", 3768); |
|
311 |
|
312 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Connection established"); |
|
313 bool ok = m_conn->waitForConnected(); |
|
314 Q_ASSERT(ok); |
|
315 QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient()); |
|
316 |
|
317 m_dbg = new QDeclarativeEngineDebug(m_conn, this); |
|
318 } |
|
319 |
|
320 void tst_QDeclarativeDebug::cleanupTestCase() |
|
321 { |
|
322 qDeleteAll(m_components); |
|
323 } |
|
324 |
|
325 void tst_QDeclarativeDebug::watch_property() |
|
326 { |
|
327 QDeclarativeDebugObjectReference obj = findRootObject(); |
|
328 QDeclarativeDebugPropertyReference prop = findProperty(obj.properties(), "width"); |
|
329 |
|
330 QDeclarativeDebugPropertyWatch *watch; |
|
331 |
|
332 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
333 watch = unconnected->addWatch(prop, this); |
|
334 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead); |
|
335 delete watch; |
|
336 delete unconnected; |
|
337 |
|
338 watch = m_dbg->addWatch(QDeclarativeDebugPropertyReference(), this); |
|
339 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)))); |
|
340 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive); |
|
341 delete watch; |
|
342 |
|
343 watch = m_dbg->addWatch(prop, this); |
|
344 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting); |
|
345 QCOMPARE(watch->objectDebugId(), obj.debugId()); |
|
346 QCOMPARE(watch->name(), prop.name()); |
|
347 |
|
348 QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant))); |
|
349 |
|
350 int origWidth = m_rootItem->property("width").toInt(); |
|
351 m_rootItem->setProperty("width", origWidth*2); |
|
352 |
|
353 // stateChanged() is received before valueChanged() |
|
354 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)))); |
|
355 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active); |
|
356 QCOMPARE(spy.count(), 1); |
|
357 |
|
358 m_dbg->removeWatch(watch); |
|
359 delete watch; |
|
360 |
|
361 // restore original value and verify spy doesn't get additional signal since watch has been removed |
|
362 m_rootItem->setProperty("width", origWidth); |
|
363 QTest::qWait(100); |
|
364 QCOMPARE(spy.count(), 1); |
|
365 |
|
366 QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name().toUtf8()); |
|
367 QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2)); |
|
368 } |
|
369 |
|
370 void tst_QDeclarativeDebug::watch_object() |
|
371 { |
|
372 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
373 waitForQuery(q_engines); |
|
374 |
|
375 Q_ASSERT(q_engines->engines().count() > 0); |
|
376 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); |
|
377 waitForQuery(q_context); |
|
378 |
|
379 Q_ASSERT(q_context->rootContext().objects().count() > 0); |
|
380 QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this); |
|
381 waitForQuery(q_obj); |
|
382 |
|
383 QDeclarativeDebugObjectReference obj = q_obj->object(); |
|
384 |
|
385 delete q_engines; |
|
386 delete q_context; |
|
387 delete q_obj; |
|
388 |
|
389 QDeclarativeDebugWatch *watch; |
|
390 |
|
391 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
392 watch = unconnected->addWatch(obj, this); |
|
393 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead); |
|
394 delete watch; |
|
395 delete unconnected; |
|
396 |
|
397 watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), this); |
|
398 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)))); |
|
399 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive); |
|
400 delete watch; |
|
401 |
|
402 watch = m_dbg->addWatch(obj, this); |
|
403 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting); |
|
404 QCOMPARE(watch->objectDebugId(), obj.debugId()); |
|
405 |
|
406 QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant))); |
|
407 |
|
408 int origWidth = m_rootItem->property("width").toInt(); |
|
409 int origHeight = m_rootItem->property("height").toInt(); |
|
410 m_rootItem->setProperty("width", origWidth*2); |
|
411 m_rootItem->setProperty("height", origHeight*2); |
|
412 |
|
413 // stateChanged() is received before any valueChanged() signals |
|
414 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)))); |
|
415 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active); |
|
416 QVERIFY(spy.count() > 0); |
|
417 |
|
418 int newWidth = -1; |
|
419 int newHeight = -1; |
|
420 for (int i=0; i<spy.count(); i++) { |
|
421 const QVariantList &values = spy[i]; |
|
422 if (values[0].value<QByteArray>() == "width") |
|
423 newWidth = values[1].value<QVariant>().toInt(); |
|
424 else if (values[0].value<QByteArray>() == "height") |
|
425 newHeight = values[1].value<QVariant>().toInt(); |
|
426 |
|
427 } |
|
428 |
|
429 m_dbg->removeWatch(watch); |
|
430 delete watch; |
|
431 |
|
432 // since watch has been removed, restoring the original values should not trigger a valueChanged() |
|
433 spy.clear(); |
|
434 m_rootItem->setProperty("width", origWidth); |
|
435 m_rootItem->setProperty("height", origHeight); |
|
436 QTest::qWait(100); |
|
437 QCOMPARE(spy.count(), 0); |
|
438 |
|
439 QCOMPARE(newWidth, origWidth * 2); |
|
440 QCOMPARE(newHeight, origHeight * 2); |
|
441 } |
|
442 |
|
443 void tst_QDeclarativeDebug::watch_expression() |
|
444 { |
|
445 QFETCH(QString, expr); |
|
446 QFETCH(int, increment); |
|
447 QFETCH(int, incrementCount); |
|
448 |
|
449 int origWidth = m_rootItem->property("width").toInt(); |
|
450 |
|
451 QDeclarativeDebugObjectReference obj = findRootObject(); |
|
452 |
|
453 QDeclarativeDebugObjectExpressionWatch *watch; |
|
454 |
|
455 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
456 watch = unconnected->addWatch(obj, expr, this); |
|
457 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead); |
|
458 delete watch; |
|
459 delete unconnected; |
|
460 |
|
461 watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), expr, this); |
|
462 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)))); |
|
463 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive); |
|
464 delete watch; |
|
465 |
|
466 watch = m_dbg->addWatch(obj, expr, this); |
|
467 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting); |
|
468 QCOMPARE(watch->objectDebugId(), obj.debugId()); |
|
469 QCOMPARE(watch->expression(), expr); |
|
470 |
|
471 QSignalSpy spyState(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))); |
|
472 |
|
473 QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant))); |
|
474 int expectedSpyCount = incrementCount + 1; // should also get signal with expression's initial value |
|
475 |
|
476 int width = origWidth; |
|
477 for (int i=0; i<incrementCount+1; i++) { |
|
478 if (i > 0) { |
|
479 width += increment; |
|
480 m_rootItem->setProperty("width", width); |
|
481 } |
|
482 if (!QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(valueChanged(QByteArray,QVariant)))) |
|
483 QFAIL("Did not receive valueChanged() for expression"); |
|
484 } |
|
485 |
|
486 if (spyState.count() == 0) |
|
487 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)))); |
|
488 QCOMPARE(spyState.count(), 1); |
|
489 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active); |
|
490 |
|
491 m_dbg->removeWatch(watch); |
|
492 delete watch; |
|
493 |
|
494 // restore original value and verify spy doesn't get a signal since watch has been removed |
|
495 m_rootItem->setProperty("width", origWidth); |
|
496 QTest::qWait(100); |
|
497 QCOMPARE(spy.count(), expectedSpyCount); |
|
498 |
|
499 width = origWidth + increment; |
|
500 for (int i=0; i<spy.count(); i++) { |
|
501 QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width); |
|
502 width += increment; |
|
503 } |
|
504 } |
|
505 |
|
506 void tst_QDeclarativeDebug::watch_expression_data() |
|
507 { |
|
508 QTest::addColumn<QString>("expr"); |
|
509 QTest::addColumn<int>("increment"); |
|
510 QTest::addColumn<int>("incrementCount"); |
|
511 |
|
512 QTest::newRow("width") << "width" << 0 << 0; |
|
513 QTest::newRow("width+10") << "width + 10" << 10 << 5; |
|
514 } |
|
515 |
|
516 void tst_QDeclarativeDebug::watch_context() |
|
517 { |
|
518 QDeclarativeDebugContextReference c; |
|
519 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented"); |
|
520 QVERIFY(!m_dbg->addWatch(c, QString(), this)); |
|
521 } |
|
522 |
|
523 void tst_QDeclarativeDebug::watch_file() |
|
524 { |
|
525 QDeclarativeDebugFileReference f; |
|
526 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented"); |
|
527 QVERIFY(!m_dbg->addWatch(f, this)); |
|
528 } |
|
529 |
|
530 void tst_QDeclarativeDebug::queryAvailableEngines() |
|
531 { |
|
532 QDeclarativeDebugEnginesQuery *q_engines; |
|
533 |
|
534 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
535 q_engines = unconnected->queryAvailableEngines(0); |
|
536 QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Error); |
|
537 delete q_engines; |
|
538 delete unconnected; |
|
539 |
|
540 q_engines = m_dbg->queryAvailableEngines(this); |
|
541 delete q_engines; |
|
542 |
|
543 q_engines = m_dbg->queryAvailableEngines(this); |
|
544 QVERIFY(q_engines->engines().isEmpty()); |
|
545 waitForQuery(q_engines); |
|
546 |
|
547 // TODO test multiple engines |
|
548 QList<QDeclarativeDebugEngineReference> engines = q_engines->engines(); |
|
549 QCOMPARE(engines.count(), 1); |
|
550 |
|
551 foreach(const QDeclarativeDebugEngineReference &e, engines) { |
|
552 QCOMPARE(e.debugId(), QDeclarativeDebugService::idForObject(m_engine)); |
|
553 QCOMPARE(e.name(), m_engine->objectName()); |
|
554 } |
|
555 |
|
556 delete q_engines; |
|
557 } |
|
558 |
|
559 void tst_QDeclarativeDebug::queryRootContexts() |
|
560 { |
|
561 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
562 waitForQuery(q_engines); |
|
563 int engineId = q_engines->engines()[0].debugId(); |
|
564 |
|
565 QDeclarativeDebugRootContextQuery *q_context; |
|
566 |
|
567 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
568 q_context = unconnected->queryRootContexts(engineId, this); |
|
569 QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Error); |
|
570 delete q_context; |
|
571 delete unconnected; |
|
572 |
|
573 q_context = m_dbg->queryRootContexts(engineId, this); |
|
574 delete q_context; |
|
575 |
|
576 q_context = m_dbg->queryRootContexts(engineId, this); |
|
577 waitForQuery(q_context); |
|
578 |
|
579 QDeclarativeContext *actualContext = m_engine->rootContext(); |
|
580 QDeclarativeDebugContextReference context = q_context->rootContext(); |
|
581 QCOMPARE(context.debugId(), QDeclarativeDebugService::idForObject(actualContext)); |
|
582 QCOMPARE(context.name(), actualContext->objectName()); |
|
583 |
|
584 QCOMPARE(context.objects().count(), 2); // 2 qml component objects created for context in main() |
|
585 |
|
586 // root context query sends only root object data - it doesn't fill in |
|
587 // the children or property info |
|
588 QCOMPARE(context.objects()[0].properties().count(), 0); |
|
589 QCOMPARE(context.objects()[0].children().count(), 0); |
|
590 |
|
591 QCOMPARE(context.contexts().count(), 1); |
|
592 QVERIFY(context.contexts()[0].debugId() >= 0); |
|
593 QCOMPARE(context.contexts()[0].name(), QString("tst_QDeclarativeDebug_childContext")); |
|
594 |
|
595 delete q_engines; |
|
596 delete q_context; |
|
597 } |
|
598 |
|
599 void tst_QDeclarativeDebug::queryObject() |
|
600 { |
|
601 QFETCH(bool, recursive); |
|
602 |
|
603 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
604 waitForQuery(q_engines); |
|
605 |
|
606 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); |
|
607 waitForQuery(q_context); |
|
608 QDeclarativeDebugObjectReference rootObject = q_context->rootContext().objects()[0]; |
|
609 |
|
610 QDeclarativeDebugObjectQuery *q_obj = 0; |
|
611 |
|
612 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
613 q_obj = recursive ? unconnected->queryObjectRecursive(rootObject, this) : unconnected->queryObject(rootObject, this); |
|
614 QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Error); |
|
615 delete q_obj; |
|
616 delete unconnected; |
|
617 |
|
618 q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this); |
|
619 delete q_obj; |
|
620 |
|
621 q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this); |
|
622 waitForQuery(q_obj); |
|
623 |
|
624 QDeclarativeDebugObjectReference obj = q_obj->object(); |
|
625 |
|
626 delete q_engines; |
|
627 delete q_context; |
|
628 delete q_obj; |
|
629 |
|
630 // check source as defined in main() |
|
631 QDeclarativeDebugFileReference source = obj.source(); |
|
632 QCOMPARE(source.url(), QUrl::fromLocalFile("")); |
|
633 QCOMPARE(source.lineNumber(), 2); |
|
634 QCOMPARE(source.columnNumber(), 1); |
|
635 |
|
636 // generically test all properties, children and childrens' properties |
|
637 recursiveObjectTest(m_rootItem, obj, recursive); |
|
638 |
|
639 if (recursive) { |
|
640 foreach(const QDeclarativeDebugObjectReference &child, obj.children()) |
|
641 QVERIFY(child.properties().count() > 0); |
|
642 |
|
643 QDeclarativeDebugObjectReference rect; |
|
644 QDeclarativeDebugObjectReference text; |
|
645 foreach (const QDeclarativeDebugObjectReference &child, obj.children()) { |
|
646 if (child.className() == "Rectangle") |
|
647 rect = child; |
|
648 else if (child.className() == "Text") |
|
649 text = child; |
|
650 } |
|
651 |
|
652 // test specific property values |
|
653 QCOMPARE(findProperty(rect.properties(), "width").value(), qVariantFromValue(500)); |
|
654 QCOMPARE(findProperty(rect.properties(), "height").value(), qVariantFromValue(600)); |
|
655 QCOMPARE(findProperty(rect.properties(), "color").value(), qVariantFromValue(QColor("blue"))); |
|
656 |
|
657 QCOMPARE(findProperty(text.properties(), "color").value(), qVariantFromValue(QColor("blue"))); |
|
658 |
|
659 } else { |
|
660 foreach(const QDeclarativeDebugObjectReference &child, obj.children()) |
|
661 QCOMPARE(child.properties().count(), 0); |
|
662 } |
|
663 } |
|
664 |
|
665 void tst_QDeclarativeDebug::queryObject_data() |
|
666 { |
|
667 QTest::addColumn<bool>("recursive"); |
|
668 |
|
669 QTest::newRow("non-recursive") << false; |
|
670 QTest::newRow("recursive") << true; |
|
671 } |
|
672 |
|
673 void tst_QDeclarativeDebug::queryExpressionResult() |
|
674 { |
|
675 QFETCH(QString, expr); |
|
676 QFETCH(QVariant, result); |
|
677 |
|
678 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
679 waitForQuery(q_engines); // check immediate deletion is ok |
|
680 |
|
681 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); |
|
682 waitForQuery(q_context); |
|
683 int objectId = q_context->rootContext().objects()[0].debugId(); |
|
684 |
|
685 QDeclarativeDebugExpressionQuery *q_expr; |
|
686 |
|
687 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0); |
|
688 q_expr = unconnected->queryExpressionResult(objectId, expr, this); |
|
689 QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Error); |
|
690 delete q_expr; |
|
691 delete unconnected; |
|
692 |
|
693 q_expr = m_dbg->queryExpressionResult(objectId, expr, this); |
|
694 delete q_expr; |
|
695 |
|
696 q_expr = m_dbg->queryExpressionResult(objectId, expr, this); |
|
697 QCOMPARE(q_expr->expression(), expr); |
|
698 waitForQuery(q_expr); |
|
699 |
|
700 QCOMPARE(q_expr->result(), result); |
|
701 |
|
702 delete q_engines; |
|
703 delete q_context; |
|
704 delete q_expr; |
|
705 } |
|
706 |
|
707 void tst_QDeclarativeDebug::queryExpressionResult_data() |
|
708 { |
|
709 QTest::addColumn<QString>("expr"); |
|
710 QTest::addColumn<QVariant>("result"); |
|
711 |
|
712 QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60); |
|
713 QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500); |
|
714 QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>")); |
|
715 } |
|
716 |
|
717 void tst_QDeclarativeDebug::tst_QDeclarativeDebugFileReference() |
|
718 { |
|
719 QDeclarativeDebugFileReference ref; |
|
720 QVERIFY(ref.url().isEmpty()); |
|
721 QCOMPARE(ref.lineNumber(), -1); |
|
722 QCOMPARE(ref.columnNumber(), -1); |
|
723 |
|
724 ref.setUrl(QUrl("http://test")); |
|
725 QCOMPARE(ref.url(), QUrl("http://test")); |
|
726 ref.setLineNumber(1); |
|
727 QCOMPARE(ref.lineNumber(), 1); |
|
728 ref.setColumnNumber(1); |
|
729 QCOMPARE(ref.columnNumber(), 1); |
|
730 |
|
731 QDeclarativeDebugFileReference copy(ref); |
|
732 QDeclarativeDebugFileReference copyAssign; |
|
733 copyAssign = ref; |
|
734 foreach (const QDeclarativeDebugFileReference &r, (QList<QDeclarativeDebugFileReference>() << copy << copyAssign)) { |
|
735 QCOMPARE(r.url(), ref.url()); |
|
736 QCOMPARE(r.lineNumber(), ref.lineNumber()); |
|
737 QCOMPARE(r.columnNumber(), ref.columnNumber()); |
|
738 } |
|
739 } |
|
740 |
|
741 void tst_QDeclarativeDebug::tst_QDeclarativeDebugEngineReference() |
|
742 { |
|
743 QDeclarativeDebugEngineReference ref; |
|
744 QCOMPARE(ref.debugId(), -1); |
|
745 QVERIFY(ref.name().isEmpty()); |
|
746 |
|
747 ref = QDeclarativeDebugEngineReference(1); |
|
748 QCOMPARE(ref.debugId(), 1); |
|
749 QVERIFY(ref.name().isEmpty()); |
|
750 |
|
751 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
752 waitForQuery(q_engines); |
|
753 ref = q_engines->engines()[0]; |
|
754 delete q_engines; |
|
755 |
|
756 QDeclarativeDebugEngineReference copy(ref); |
|
757 QDeclarativeDebugEngineReference copyAssign; |
|
758 copyAssign = ref; |
|
759 foreach (const QDeclarativeDebugEngineReference &r, (QList<QDeclarativeDebugEngineReference>() << copy << copyAssign)) { |
|
760 QCOMPARE(r.debugId(), ref.debugId()); |
|
761 QCOMPARE(r.name(), ref.name()); |
|
762 } |
|
763 } |
|
764 |
|
765 void tst_QDeclarativeDebug::tst_QDeclarativeDebugObjectReference() |
|
766 { |
|
767 QDeclarativeDebugObjectReference ref; |
|
768 QCOMPARE(ref.debugId(), -1); |
|
769 QCOMPARE(ref.className(), QString()); |
|
770 QCOMPARE(ref.name(), QString()); |
|
771 QCOMPARE(ref.contextDebugId(), -1); |
|
772 QVERIFY(ref.properties().isEmpty()); |
|
773 QVERIFY(ref.children().isEmpty()); |
|
774 |
|
775 QDeclarativeDebugFileReference source = ref.source(); |
|
776 QVERIFY(source.url().isEmpty()); |
|
777 QVERIFY(source.lineNumber() < 0); |
|
778 QVERIFY(source.columnNumber() < 0); |
|
779 |
|
780 ref = QDeclarativeDebugObjectReference(1); |
|
781 QCOMPARE(ref.debugId(), 1); |
|
782 |
|
783 QDeclarativeDebugObjectReference rootObject = findRootObject(); |
|
784 QDeclarativeDebugObjectQuery *query = m_dbg->queryObjectRecursive(rootObject, this); |
|
785 waitForQuery(query); |
|
786 ref = query->object(); |
|
787 delete query; |
|
788 |
|
789 QVERIFY(ref.debugId() >= 0); |
|
790 |
|
791 QDeclarativeDebugObjectReference copy(ref); |
|
792 QDeclarativeDebugObjectReference copyAssign; |
|
793 copyAssign = ref; |
|
794 foreach (const QDeclarativeDebugObjectReference &r, (QList<QDeclarativeDebugObjectReference>() << copy << copyAssign)) |
|
795 recursiveCompareObjects(r, ref); |
|
796 } |
|
797 |
|
798 void tst_QDeclarativeDebug::tst_QDeclarativeDebugContextReference() |
|
799 { |
|
800 QDeclarativeDebugContextReference ref; |
|
801 QCOMPARE(ref.debugId(), -1); |
|
802 QVERIFY(ref.name().isEmpty()); |
|
803 QVERIFY(ref.objects().isEmpty()); |
|
804 QVERIFY(ref.contexts().isEmpty()); |
|
805 |
|
806 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); |
|
807 waitForQuery(q_engines); |
|
808 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); |
|
809 waitForQuery(q_context); |
|
810 |
|
811 ref = q_context->rootContext(); |
|
812 delete q_engines; |
|
813 delete q_context; |
|
814 QVERIFY(ref.debugId() >= 0); |
|
815 |
|
816 QDeclarativeDebugContextReference copy(ref); |
|
817 QDeclarativeDebugContextReference copyAssign; |
|
818 copyAssign = ref; |
|
819 foreach (const QDeclarativeDebugContextReference &r, (QList<QDeclarativeDebugContextReference>() << copy << copyAssign)) |
|
820 recursiveCompareContexts(r, ref); |
|
821 } |
|
822 |
|
823 void tst_QDeclarativeDebug::tst_QDeclarativeDebugPropertyReference() |
|
824 { |
|
825 QDeclarativeDebugObjectReference rootObject = findRootObject(); |
|
826 QDeclarativeDebugObjectQuery *query = m_dbg->queryObject(rootObject, this); |
|
827 waitForQuery(query); |
|
828 QDeclarativeDebugObjectReference obj = query->object(); |
|
829 delete query; |
|
830 |
|
831 QDeclarativeDebugPropertyReference ref = findProperty(obj.properties(), "scale"); |
|
832 QVERIFY(ref.objectDebugId() > 0); |
|
833 QVERIFY(!ref.name().isEmpty()); |
|
834 QVERIFY(!ref.value().isNull()); |
|
835 QVERIFY(!ref.valueTypeName().isEmpty()); |
|
836 QVERIFY(!ref.binding().isEmpty()); |
|
837 QVERIFY(ref.hasNotifySignal()); |
|
838 |
|
839 QDeclarativeDebugPropertyReference copy(ref); |
|
840 QDeclarativeDebugPropertyReference copyAssign; |
|
841 copyAssign = ref; |
|
842 foreach (const QDeclarativeDebugPropertyReference &r, (QList<QDeclarativeDebugPropertyReference>() << copy << copyAssign)) |
|
843 compareProperties(r, ref); |
|
844 } |
|
845 |
|
846 QTEST_MAIN(tst_QDeclarativeDebug) |
|
847 |
|
848 #include "tst_qdeclarativedebug.moc" |