1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module 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 | |
29 | #include <QtTest/QTest> |
30 | #include <Qt3DAnimation/qchannelmapping.h> |
31 | #include <Qt3DAnimation/private/qabstractchannelmapping_p.h> |
32 | #include <Qt3DAnimation/private/qchannelmapping_p.h> |
33 | #include <Qt3DAnimation/private/qchannelmappingcreatedchange_p.h> |
34 | #include <Qt3DCore/qentity.h> |
35 | #include <Qt3DCore/qnodecreatedchange.h> |
36 | #include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> |
37 | #include <QObject> |
38 | #include <QSignalSpy> |
39 | #include <QQuaternion> |
40 | #include <testpostmanarbiter.h> |
41 | |
42 | class tst_QTargetEntity : public Qt3DCore::QEntity |
43 | { |
44 | Q_OBJECT |
45 | Q_PROPERTY(QQuaternion rotation MEMBER m_rotation NOTIFY rotationChanged) |
46 | Q_PROPERTY(QVector3D translation MEMBER m_translation NOTIFY translationChanged) |
47 | Q_PROPERTY(QVector3D scale MEMBER m_scale NOTIFY scaleChanged) |
48 | Q_PROPERTY(float floatProperty MEMBER m_floatProperty NOTIFY floatPropertyChanged) |
49 | Q_PROPERTY(QVector2D vec2Property MEMBER m_vec2Property NOTIFY vec2PropertyChanged) |
50 | Q_PROPERTY(QVector3D vec3Property MEMBER m_vec3Property NOTIFY vec3PropertyChanged) |
51 | Q_PROPERTY(QVector4D vec4Property MEMBER m_vec4Property NOTIFY vec4PropertyChanged) |
52 | Q_PROPERTY(QQuaternion quaternionProperty MEMBER m_quaternionProperty NOTIFY quaternionPropertyChanged) |
53 | Q_PROPERTY(QVariantList listProperty MEMBER m_listProperty NOTIFY listPropertyChanged) |
54 | Q_PROPERTY(QVector<float> vecProperty MEMBER m_vecProperty NOTIFY vecPropertyChanged) |
55 | |
56 | signals: |
57 | void rotationChanged(); |
58 | void translationChanged(); |
59 | void scaleChanged(); |
60 | void floatPropertyChanged(); |
61 | void vec2PropertyChanged(); |
62 | void vec3PropertyChanged(); |
63 | void vec4PropertyChanged(); |
64 | void quaternionPropertyChanged(); |
65 | void listPropertyChanged(); |
66 | void vecPropertyChanged(); |
67 | |
68 | private: |
69 | QQuaternion m_rotation; |
70 | QVector3D m_translation; |
71 | QVector3D m_scale; |
72 | float m_floatProperty; |
73 | QVector2D m_vec2Property; |
74 | QVector3D m_vec3Property; |
75 | QVector4D m_vec4Property; |
76 | QQuaternion m_quaternionProperty; |
77 | QVariantList m_listProperty; |
78 | QVector<float> m_vecProperty; |
79 | }; |
80 | |
81 | |
82 | class tst_QChannelMapping : public QObject |
83 | { |
84 | Q_OBJECT |
85 | |
86 | private Q_SLOTS: |
87 | void checkDefaultConstruction() |
88 | { |
89 | // GIVEN |
90 | Qt3DAnimation::QChannelMapping mapping; |
91 | |
92 | // THEN |
93 | QCOMPARE(mapping.channelName(), QString()); |
94 | QCOMPARE(mapping.target(), static_cast<Qt3DCore::QNode *>(nullptr)); |
95 | QCOMPARE(mapping.property(), QString()); |
96 | |
97 | const Qt3DAnimation::QChannelMappingPrivate *d = |
98 | static_cast<const Qt3DAnimation::QChannelMappingPrivate *>( |
99 | Qt3DAnimation::QChannelMappingPrivate::get(q: &mapping)); |
100 | |
101 | QCOMPARE(d->m_type, static_cast<int>(QVariant::Invalid)); |
102 | QCOMPARE(d->m_componentCount, 0); |
103 | } |
104 | |
105 | void checkPropertyChanges() |
106 | { |
107 | // GIVEN |
108 | Qt3DAnimation::QChannelMapping mapping; |
109 | |
110 | { |
111 | // WHEN |
112 | QSignalSpy spy(&mapping, SIGNAL(channelNameChanged(QString))); |
113 | const QString newValue(QStringLiteral("Rotation" )); |
114 | mapping.setChannelName(newValue); |
115 | |
116 | // THEN |
117 | QVERIFY(spy.isValid()); |
118 | QCOMPARE(mapping.channelName(), newValue); |
119 | QCOMPARE(spy.count(), 1); |
120 | |
121 | // WHEN |
122 | spy.clear(); |
123 | mapping.setChannelName(newValue); |
124 | |
125 | // THEN |
126 | QCOMPARE(mapping.channelName(), newValue); |
127 | QCOMPARE(spy.count(), 0); |
128 | } |
129 | |
130 | { |
131 | // WHEN |
132 | QSignalSpy spy(&mapping, SIGNAL(targetChanged(Qt3DCore::QNode*))); |
133 | auto newValue = new Qt3DCore::QEntity(); |
134 | mapping.setTarget(newValue); |
135 | |
136 | // THEN |
137 | QVERIFY(spy.isValid()); |
138 | QCOMPARE(mapping.target(), newValue); |
139 | QCOMPARE(newValue->parent(), &mapping); |
140 | QCOMPARE(spy.count(), 1); |
141 | |
142 | // WHEN |
143 | spy.clear(); |
144 | mapping.setTarget(newValue); |
145 | |
146 | // THEN |
147 | QCOMPARE(mapping.target(), newValue); |
148 | QCOMPARE(spy.count(), 0); |
149 | } |
150 | |
151 | { |
152 | // WHEN |
153 | QSignalSpy spy(&mapping, SIGNAL(propertyChanged(QString))); |
154 | const QString newValue(QStringLiteral("rotation" )); |
155 | mapping.setProperty(newValue); |
156 | |
157 | // THEN |
158 | QVERIFY(spy.isValid()); |
159 | QCOMPARE(mapping.property(), newValue); |
160 | QCOMPARE(spy.count(), 1); |
161 | |
162 | // WHEN |
163 | spy.clear(); |
164 | mapping.setProperty(newValue); |
165 | |
166 | // THEN |
167 | QCOMPARE(mapping.property(), newValue); |
168 | QCOMPARE(spy.count(), 0); |
169 | } |
170 | } |
171 | |
172 | void checkCreationData() |
173 | { |
174 | // GIVEN |
175 | Qt3DAnimation::QChannelMapping mapping; |
176 | auto target = new tst_QTargetEntity; |
177 | |
178 | mapping.setChannelName(QStringLiteral("Location" )); |
179 | mapping.setTarget(target); |
180 | mapping.setProperty(QStringLiteral("translation" )); |
181 | |
182 | // WHEN |
183 | QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges; |
184 | |
185 | { |
186 | Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&mapping); |
187 | creationChanges = creationChangeGenerator.creationChanges(); |
188 | } |
189 | |
190 | // THEN |
191 | { |
192 | QCOMPARE(creationChanges.size(), 2); // 1 for mapping, 1 for target |
193 | |
194 | const auto creationChangeData = qSharedPointerCast<Qt3DAnimation::QChannelMappingCreatedChange<Qt3DAnimation::QChannelMappingData>>(src: creationChanges.first()); |
195 | const Qt3DAnimation::QChannelMappingData data = creationChangeData->data; |
196 | |
197 | QCOMPARE(mapping.id(), creationChangeData->subjectId()); |
198 | QCOMPARE(mapping.isEnabled(), true); |
199 | QCOMPARE(mapping.isEnabled(), creationChangeData->isNodeEnabled()); |
200 | QCOMPARE(mapping.metaObject(), creationChangeData->metaObject()); |
201 | QCOMPARE(creationChangeData->type(), Qt3DAnimation::QChannelMappingCreatedChangeBase::ChannelMapping); |
202 | QCOMPARE(mapping.channelName(), data.channelName); |
203 | QCOMPARE(mapping.target()->id(), data.targetId); |
204 | QVERIFY(qstrcmp(mapping.property().toLatin1().constData(), data.propertyName) == 0); |
205 | QCOMPARE(data.type, static_cast<int>(QVariant::Vector3D)); |
206 | QCOMPARE(data.componentCount, 3); |
207 | } |
208 | |
209 | // WHEN |
210 | mapping.setEnabled(false); |
211 | |
212 | { |
213 | Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&mapping); |
214 | creationChanges = creationChangeGenerator.creationChanges(); |
215 | } |
216 | |
217 | // THEN |
218 | { |
219 | QCOMPARE(creationChanges.size(), 2); // 1 for mapping, 1 for target |
220 | |
221 | const auto creationChangeData = qSharedPointerCast<Qt3DAnimation::QChannelMappingCreatedChange<Qt3DAnimation::QChannelMappingData>>(src: creationChanges.first()); |
222 | const Qt3DAnimation::QChannelMappingData data = creationChangeData->data; |
223 | |
224 | QCOMPARE(mapping.id(), creationChangeData->subjectId()); |
225 | QCOMPARE(mapping.isEnabled(), false); |
226 | QCOMPARE(mapping.isEnabled(), creationChangeData->isNodeEnabled()); |
227 | QCOMPARE(mapping.metaObject(), creationChangeData->metaObject()); |
228 | QCOMPARE(creationChangeData->type(), Qt3DAnimation::QChannelMappingCreatedChangeBase::ChannelMapping); |
229 | QCOMPARE(mapping.channelName(), data.channelName); |
230 | QCOMPARE(mapping.target()->id(), data.targetId); |
231 | QVERIFY(qstrcmp(mapping.property().toLatin1().constData(), data.propertyName) == 0); |
232 | QCOMPARE(data.type, static_cast<int>(QVariant::Vector3D)); |
233 | QCOMPARE(data.componentCount, 3); |
234 | } |
235 | } |
236 | |
237 | void checkPropertyUpdateChanges() |
238 | { |
239 | // GIVEN |
240 | TestArbiter arbiter; |
241 | Qt3DAnimation::QChannelMapping mapping; |
242 | QScopedPointer<Qt3DCore::QEntity> target(new tst_QTargetEntity()); |
243 | arbiter.setArbiterOnNode(&mapping); |
244 | |
245 | { |
246 | // WHEN |
247 | mapping.setChannelName(QStringLiteral("Scale" )); |
248 | |
249 | // THEN |
250 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
251 | QCOMPARE(arbiter.dirtyNodes.front(), &mapping); |
252 | |
253 | arbiter.dirtyNodes.clear(); |
254 | |
255 | // WHEN |
256 | mapping.setChannelName(QStringLiteral("Scale" )); |
257 | |
258 | // THEN |
259 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
260 | } |
261 | |
262 | { |
263 | // WHEN |
264 | mapping.setTarget(target.data()); |
265 | |
266 | // THEN |
267 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
268 | QCOMPARE(arbiter.dirtyNodes.front(), &mapping); |
269 | |
270 | arbiter.dirtyNodes.clear(); |
271 | |
272 | // WHEN |
273 | mapping.setTarget(target.data()); |
274 | |
275 | // THEN |
276 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
277 | } |
278 | |
279 | { |
280 | // WHEN |
281 | target->setProperty(name: "scale" , value: QVector3D(1.0f, 0.0f, 0.0f)); |
282 | mapping.setProperty(QStringLiteral("scale" )); |
283 | QCoreApplication::processEvents(); |
284 | |
285 | // THEN |
286 | QCOMPARE(arbiter.events.size(), 0); |
287 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
288 | QCOMPARE(arbiter.dirtyNodes.front(), &mapping); |
289 | |
290 | arbiter.dirtyNodes.clear(); |
291 | |
292 | // WHEN |
293 | mapping.setProperty(QStringLiteral("scale" )); |
294 | QCoreApplication::processEvents(); |
295 | |
296 | // THEN |
297 | QCOMPARE(arbiter.events.size(), 0); |
298 | QCOMPARE(arbiter.dirtyNodes.size(), 0); |
299 | } |
300 | } |
301 | |
302 | void checkPropertyUpdateNameTypeAndComponentCount_data() |
303 | { |
304 | QTest::addColumn<QByteArray>(name: "propertyName" ); |
305 | QTest::addColumn<QVariant>(name: "value" ); |
306 | QTest::addColumn<int>(name: "expectedType" ); |
307 | QTest::addColumn<int>(name: "expectedComponentCount" ); |
308 | |
309 | QTest::newRow(dataTag: "float" ) << QByteArrayLiteral("floatProperty" ) << QVariant(1.0f) << static_cast<int>(QMetaType::Float) << 1; |
310 | QTest::newRow(dataTag: "vec2" ) << QByteArrayLiteral("vec2Property" ) << QVariant(QVector2D(1.0f, 1.0f)) << static_cast<int>(QVariant::Vector2D) << 2; |
311 | QTest::newRow(dataTag: "vec3" ) << QByteArrayLiteral("vec3Property" ) << QVariant(QVector3D(1.0f, 1.0f, 1.0f)) << static_cast<int>(QVariant::Vector3D) << 3; |
312 | QTest::newRow(dataTag: "vec4" ) << QByteArrayLiteral("vec4Property" ) << QVariant(QVector4D(1.0f, 1.0f, 1.0f, 1.0f)) << static_cast<int>(QVariant::Vector4D) << 4; |
313 | QTest::newRow(dataTag: "quaternion" ) << QByteArrayLiteral("quaternionProperty" ) << QVariant(QQuaternion(1.0f, 1.0f, 1.0f, 1.0f)) << static_cast<int>(QVariant::Quaternion) << 4; |
314 | |
315 | QVariantList list = QVariantList() << QVariant(1.0f) << QVariant(1.0) << QVariant(1.0f) << QVariant(1.0f) << QVariant(1.0f); |
316 | QTest::newRow(dataTag: "variantlist" ) << QByteArrayLiteral("listProperty" ) << QVariant::fromValue(value: list) << static_cast<int>(QVariant::List) << 5; |
317 | |
318 | QVector<float> vec(8); |
319 | QTest::newRow(dataTag: "vector" ) << QByteArrayLiteral("vecProperty" ) << QVariant::fromValue(value: vec) << qMetaTypeId<decltype(vec)>() << 8; |
320 | } |
321 | |
322 | void checkPropertyUpdateNameTypeAndComponentCount() |
323 | { |
324 | // GIVEN |
325 | QFETCH(QByteArray, propertyName); |
326 | QFETCH(QVariant, value); |
327 | QFETCH(int, expectedType); |
328 | QFETCH(int, expectedComponentCount); |
329 | |
330 | Q_UNUSED(expectedType) |
331 | Q_UNUSED(expectedComponentCount) |
332 | |
333 | TestArbiter arbiter; |
334 | Qt3DAnimation::QChannelMapping mapping; |
335 | QScopedPointer<Qt3DCore::QEntity> target(new tst_QTargetEntity()); |
336 | mapping.setTarget(target.data()); |
337 | arbiter.setArbiterOnNode(&mapping); |
338 | |
339 | { |
340 | // WHEN |
341 | target->setProperty(name: propertyName.constData(), value); |
342 | |
343 | // THEN |
344 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
345 | QCOMPARE(arbiter.dirtyNodes.front(), target.data()); |
346 | |
347 | arbiter.dirtyNodes.clear(); |
348 | |
349 | // THEN |
350 | mapping.setProperty(QString::fromLatin1(str: propertyName)); |
351 | |
352 | // THEN |
353 | QCOMPARE(arbiter.dirtyNodes.size(), 1); |
354 | } |
355 | } |
356 | |
357 | }; |
358 | |
359 | QTEST_MAIN(tst_QChannelMapping) |
360 | |
361 | #include "tst_qchannelmapping.moc" |
362 | |