Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qpropertytesthelper_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QPROPERTYTESTHELPER_P_H
5#define QPROPERTYTESTHELPER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/QObject>
19#include <QtCore/QProperty>
20#include <QtTest/QSignalSpy>
21#include <QTest>
22#include <private/qglobal_p.h>
23
25
26namespace QTestPrivate {
27
45#define QPROPERTY_TEST_COMPARISON_HELPER(actual, expected, comparator, represent) \
46 do { \
47 const size_t maxMsgLen = 1024; \
48 char msg[maxMsgLen] = { '\0' }; \
49 auto actualStr = represent(actual); \
50 auto expectedStr = represent(expected); \
51 const size_t len1 = mbstowcs(nullptr, #actual, maxMsgLen); \
52 const size_t len2 = mbstowcs(nullptr, #expected, maxMsgLen); \
53 qsnprintf(msg, maxMsgLen, "\n%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s\n", \
54 "Comparison failed!", #actual, qMax(len1, len2) - len1 + 1, ":", \
55 actualStr ? actualStr : "<null>", #expected, qMax(len1, len2) - len2 + 1, ":", \
56 expectedStr ? expectedStr : "<null>"); \
57 delete[] actualStr; \
58 delete[] expectedStr; \
59 QVERIFY2(comparator(actual, expected), msg); \
60 } while (false)
61
96template<typename TestedClass, typename PropertyType>
98 TestedClass &instance, const PropertyType &initial, const PropertyType &changed,
99 const char *propertyName,
100 std::function<bool(const PropertyType &, const PropertyType &)> comparator =
101 [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
102 std::function<char *(const PropertyType &)> represent =
103 [](const PropertyType &val) { return QTest::toString(val); })
104{
105 // get the property
106 const QMetaObject *metaObject = instance.metaObject();
107 QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
108 QVERIFY2(metaProperty.metaType() == QMetaType::fromType<PropertyType>(),
109 QByteArray("Preconditions not met for ") + propertyName + "\n"
110 "The type of initial and changed value does not match the type of the property.\n"
111 "Please ensure that the types match exactly (convertability is not enough).\n"
112 "You can provide the template types to the "
113 "function explicitly to force a certain type.\n"
114 "Expected was a " + metaProperty.metaType().name()
115 + " but " + QMetaType::fromType<PropertyType>().name() + " was provided.");
116
117 // in case the TestedClass has setProperty()/property() methods.
118 QObject &testedObj = static_cast<QObject &>(instance);
119
120 QVERIFY2(metaProperty.isBindable() && metaProperty.isWritable(),
121 "Preconditions not met for " + QByteArray(propertyName));
122
124 if (metaProperty.hasNotifySignal())
125 spy.reset(new QSignalSpy(&instance, metaProperty.notifySignal()));
126
127 testedObj.setProperty(propertyName, QVariant::fromValue(initial));
129 testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
130 represent);
131 if (spy)
132 QCOMPARE(spy->size(), 1);
133
134 QUntypedBindable bindable = metaProperty.bindable(&instance);
135
136 // Bind to the object's property (using both lambda and
137 // Qt:makePropertyBinding).
138 QProperty<PropertyType> propObserver(changed);
139 propObserver.setBinding(bindable.makeBinding());
140 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
141
142 QProperty<PropertyType> propObserverLambda(changed);
143 propObserverLambda.setBinding(
144 [&]() { return testedObj.property(propertyName).template value<PropertyType>(); });
145 QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), initial, comparator, represent);
146
147 testedObj.setProperty(propertyName, QVariant::fromValue(changed));
148 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
149 QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), changed, comparator, represent);
150 if (spy)
151 QCOMPARE(spy->size(), 2);
152
153 // Bind object's property to other property
154 QProperty<PropertyType> propSetter(initial);
155 QVERIFY(!bindable.hasBinding());
156 bindable.setBinding(Qt::makePropertyBinding(propSetter));
157
158 QVERIFY(bindable.hasBinding());
160 testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
161 represent);
162 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
163 QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), initial, comparator, represent);
164 if (spy)
165 QCOMPARE(spy->size(), 3);
166
167 // Count notifications triggered; should only happen on actual change.
168 int updateCount = 0;
169 auto handler = bindable.onValueChanged([&updateCount]() { ++updateCount; });
170 Q_UNUSED(handler)
171
172 propSetter.setValue(changed);
174 testedObj.property(propertyName).template value<PropertyType>(), changed, comparator,
175 represent);
176 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
177 QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), changed, comparator, represent);
178 QCOMPARE(updateCount, 1);
179 if (spy)
180 QCOMPARE(spy->size(), 4);
181
182 // Test that manually setting the value (even the same one) breaks the
183 // binding.
184 testedObj.setProperty(propertyName, QVariant::fromValue(changed));
185 QVERIFY(!bindable.hasBinding());
186 // Setting the same value should have no impact on udpateCount.
187 QCOMPARE(updateCount, 1);
188
189 // value didn't change -> the signal should not be emitted
190 if (spy)
191 QCOMPARE(spy->size(), 4);
192}
193
237template<typename TestedClass, typename PropertyType>
239 TestedClass &instance, const PropertyType &prior, const PropertyType &changed,
240 const char *propertyName,
241 bool bindingPreservedOnWrite = true,
242 std::function<bool(const PropertyType &, const PropertyType &)> comparator =
243 [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
244 std::function<char *(const PropertyType &)> represent =
245 [](const PropertyType &val) { return QTest::toString(val); })
246{
247 // get the property
248 const QMetaObject *metaObject = instance.metaObject();
249 QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
250
251 // in case the TestedClass has setProperty()/property() methods.
252 QObject &testedObj = static_cast<QObject &>(instance);
253
254 QVERIFY2(metaProperty.metaType() == QMetaType::fromType<PropertyType>(),
255 QByteArray("Preconditions not met for ") + propertyName + "\n"
256 "The type of prior and changed value does not match the type of the property.\n"
257 "Please ensure that the types match exactly (convertability is not enough).\n"
258 "You can provide the template types to the "
259 "function explicitly to force a certain type.\n"
260 "Property is " + metaProperty.metaType().name()
261 + " but parameters are " + QMetaType::fromType<PropertyType>().name() + ".\n");
262
263 QVERIFY2(metaProperty.isBindable(), "Preconditions not met for " + QByteArray(propertyName));
264
265 QUntypedBindable bindable = metaProperty.bindable(&instance);
266
268 if (metaProperty.hasNotifySignal())
269 spy.reset(new QSignalSpy(&instance, metaProperty.notifySignal()));
270
272 testedObj.property(propertyName).template value<PropertyType>(), prior, comparator,
273 represent);
274
275 QProperty<PropertyType> propObserver;
276 propObserver.setBinding(bindable.makeBinding());
277 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), prior, comparator, represent);
278
279 // Create a binding that sets the 'changed' value to the property
280 QProperty<PropertyType> propSetter(changed);
281 QVERIFY(!bindable.hasBinding());
282 bindable.setBinding(Qt::makePropertyBinding(propSetter));
283 QVERIFY(bindable.hasBinding());
284
286 testedObj.property(propertyName).template value<PropertyType>(), changed, comparator,
287 represent);
288 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
289 if (spy)
290 QCOMPARE(spy->size(), 1);
291
292 // Attempt to set back the 'prior' value and verify that it has no effect
293 testedObj.setProperty(propertyName, QVariant::fromValue(prior));
295 testedObj.property(propertyName).template value<PropertyType>(), changed, comparator,
296 represent);
297 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
298 if (spy)
299 QCOMPARE(spy->size(), 1);
300 if (bindingPreservedOnWrite)
301 QVERIFY(bindable.hasBinding());
302 else
303 QVERIFY(!bindable.hasBinding());
304}
305
306
341template<typename TestedClass, typename PropertyType>
343 TestedClass &instance, const PropertyType &initial, const PropertyType &changed,
344 const char *propertyName,
345 std::function<void()> mutator = []() { QFAIL("Data modifier function must be provided"); },
346 std::function<bool(const PropertyType &, const PropertyType &)> comparator =
347 [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
348 std::function<char *(const PropertyType &)> represent =
349 [](const PropertyType &val) { return QTest::toString(val); })
350{
351 // get the property
352 const QMetaObject *metaObject = instance.metaObject();
353 QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
354
355 // in case the TestedClass has setProperty()/property() methods.
356 QObject &testedObj = static_cast<QObject &>(instance);
357
358 QVERIFY2(metaProperty.metaType() == QMetaType::fromType<PropertyType>(),
359 QByteArray("Preconditions not met for ") + propertyName + "\n"
360 "The type of initial and changed value does not match the type of the property.\n"
361 "Please ensure that the types match exactly (convertability is not enough).\n"
362 "You can provide the template types to the "
363 "function explicitly to force a certain type.\n"
364 "Expected was a " + metaProperty.metaType().name()
365 + " but " + QMetaType::fromType<PropertyType>().name() + " was provided.");
366
367 QVERIFY2(metaProperty.isBindable(), "Preconditions not met for " + QByteArray(propertyName));
368
369 QUntypedBindable bindable = metaProperty.bindable(&instance);
370
372 if (metaProperty.hasNotifySignal())
373 spy.reset(new QSignalSpy(&instance, metaProperty.notifySignal()));
374
376 testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
377 represent);
378
379 // Check that attempting to bind this read-only property to another property has no effect:
380 QProperty<PropertyType> propSetter(initial);
381 QVERIFY(!bindable.hasBinding());
382 bindable.setBinding(Qt::makePropertyBinding(propSetter));
383 QVERIFY(!bindable.hasBinding());
384 propSetter.setValue(changed);
386 testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
387 represent);
388 if (spy)
389 QCOMPARE(spy->size(), 0);
390
391 QProperty<PropertyType> propObserver;
392 propObserver.setBinding(bindable.makeBinding());
393
394 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
395
396 // Invoke mutator function. Now property value should be changed.
397 mutator();
398
400 testedObj.property(propertyName).template value<PropertyType>(), changed, comparator,
401 represent);
402 QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
403
404 if (spy)
405 QCOMPARE(spy->size(), 1);
406}
407
408} // namespace QTestPrivate
409
411
412#endif // QPROPERTYTESTHELPER_P_H
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Definition qlist.h:386
\inmodule QtCore
bool isBindable() const
QMetaType metaType() const
QMetaMethod notifySignal() const
bool isWritable() const
Returns true if this property is writable; otherwise returns false.
QUntypedBindable bindable(QObject *object) const
bool hasNotifySignal() const
Returns true if this property has a corresponding change notify signal; otherwise returns false.
constexpr const char * name() const
Definition qmetatype.h:2650
\inmodule QtCore
Definition qobject.h:90
QVariant property(const char *name) const
Returns the value of the object's name property.
Definition qobject.cpp:4187
bool setProperty(const char *name, const QVariant &value)
Sets the value of the object's name property to value.
\inmodule QtCore
Definition qproperty.h:349
parameter_type value() const
Returns the value of the property.
Definition qproperty.h:384
QPropertyBinding< T > setBinding(const QPropertyBinding< T > &newBinding)
Associates the value of this property with the provided newBinding expression and returns the previou...
Definition qproperty.h:442
\inmodule QtCore
\inmodule QtCore
Definition qproperty.h:677
QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location=QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
Creates a binding returning the underlying properties' value, using a specified source location.
Definition qproperty.h:701
bool hasBinding() const
Returns true if the underlying property has a binding.
Definition qproperty.h:785
bool setBinding(const QUntypedPropertyBinding &binding)
Sets the underlying property's binding to binding.
Definition qproperty.h:766
QPropertyChangeHandler< Functor > onValueChanged(Functor f) const
Installs f as a change handler.
Definition qproperty.h:733
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
QSignalSpy spy(myCustomObject, SIGNAL(mySignal(int, QString, double)))
[0]
Combined button and popup list for selecting options.
void testWriteOncePropertyBasics(TestedClass &instance, const PropertyType &prior, const PropertyType &changed, const char *propertyName, bool bindingPreservedOnWrite=true, std::function< bool(const PropertyType &, const PropertyType &)> comparator=[](const PropertyType &lhs, const PropertyType &rhs) { return lhs==rhs;}, std::function< char *(const PropertyType &)> represent=[](const PropertyType &val) { return QTest::toString(val);})
void testReadOnlyPropertyBasics(TestedClass &instance, const PropertyType &initial, const PropertyType &changed, const char *propertyName, std::function< void()> mutator=[]() { QFAIL("Data modifier function must be provided");}, std::function< bool(const PropertyType &, const PropertyType &)> comparator=[](const PropertyType &lhs, const PropertyType &rhs) { return lhs==rhs;}, std::function< char *(const PropertyType &)> represent=[](const PropertyType &val) { return QTest::toString(val);})
void testReadWritePropertyBasics(TestedClass &instance, const PropertyType &initial, const PropertyType &changed, const char *propertyName, std::function< bool(const PropertyType &, const PropertyType &)> comparator=[](const PropertyType &lhs, const PropertyType &rhs) { return lhs==rhs;}, std::function< char *(const PropertyType &)> represent=[](const PropertyType &val) { return QTest::toString(val);})
char * toString(const MyPoint &point)
auto makePropertyBinding(Functor &&f, const QPropertyBindingSourceLocation &location=QT_PROPERTY_DEFAULT_BINDING_LOCATION, std::enable_if_t< std::is_invocable_v< Functor > > *=nullptr)
Definition qproperty.h:212
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLuint GLfloat * val
#define QPROPERTY_TEST_COMPARISON_HELPER(actual, expected, comparator, represent)
#define QFAIL(message)
Definition qtestcase.h:35
#define QCOMPARE(actual, expected)
Definition qtestcase.h:52
#define QVERIFY(statement)
Definition qtestcase.h:29
#define QVERIFY2(statement, description)
Definition qtestcase.h:41
#define Q_UNUSED(x)
obj metaObject() -> className()
\inmodule QtCore