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 <qtest.h>
29#include <QtQml/qqmlengine.h>
30#include <QtQml/qqmlcomponent.h>
31#include <private/qqmldata_p.h>
32#include <private/qqmlbinding_p.h>
33#include <QtQuick/private/qquicktext_p.h>
34#include <QtQuick/private/qquickrectangle_p.h>
35#include "../../shared/util.h"
36#include <qqmlcontext.h>
37
38class tst_bindingdependencyapi : public QObject
39{
40 Q_OBJECT
41public:
42 tst_bindingdependencyapi();
43
44private slots:
45 void testSingleDep_data();
46 void testSingleDep();
47 void testManyDeps_data();
48 void testManyDeps();
49 void testConditionalDependencies_data();
50 void testConditionalDependencies();
51 void testBindingLoop();
52
53private:
54 bool findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value);
55};
56
57tst_bindingdependencyapi::tst_bindingdependencyapi()
58{
59}
60
61
62void tst_bindingdependencyapi::testSingleDep_data()
63{
64 QTest::addColumn<QByteArray>(name: "code");
65 QTest::addColumn<QString>(name: "referencedObjectName");
66
67 QTest::addRow(format: "context property")
68 << QByteArray("import QtQuick 2.0\n"
69 "Rectangle {\n"
70 "objectName: \"rect\"\n"
71 "property string labelText: \"Hello world!\"\n"
72 "Text { text: labelText }\n"
73 "}") << "rect";
74
75 QTest::addRow(format: "scope property")
76 << QByteArray("import QtQuick 2.0\n"
77 "Rectangle {\n"
78 "property string labelText: \"I am wrong!\"\n"
79 "Text {\n"
80 "objectName: \"text\"\n"
81 "property string labelText: \"Hello world!\"\n"
82 "text: labelText\n"
83 "}\n"
84 "}") << "text";
85
86 QTest::addRow(format: "id object property")
87 << QByteArray("import QtQuick 2.0\n"
88 "Rectangle {\n"
89 "id: rect\n"
90 "objectName: \"rect\"\n"
91 "property string labelText: \"Hello world!\"\n"
92 "Text { text: rect.labelText }\n"
93 "}") << "rect";
94
95 QTest::addRow(format: "dynamic context property")
96 << QByteArray("import QtQuick 2.0\n"
97 "Rectangle {\n"
98 "objectName: \"rect\"\n"
99 "property string labelText: \"Hello world!\"\n"
100 "Text { Component.onCompleted: text = Qt.binding(function() { return labelText; }); }\n"
101 "}") << "rect";
102
103 QTest::addRow(format: "dynamic scope property")
104 << QByteArray("import QtQuick 2.0\n"
105 "Rectangle {\n"
106 "property string labelText: \"I am wrong!\"\n"
107 "Text {\n"
108 "objectName: \"text\"\n"
109 "property string labelText: \"Hello world!\"\n"
110 "Component.onCompleted: text = Qt.binding(function() { return labelText; });\n"
111 "}\n"
112 "}") << "text";
113
114 QTest::addRow(format: "dynamic id object property")
115 << QByteArray("import QtQuick 2.0\n"
116 "Rectangle {\n"
117 "id: rect\n"
118 "objectName: \"rect\"\n"
119 "property string labelText: \"Hello world!\"\n"
120 "Text { Component.onCompleted: text = Qt.binding(function() { return rect.labelText; }); }\n"
121 "}") << "rect";
122}
123
124void tst_bindingdependencyapi::testSingleDep()
125{
126 QFETCH(QByteArray, code);
127 QFETCH(QString, referencedObjectName);
128
129 QQmlEngine engine;
130 QQmlComponent c(&engine);
131 c.setData(code, baseUrl: QUrl());
132 QObject *rect = c.create();
133 QTest::qWait(ms: 10);
134 QVERIFY(rect != nullptr);
135 QObject *text = rect->findChildren<QQuickText *>().front();
136
137 QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(aName: referencedObjectName);
138
139 auto data = QQmlData::get(object: text);
140 QVERIFY(data);
141 auto b = data->bindings;
142 QVERIFY(b);
143 auto binding = dynamic_cast<QQmlBinding*>(b);
144 QVERIFY(binding);
145 auto dependencies = binding->dependencies();
146 QCOMPARE(dependencies.size(), 1);
147 auto dependency = dependencies.front();
148 QVERIFY(dependency.isValid());
149 QCOMPARE(quintptr(dependency.object()), quintptr(referencedObject));
150 QCOMPARE(dependency.property().name(), "labelText");
151 QCOMPARE(dependency.read().toString(), QStringLiteral("Hello world!"));
152 QCOMPARE(dependency, QQmlProperty(referencedObject, "labelText"));
153
154 delete rect;
155}
156
157bool tst_bindingdependencyapi::findProperties(const QVector<QQmlProperty> &properties, QObject *obj, const QString &propertyName, const QVariant &value)
158{
159 auto dep = std::find_if(first: properties.cbegin(), last: properties.cend(), pred: [&](const QQmlProperty &dep) {
160 return dep.object() == obj
161 && dep.property().name() == propertyName
162 && dep.read() == value;
163 });
164 if (dep == properties.cend()) {
165 qWarning() << "Searched for property with:" << "{ object:" << obj << ", propertyName:" << propertyName << ", value:" << value << "}" << "but only found:";
166 for (auto dep : properties) {
167 qWarning() << "{ object:" << dep.object() << ", propertyName:" << dep.property().name() << ", value:" << dep.read() << "}";
168 }
169 return false;
170 }
171 return true;
172}
173
174void tst_bindingdependencyapi::testManyDeps_data()
175{
176 QTest::addColumn<QByteArray>(name: "code");
177
178 QTest::addRow(format: "permanent binding")
179 << QByteArray("import QtQuick 2.0\n"
180 "Rectangle {\n"
181 "id: rect\n"
182 "objectName: 'rect'\n"
183 "property string name: 'world'\n"
184 "Text {\n"
185 "text: config.helloWorldTemplate.arg(greeting).arg(rect.name) \n"
186 "property string greeting: 'Hello'\n"
187 "}\n"
188 "QtObject { id: config; objectName: 'config'; property string helloWorldTemplate: '%1 %2!' }\n"
189 "}");
190
191 QTest::addRow(format: "dynamic binding")
192 << QByteArray("import QtQuick 2.0\n"
193 "Rectangle {\n"
194 "id: rect\n"
195 "objectName: 'rect'\n"
196 "property string name: 'world'\n"
197 "Text {\n"
198 "Component.onCompleted: text = Qt.binding(function() { return config.helloWorldTemplate.arg(greeting).arg(rect.name); }); \n"
199 "property string greeting: 'Hello'\n"
200 "}\n"
201 "QtObject { id: config; objectName: 'config'; property string helloWorldTemplate: '%1 %2!' }\n"
202 "}");
203}
204
205void tst_bindingdependencyapi::testManyDeps()
206{
207 QFETCH(QByteArray, code);
208 QQmlEngine engine;
209 QQmlComponent c(&engine);
210 c.setData(code, baseUrl: QUrl());
211 QObject *rect = c.create();
212 if (c.isError()) {
213 qWarning() << c.errorString();
214 }
215 QTest::qWait(ms: 100);
216 QVERIFY(rect != nullptr);
217 QObject *text = rect->findChildren<QQuickText *>().front();
218 QObject *configObj = rect->findChild<QObject *>(aName: "config");
219
220 auto data = QQmlData::get(object: text);
221 QVERIFY(data);
222 auto b = data->bindings;
223 QVERIFY(b);
224 auto binding = dynamic_cast<QQmlBinding*>(b);
225 QVERIFY(binding);
226 auto dependencies = binding->dependencies();
227 QCOMPARE(dependencies.size(), 3);
228
229 QVERIFY(findProperties(dependencies, rect, "name", "world"));
230 QVERIFY(findProperties(dependencies, text, "greeting", "Hello"));
231 QVERIFY(findProperties(dependencies, configObj, "helloWorldTemplate", "%1 %2!"));
232
233 delete rect;
234}
235
236void tst_bindingdependencyapi::testConditionalDependencies_data()
237{
238 QTest::addColumn<QByteArray>(name: "code");
239 QTest::addColumn<QString>(name: "referencedObjectName");
240
241 QTest::addRow(format: "id object property")
242 << QByteArray("import QtQuick 2.0\n"
243 "Rectangle {\n"
244 "id: rect\n"
245 "objectName: \"rect\"\n"
246 "property bool haveDep: false\n"
247 "property string labelText: \"Hello world!\"\n"
248 "Text { text: rect.haveDep ? rect.labelText : '' }\n"
249 "}") << "rect";
250
251 QTest::addRow(format: "dynamic context property")
252 << QByteArray("import QtQuick 2.0\n"
253 "Rectangle {\n"
254 "objectName: \"rect\"\n"
255 "property bool haveDep: false\n"
256 "property string labelText: \"Hello world!\"\n"
257 "Text { Component.onCompleted: text = Qt.binding(function() { return haveDep ? labelText : ''; }); }\n"
258 "}") << "rect";
259
260 QTest::addRow(format: "dynamic scope property")
261 << QByteArray("import QtQuick 2.0\n"
262 "Rectangle {\n"
263 "property string labelText: \"I am wrong!\"\n"
264 "Text {\n"
265 "objectName: \"text\"\n"
266 "property bool haveDep: false\n"
267 "property string labelText: \"Hello world!\"\n"
268 "Component.onCompleted: text = Qt.binding(function() { return haveDep ? labelText : ''; });\n"
269 "}\n"
270 "}") << "text";
271
272 QTest::addRow(format: "dynamic id object property")
273 << QByteArray("import QtQuick 2.0\n"
274 "Rectangle {\n"
275 "id: rect\n"
276 "objectName: \"rect\"\n"
277 "property bool haveDep: false\n"
278 "property string labelText: \"Hello world!\"\n"
279 "Text { Component.onCompleted: text = Qt.binding(function() { return rect.haveDep ? rect.labelText : ''; }); }\n"
280 "}") << "rect";
281}
282
283void tst_bindingdependencyapi::testConditionalDependencies()
284{
285 QFETCH(QByteArray, code);
286 QFETCH(QString, referencedObjectName);
287
288 QQmlEngine engine;
289 QQmlComponent c(&engine);
290 c.setData(code, baseUrl: QUrl());
291 QObject *rect = c.create();
292 QTest::qWait(ms: 10);
293 QVERIFY(rect != nullptr);
294 QObject *text = rect->findChildren<QQuickText *>().front();
295
296 QObject *referencedObject = rect->objectName() == referencedObjectName ? rect : rect->findChild<QObject *>(aName: referencedObjectName);
297
298 auto data = QQmlData::get(object: text);
299 QVERIFY(data);
300 auto b = data->bindings;
301 QVERIFY(b);
302 auto binding = dynamic_cast<QQmlBinding*>(b);
303 QVERIFY(binding);
304 auto dependencies = binding->dependencies();
305 QCOMPARE(dependencies.size(), 1);
306 QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false));
307
308 referencedObject->setProperty(name: "haveDep", value: true);
309 dependencies = binding->dependencies();
310 QCOMPARE(dependencies.size(), 2);
311 QVERIFY(findProperties(dependencies, referencedObject, "haveDep", true));
312 QVERIFY(findProperties(dependencies, referencedObject, "labelText", "Hello world!"));
313
314 referencedObject->setProperty(name: "haveDep", value: false);
315 dependencies = binding->dependencies();
316 QCOMPARE(dependencies.size(), 1);
317 QVERIFY(findProperties(dependencies, referencedObject, "haveDep", false));
318
319 delete rect;
320}
321
322void tst_bindingdependencyapi::testBindingLoop()
323{
324 QQmlEngine engine;
325 QQmlComponent c(&engine);
326 c.setData(QByteArray("import QtQuick 2.0\n"
327 "Rectangle {\n"
328 "property string labelText: label.text\n"
329 "Text {\n"
330 "id: label\n"
331 "text: labelText\n"
332 "}\n"
333 "}"), baseUrl: QUrl());
334 QObject *rect = c.create();
335 if (c.isError()) {
336 qWarning() << c.errorString();
337 }
338 QTest::qWait(ms: 100);
339 QVERIFY(rect != nullptr);
340 QObject *text = rect->findChildren<QQuickText *>().front();
341
342 auto data = QQmlData::get(object: text);
343 QVERIFY(data);
344 auto b = data->bindings;
345 QVERIFY(b);
346 auto binding = dynamic_cast<QQmlBinding*>(b);
347 QVERIFY(binding);
348 auto dependencies = binding->dependencies();
349 QCOMPARE(dependencies.size(), 1);
350 auto dependency = dependencies.front();
351 QVERIFY(dependency.isValid());
352 QCOMPARE(quintptr(dependency.object()), quintptr(rect));
353 QCOMPARE(dependency.property().name(), "labelText");
354
355 delete rect;
356}
357
358QTEST_MAIN(tst_bindingdependencyapi)
359
360#include "tst_bindingdependencyapi.moc"
361

source code of qtdeclarative/tests/auto/qml/bindingdependencyapi/tst_bindingdependencyapi.cpp