1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <QtTest/QtTest>
29#include <qsignalspy.h>
30#include <QtQml/qqmlengine.h>
31#include <QtQml/qqmlcomponent.h>
32#include <QtQuick/qquickview.h>
33#include <QtQuick/private/qquickrectangle_p.h>
34#include <QtQuick/private/qquicktext_p.h>
35#include <QtQuick/private/qquickbehavior_p.h>
36#include <QtQuick/private/qquickanimation_p.h>
37#include <QtQuick/private/qquicksmoothedanimation_p.h>
38#include <private/qquickitem_p.h>
39#include "../../shared/util.h"
40
41class tst_qquickbehaviors : public QQmlDataTest
42{
43 Q_OBJECT
44public:
45 tst_qquickbehaviors() {}
46
47private slots:
48 void init() { qApp->processEvents(); } //work around animation timer bug (QTBUG-22865)
49 void simpleBehavior();
50 void scriptTriggered();
51 void cppTriggered();
52 void loop();
53 void colorBehavior();
54 void parentBehavior();
55 void replaceBinding();
56 //void transitionOverrides();
57 void group();
58 void valueType();
59 void emptyBehavior();
60 void explicitSelection();
61 void nonSelectingBehavior();
62 void reassignedAnimation();
63 void disabled();
64 void dontStart();
65 void startup();
66 void groupedPropertyCrash();
67 void runningTrue();
68 void sameValue();
69 void delayedRegistration();
70 void startOnCompleted();
71 void multipleChangesToValueType();
72 void currentValue();
73 void disabledWriteWhileRunning();
74 void aliasedProperty();
75 void innerBehaviorOverwritten();
76 void oneWay();
77 void safeToDelete();
78 void targetProperty();
79};
80
81void tst_qquickbehaviors::simpleBehavior()
82{
83 QQmlEngine engine;
84 QQmlComponent c(&engine, testFileUrl(fileName: "simple.qml"));
85 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
86 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
87 QTRY_VERIFY(qobject_cast<QQuickBehavior*>(rect->findChild<QQuickBehavior*>("MyBehavior"))->animation());
88
89 QQuickItemPrivate::get(item: rect.data())->setState("moved");
90 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() > 0);
91 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() < 200);
92 //i.e. the behavior has been triggered
93}
94
95void tst_qquickbehaviors::scriptTriggered()
96{
97 QQmlEngine engine;
98 QQmlComponent c(&engine, testFileUrl(fileName: "scripttrigger.qml"));
99 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
100 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
101
102 rect->setColor(QColor("red"));
103 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() > 0);
104 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() < 200);
105 //i.e. the behavior has been triggered
106}
107
108void tst_qquickbehaviors::cppTriggered()
109{
110 QQmlEngine engine;
111 QQmlComponent c(&engine, testFileUrl(fileName: "cpptrigger.qml"));
112 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
113 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
114
115 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
116 QTRY_VERIFY(innerRect);
117
118 innerRect->setProperty(name: "x", value: 200);
119 QTRY_VERIFY(innerRect->x() > 0);
120 QTRY_VERIFY(innerRect->x() < 200); //i.e. the behavior has been triggered
121}
122
123void tst_qquickbehaviors::loop()
124{
125 QQmlEngine engine;
126 QQmlComponent c(&engine, testFileUrl(fileName: "loop.qml"));
127 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
128 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
129
130 //don't crash
131 QQuickItemPrivate::get(item: rect.data())->setState("moved");
132}
133
134void tst_qquickbehaviors::colorBehavior()
135{
136 QQmlEngine engine;
137 QQmlComponent c(&engine, testFileUrl(fileName: "color.qml"));
138 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
139 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
140
141 QQuickItemPrivate::get(item: rect.data())->setState("red");
142 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->color() != QColor("red"));
143 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->color() != QColor("green"));
144 //i.e. the behavior has been triggered
145}
146
147void tst_qquickbehaviors::parentBehavior()
148{
149 QQmlEngine engine;
150 QQmlComponent c(&engine, testFileUrl(fileName: "parent.qml"));
151 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
152 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
153
154 QQuickItemPrivate::get(item: rect.data())->setState("reparented");
155 QTRY_VERIFY(rect->findChild<QQuickRectangle*>("MyRect")->parentItem() != rect->findChild<QQuickItem*>("NewParent"));
156 QTRY_VERIFY(rect->findChild<QQuickRectangle*>("MyRect")->parentItem() == rect->findChild<QQuickItem*>("NewParent"));
157}
158
159void tst_qquickbehaviors::replaceBinding()
160{
161 QQmlEngine engine;
162 QQmlComponent c(&engine, testFileUrl(fileName: "binding.qml"));
163 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
164 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
165
166 QQuickItemPrivate::get(item: rect.data())->setState("moved");
167 QQuickRectangle *innerRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"));
168 QTRY_VERIFY(innerRect);
169 QTRY_VERIFY(innerRect->x() > 0);
170 QTRY_VERIFY(innerRect->x() < 200);
171 //i.e. the behavior has been triggered
172 QTRY_COMPARE(innerRect->x(), (qreal)200);
173 rect->setProperty(name: "basex", value: 10);
174 QTRY_COMPARE(innerRect->x(), (qreal)200);
175 rect->setProperty(name: "movedx", value: 210);
176 QTRY_COMPARE(innerRect->x(), (qreal)210);
177
178 QQuickItemPrivate::get(item: rect.data())->setState("");
179 QTRY_VERIFY(innerRect->x() > 10);
180 QTRY_VERIFY(innerRect->x() < 210); //i.e. the behavior has been triggered
181 QTRY_COMPARE(innerRect->x(), (qreal)10);
182 rect->setProperty(name: "movedx", value: 200);
183 QTRY_COMPARE(innerRect->x(), (qreal)10);
184 rect->setProperty(name: "basex", value: 20);
185 QTRY_COMPARE(innerRect->x(), (qreal)20);
186}
187
188void tst_qquickbehaviors::group()
189{
190 /* XXX TODO Create a test element for this case.
191 {
192 QQmlEngine engine;
193 QQmlComponent c(&engine, testFileUrl("groupProperty.qml")));
194 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create()));;
195 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
196
197 QQuickItemPrivate::get(rect.data())->setState("moved");
198 //QTest::qWait(200);
199 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() > 0);
200 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() < 200);
201 //i.e. the behavior has been triggered
202 }
203 */
204
205 {
206 QQmlEngine engine;
207 QQmlComponent c(&engine, testFileUrl(fileName: "groupProperty2.qml"));
208 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
209 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
210
211 QQuickItemPrivate::get(item: rect.data())->setState("moved");
212 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->border()->width() > 0);
213 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->border()->width() < 4);
214 //i.e. the behavior has been triggered
215 }
216}
217
218void tst_qquickbehaviors::valueType()
219{
220 QQmlEngine engine;
221 QQmlComponent c(&engine, testFileUrl(fileName: "valueType.qml"));
222 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
223 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
224
225 //QTBUG-20827
226 QCOMPARE(rect->color(), QColor::fromRgb(255,0,255));
227}
228
229void tst_qquickbehaviors::emptyBehavior()
230{
231 QQmlEngine engine;
232 QQmlComponent c(&engine, testFileUrl(fileName: "empty.qml"));
233 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
234 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
235
236 QQuickItemPrivate::get(item: rect.data())->setState("moved");
237 qreal x = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"))->x();
238 QCOMPARE(x, qreal(200)); //should change immediately
239}
240
241void tst_qquickbehaviors::explicitSelection()
242{
243 QQmlEngine engine;
244 QQmlComponent c(&engine, testFileUrl(fileName: "explicit.qml"));
245 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
246 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
247
248 QQuickItemPrivate::get(item: rect.data())->setState("moved");
249 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() > 0);
250 QTRY_VERIFY(qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("MyRect"))->x() < 200);
251 //i.e. the behavior has been triggered
252}
253
254void tst_qquickbehaviors::nonSelectingBehavior()
255{
256 QQmlEngine engine;
257 QQmlComponent c(&engine, testFileUrl(fileName: "nonSelecting2.qml"));
258 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
259 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
260
261 QQuickItemPrivate::get(item: rect.data())->setState("moved");
262 qreal x = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"))->x();
263 QCOMPARE(x, qreal(200)); //should change immediately
264}
265
266void tst_qquickbehaviors::reassignedAnimation()
267{
268 QQmlEngine engine;
269 QQmlComponent c(&engine, testFileUrl(fileName: "reassignedAnimation.qml"));
270 QString warning = testFileUrl(fileName: "reassignedAnimation.qml").toString() + ":9:9: QML Behavior: Cannot change the animation assigned to a Behavior.";
271 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
272 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
273 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
274 QCOMPARE(qobject_cast<QQuickNumberAnimation*>(
275 rect->findChild<QQuickBehavior*>("MyBehavior")->animation())->duration(), 200);
276}
277
278void tst_qquickbehaviors::disabled()
279{
280 QQmlEngine engine;
281 QQmlComponent c(&engine, testFileUrl(fileName: "disabled.qml"));
282 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
283 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
284 QCOMPARE(rect->findChild<QQuickBehavior*>("MyBehavior")->enabled(), false);
285
286 QQuickItemPrivate::get(item: rect.data())->setState("moved");
287 qreal x = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRect"))->x();
288 QCOMPARE(x, qreal(200)); //should change immediately
289}
290
291void tst_qquickbehaviors::dontStart()
292{
293 QQmlEngine engine;
294
295 QQmlComponent c(&engine, testFileUrl(fileName: "dontStart.qml"));
296
297 QString warning = c.url().toString() + ":13:13: QML NumberAnimation: setRunning() cannot be used on non-root animation nodes.";
298 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
299 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
300 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
301
302 QQuickAbstractAnimation *myAnim = rect->findChild<QQuickAbstractAnimation*>(aName: "MyAnim");
303 QVERIFY(myAnim);
304 QVERIFY(!myAnim->qtAnimation());
305}
306
307void tst_qquickbehaviors::startup()
308{
309 {
310 QQmlEngine engine;
311 QQmlComponent c(&engine, testFileUrl(fileName: "startup.qml"));
312 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
313 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
314
315 QQuickRectangle *innerRect = rect->findChild<QQuickRectangle*>(aName: "innerRect");
316 QVERIFY(innerRect);
317
318 QCOMPARE(innerRect->x(), qreal(100)); //should be set immediately
319 }
320
321 {
322 QQmlEngine engine;
323 QQmlComponent c(&engine, testFileUrl(fileName: "startup2.qml"));
324 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
325 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
326
327 QQuickRectangle *innerRect = rect->findChild<QQuickRectangle*>(aName: "innerRect");
328 QVERIFY(innerRect);
329
330 QQuickText *text = rect->findChild<QQuickText*>();
331 QVERIFY(text);
332
333 QCOMPARE(innerRect->x(), text->width()); //should be set immediately
334 }
335}
336
337//QTBUG-10799
338void tst_qquickbehaviors::groupedPropertyCrash()
339{
340 QQmlEngine engine;
341 QQmlComponent c(&engine, testFileUrl(fileName: "groupedPropertyCrash.qml"));
342 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
343 QVERIFY2(!rect.isNull(), qPrintable(c.errorString())); //don't crash
344}
345
346//QTBUG-5491
347void tst_qquickbehaviors::runningTrue()
348{
349 QQmlEngine engine;
350 QQmlComponent c(&engine, testFileUrl(fileName: "runningTrue.qml"));
351 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
352 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
353
354 QQuickAbstractAnimation *animation = rect->findChild<QQuickAbstractAnimation*>(aName: "rotAnim");
355 QVERIFY(animation);
356
357 QSignalSpy runningSpy(animation, SIGNAL(runningChanged(bool)));
358 rect->setProperty(name: "myValue", value: 180);
359 QTRY_VERIFY(runningSpy.count() > 0);
360}
361
362//QTBUG-12295
363void tst_qquickbehaviors::sameValue()
364{
365 QQmlEngine engine;
366 QQmlComponent c(&engine, testFileUrl(fileName: "qtbug12295.qml"));
367 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
368 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
369
370 QQuickRectangle *target = rect->findChild<QQuickRectangle*>(aName: "myRect");
371 QVERIFY(target);
372
373 target->setX(100);
374 QCOMPARE(target->x(), qreal(100));
375
376 target->setProperty(name: "x", value: 0);
377 QTRY_VERIFY(target->x() != qreal(0) && target->x() != qreal(100));
378 QTRY_VERIFY(target->x() == qreal(0)); //make sure Behavior has finished.
379
380 target->setX(100);
381 QCOMPARE(target->x(), qreal(100));
382
383 //this is the main point of the test -- the behavior needs to be triggered again
384 //even though we set 0 twice in a row.
385 target->setProperty(name: "x", value: 0);
386 QTRY_VERIFY(target->x() != qreal(0) && target->x() != qreal(100));
387}
388
389//QTBUG-18362
390void tst_qquickbehaviors::delayedRegistration()
391{
392 QQmlEngine engine;
393
394 QQmlComponent c(&engine, testFileUrl(fileName: "delayedRegistration.qml"));
395 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
396 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
397
398 QQuickItem *innerRect = rect->property(name: "myItem").value<QQuickItem*>();
399 QVERIFY(innerRect != nullptr);
400
401 QCOMPARE(innerRect->property("x").toInt(), int(0));
402
403 QTRY_COMPARE(innerRect->property("x").toInt(), int(100));
404}
405
406//QTBUG-22555
407void tst_qquickbehaviors::startOnCompleted()
408{
409 QQmlEngine engine;
410
411 QQmlComponent c(&engine, testFileUrl(fileName: "startOnCompleted.qml"));
412 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
413 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
414
415 QQuickItem *innerRect = rect->findChild<QQuickRectangle*>();
416 QVERIFY(innerRect != nullptr);
417
418 QCOMPARE(innerRect->property("x").toInt(), int(0));
419
420 QTRY_COMPARE(innerRect->property("x").toInt(), int(100));
421}
422
423//QTBUG-25139
424void tst_qquickbehaviors::multipleChangesToValueType()
425{
426 QQmlEngine engine;
427
428 QQmlComponent c(&engine, testFileUrl(fileName: "multipleChangesToValueType.qml"));
429 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle *>(object: c.create()));
430 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
431
432 QQuickText *text = rect->findChild<QQuickText *>();
433 QVERIFY(text != nullptr);
434
435 QFont value;
436 value.setPointSize(24);
437 QCOMPARE(text->property("font").value<QFont>(), value);
438
439 QVERIFY(QMetaObject::invokeMethod(rect.data(), "updateFontProperties"));
440
441 value.setItalic(true);
442 value.setWeight(QFont::Bold);
443 QCOMPARE(text->property("font").value<QFont>(), value);
444
445 value.setPointSize(48);
446 QTRY_COMPARE(text->property("font").value<QFont>(), value);
447}
448
449//QTBUG-21549
450void tst_qquickbehaviors::currentValue()
451{
452 {
453 QQmlEngine engine;
454 QQmlComponent c(&engine, testFileUrl(fileName: "qtbug21549.qml"));
455 QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(object: c.create()));
456 QVERIFY2(!item.isNull(), qPrintable(c.errorString()));
457
458 QQuickRectangle *target = item->findChild<QQuickRectangle*>(aName: "myRect");
459 QVERIFY(target);
460
461 QCOMPARE(target->x(), qreal(0));
462
463 target->setProperty(name: "x", value: 50);
464 QCOMPARE(item->property("behaviorCount").toInt(), 1);
465 QCOMPARE(target->x(), qreal(50));
466
467 target->setProperty(name: "x", value: 50);
468 QCOMPARE(item->property("behaviorCount").toInt(), 1);
469 QCOMPARE(target->x(), qreal(50));
470
471 target->setX(100);
472 target->setProperty(name: "x", value: 100);
473 QCOMPARE(item->property("behaviorCount").toInt(), 1);
474 QCOMPARE(target->x(), qreal(100));
475 }
476
477 {
478 QQmlEngine engine;
479 QQmlComponent c(&engine, testFileUrl(fileName: "qtbug21549-2.qml"));
480 QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(object: c.create()));
481 QVERIFY2(!item.isNull(), qPrintable(c.errorString()));
482
483 QQuickRectangle *target = item->findChild<QQuickRectangle*>(aName: "myRect");
484 QVERIFY(target);
485
486 QCOMPARE(target->x(), qreal(0));
487
488 target->setProperty(name: "x", value: 100);
489
490 // the spring animation should smoothly transition to the new value triggered
491 // in the QML (which should be between 50 and 80);
492 QTRY_COMPARE(item->property("animRunning").toBool(), true);
493 QTRY_COMPARE(item->property("animRunning").toBool(), false);
494 QVERIFY2(target->x() > qreal(50) && target->x() < qreal(80), QByteArray::number(target->x()));
495 }
496}
497
498void tst_qquickbehaviors::disabledWriteWhileRunning()
499{
500 {
501 QQmlEngine engine;
502 QQmlComponent c(&engine, testFileUrl(fileName: "disabledWriteWhileRunning.qml"));
503 QScopedPointer<QQuickItem> root(qobject_cast<QQuickItem*>(object: c.create()));
504 QVERIFY2(!root.isNull(), qPrintable(c.errorString()));
505
506 QQuickRectangle *myRect = qobject_cast<QQuickRectangle*>(object: root->findChild<QQuickRectangle*>(aName: "MyRect"));
507 QQuickBehavior *myBehavior = qobject_cast<QQuickBehavior*>(object: root->findChild<QQuickBehavior*>(aName: "MyBehavior"));
508 QQuickNumberAnimation *myAnimation = qobject_cast<QQuickNumberAnimation*>(object: root->findChild<QQuickNumberAnimation*>(aName: "MyAnimation"));
509 QVERIFY(myRect);
510 QVERIFY(myBehavior);
511 QVERIFY(myAnimation);
512
513 // initial values
514 QCOMPARE(myBehavior->enabled(), true);
515 QCOMPARE(myAnimation->isRunning(), false);
516 QCOMPARE(myRect->x(), qreal(0));
517
518 // start animation
519 myRect->setProperty(name: "x", value: 200);
520 QCOMPARE(myAnimation->isRunning(), true);
521 QTRY_VERIFY(myRect->x() != qreal(0) && myRect->x() != qreal(200));
522 QCOMPARE(myBehavior->targetValue(), 200); // grabbed before starting the animation
523
524 // set disabled while animation is in progress
525 myBehavior->setProperty(name: "enabled", value: false);
526 QCOMPARE(myAnimation->isRunning(), true);
527
528 // force new value while disabled and previous animation is in progress.
529 // animation should be stopped and value stay at forced value
530 myRect->setProperty(name: "x", value: 100);
531 QCOMPARE(myAnimation->isRunning(), false);
532 QCOMPARE(myRect->x(), qreal(100));
533 QCOMPARE(myBehavior->targetValue(), 100);
534 QTest::qWait(ms: 200);
535 QCOMPARE(myRect->x(), qreal(100));
536 }
537
538 //test additional complications with SmoothedAnimation
539 {
540 QQmlEngine engine;
541 QQmlComponent c(&engine, testFileUrl(fileName: "disabledWriteWhileRunning2.qml"));
542 QScopedPointer<QQuickItem> root(qobject_cast<QQuickItem*>(object: c.create()));
543 QVERIFY2(!root.isNull(), qPrintable(c.errorString()));
544
545 QQuickRectangle *myRect = qobject_cast<QQuickRectangle*>(object: root->findChild<QQuickRectangle*>(aName: "MyRect"));
546 QQuickBehavior *myBehavior = qobject_cast<QQuickBehavior*>(object: root->findChild<QQuickBehavior*>(aName: "MyBehavior"));
547 QQuickSmoothedAnimation *myAnimation = qobject_cast<QQuickSmoothedAnimation*>(object: root->findChild<QQuickNumberAnimation*>(aName: "MyAnimation"));
548 QVERIFY(myRect);
549 QVERIFY(myBehavior);
550 QVERIFY(myAnimation);
551
552 // initial values
553 QCOMPARE(myBehavior->enabled(), true);
554 QCOMPARE(myBehavior->targetValue(), QVariant());
555 QCOMPARE(myAnimation->isRunning(), false);
556 QCOMPARE(myRect->x(), qreal(0));
557
558 // start animation
559 myRect->setProperty(name: "x", value: 200);
560 QCOMPARE(myAnimation->isRunning(), true);
561 QCOMPARE(myBehavior->targetValue(), 200);
562 QTRY_VERIFY(myRect->x() != qreal(0) && myRect->x() != qreal(200));
563
564 //set second value
565 myRect->setProperty(name: "x", value: 300);
566 QCOMPARE(myAnimation->isRunning(), true);
567 QCOMPARE(myBehavior->targetValue(), 300);
568 QTRY_VERIFY(myRect->x() != qreal(0) && myRect->x() != qreal(200));
569
570 // set disabled while animation is in progress
571 myBehavior->setProperty(name: "enabled", value: false);
572 QCOMPARE(myAnimation->isRunning(), true);
573
574 // force new value while disabled and previous animation is in progress.
575 // animation should be stopped and value stay at forced value
576 myRect->setProperty(name: "x", value: 100);
577 QCOMPARE(myAnimation->isRunning(), false);
578 QCOMPARE(myRect->x(), qreal(100));
579 QCOMPARE(myBehavior->targetValue(), 100);
580 QTest::qWait(ms: 200);
581 QCOMPARE(myRect->x(), qreal(100));
582 }
583}
584
585void tst_qquickbehaviors::aliasedProperty()
586{
587 QQmlEngine engine;
588 QQmlComponent c(&engine, testFileUrl(fileName: "aliased.qml"));
589 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));
590 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
591
592 QQuickBehavior* behavior =
593 qobject_cast<QQuickBehavior*>(object: rect->findChild<QQuickBehavior*>(aName: "behavior"));
594 QSignalSpy targetValueSpy(behavior, SIGNAL(targetValueChanged()));
595 QQuickItemPrivate::get(item: rect.data())->setState("moved");
596 QCOMPARE(behavior->targetValue(), 400);
597 QCOMPARE(targetValueSpy.count(), 1);
598 QScopedPointer<QQuickRectangle> acc(qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "acc")));
599 QScopedPointer<QQuickRectangle> range(qobject_cast<QQuickRectangle*>(object: acc->findChild<QQuickRectangle*>(aName: "range")));
600 QTRY_VERIFY(acc->property("value").toDouble() > 0);
601 QTRY_VERIFY(range->width() > 0);
602 QTRY_VERIFY(acc->property("value").toDouble() < 400);
603 QTRY_VERIFY(range->width() < 400);
604 //i.e. the behavior has been triggered
605}
606
607void tst_qquickbehaviors::innerBehaviorOverwritten()
608{
609 QQmlEngine engine;
610 QQmlComponent c(&engine, testFileUrl(fileName: "overwrittenbehavior.qml"));
611 QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(object: c.create()));
612 QQuickBehavior* behavior =
613 qobject_cast<QQuickBehavior*>(object: item->findChild<QQuickBehavior*>(aName: "behavior"));
614 QVERIFY(item->property("behaviorTriggered").toBool());
615 QCOMPARE(behavior->targetValue(), true);
616}
617
618void tst_qquickbehaviors::oneWay()
619{
620 QQmlEngine engine;
621 QQmlComponent c(&engine, testFileUrl(fileName: "oneway.qml"));
622 QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(object: c.create()));;
623 QVERIFY2(!rect.isNull(), qPrintable(c.errorString()));
624 QQuickBehavior* behavior =
625 qobject_cast<QQuickBehavior*>(object: rect->findChild<QQuickBehavior*>(aName: "MyBehaviorOneWay"));
626 QCOMPARE(behavior->enabled(), false);
627 QCOMPARE(behavior->targetValue(), QVariant());
628
629 QSignalSpy targetValueSpy(behavior, SIGNAL(targetValueChanged()));
630 QQuickRectangle *myRect = qobject_cast<QQuickRectangle*>(object: rect->findChild<QQuickRectangle*>(aName: "MyRectOneWay"));
631 myRect->setProperty(name: "x", value: 100);
632 QCOMPARE(behavior->targetValue(), 100);
633 QCOMPARE(targetValueSpy.count(), 1);
634 QCOMPARE(behavior->enabled(), false);
635 qreal x = myRect->x();
636 QCOMPARE(x, qreal(100)); //should change immediately
637 QQuickNumberAnimation *myAnimation =
638 qobject_cast<QQuickNumberAnimation*>(object: behavior->findChild<QQuickNumberAnimation*>(aName: "MyAnimationOneWay"));
639 QCOMPARE(myAnimation->isRunning(), false);
640
641 myRect->setProperty(name: "x", value: 0);
642 QCOMPARE(behavior->targetValue(), 0);
643 QCOMPARE(targetValueSpy.count(), 2);
644 QCOMPARE(behavior->enabled(), true);
645 QCOMPARE(myAnimation->isRunning(), true);
646 QVERIFY(myRect->x() > 0.0);
647 QTRY_VERIFY(myRect->x() < 100.0);
648 QTRY_COMPARE(myRect->x(), qreal(0));
649 QCOMPARE(myAnimation->isRunning(), false);
650}
651
652// QTBUG-76749
653void tst_qquickbehaviors::safeToDelete()
654{
655 QQmlEngine engine;
656 QQmlComponent c(&engine, testFileUrl(fileName: "delete.qml"));
657 QVERIFY(c.create());
658}
659
660Q_DECLARE_METATYPE(QQmlProperty)
661void tst_qquickbehaviors::targetProperty()
662{
663 QQmlEngine engine;
664 QQmlComponent c(&engine, testFileUrl(fileName: "targetProperty.qml"));
665 QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(object: c.create()));
666 QVERIFY2(!item.isNull(), qPrintable(c.errorString()));
667
668 QQuickBehavior* xBehavior =
669 qobject_cast<QQuickBehavior*>(object: item->findChild<QQuickBehavior*>(aName: "xBehavior"));
670 QCOMPARE(xBehavior->property("targetProperty").value<QQmlProperty>(), QQmlProperty(item.data(), "x"));
671 QCOMPARE(item->property("xBehaviorObject").value<QObject*>(), item.data());
672 QCOMPARE(item->property("xBehaviorName").toString(), "x");
673
674 QQuickBehavior* emptyBehavior =
675 qobject_cast<QQuickBehavior*>(object: item->findChild<QQuickBehavior*>(aName: "emptyBehavior"));
676 QCOMPARE(emptyBehavior->property("targetProperty").value<QQmlProperty>().isValid(), false);
677 QCOMPARE(item->property("emptyBehaviorObject").value<QObject*>(), nullptr);
678 QCOMPARE(item->property("emptyBehaviorName").toString(), "");
679}
680
681
682
683QTEST_MAIN(tst_qquickbehaviors)
684
685#include "tst_qquickbehaviors.moc"
686

source code of qtdeclarative/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp