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
29#include <qtest.h>
30
31#include <QList>
32#include <QByteArray>
33#include <private/qquickopenglshadereffect_p.h>
34#include <QMatrix4x4>
35#include <QtQuick/QQuickView>
36#include <QtQml/QQmlEngine>
37#include "../../shared/util.h"
38
39class TestShaderEffect : public QQuickShaderEffect
40{
41 Q_OBJECT
42 Q_PROPERTY(QVariant source READ dummyRead NOTIFY sourceChanged)
43 Q_PROPERTY(QVariant _0aA9zZ READ dummyRead NOTIFY dummyChanged)
44 Q_PROPERTY(QVariant x86 READ dummyRead NOTIFY dummyChanged)
45 Q_PROPERTY(QVariant X READ dummyRead NOTIFY dummyChanged)
46 Q_PROPERTY(QMatrix4x4 mat4x4 READ mat4x4Read NOTIFY dummyChanged)
47
48public:
49 TestShaderEffect(QQuickItem* parent = nullptr) : QQuickShaderEffect(parent)
50 {
51 }
52
53 QMatrix4x4 mat4x4Read() const { return QMatrix4x4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1); }
54 QVariant dummyRead() const { return QVariant(); }
55
56 int signalsConnected = 0;
57
58protected:
59 void connectNotify(const QMetaMethod &) override { ++signalsConnected; }
60 void disconnectNotify(const QMetaMethod &) override { --signalsConnected; }
61
62signals:
63 void dummyChanged();
64 void sourceChanged();
65
66private:
67};
68
69class tst_qquickshadereffect : public QQmlDataTest
70{
71 Q_OBJECT
72public:
73 tst_qquickshadereffect();
74
75private slots:
76 void initTestCase();
77 void cleanupTestCase();
78
79 void lookThroughShaderCode_data();
80 void lookThroughShaderCode();
81
82 void deleteSourceItem();
83 void deleteShaderEffectSource();
84 void twoImagesOneShaderEffect();
85
86 void withoutQmlEngine();
87
88 void hideParent();
89
90private:
91 enum PresenceFlags {
92 VertexPresent = 0x01,
93 TexCoordPresent = 0x02,
94 MatrixPresent = 0x04,
95 OpacityPresent = 0x08,
96 SourcePresent = 0x10
97 };
98};
99
100tst_qquickshadereffect::tst_qquickshadereffect()
101{
102 qmlRegisterType<TestShaderEffect>(uri: "ShaderEffectTest", versionMajor: 1, versionMinor: 0, qmlName: "TestShaderEffect");
103}
104
105void tst_qquickshadereffect::initTestCase()
106{
107 QQmlDataTest::initTestCase();
108}
109
110void tst_qquickshadereffect::cleanupTestCase()
111{
112}
113
114void tst_qquickshadereffect::lookThroughShaderCode_data()
115{
116 QTest::addColumn<QByteArray>(name: "vertexShader");
117 QTest::addColumn<QByteArray>(name: "fragmentShader");
118 QTest::addColumn<int>(name: "presenceFlags");
119
120 QTest::newRow(dataTag: "default")
121 << QByteArray("uniform highp mat4 qt_Matrix; \n"
122 "attribute highp vec4 qt_Vertex; \n"
123 "attribute highp vec2 qt_MultiTexCoord0; \n"
124 "varying highp vec2 qt_TexCoord0; \n"
125 "void main() { \n"
126 " qt_TexCoord0 = qt_MultiTexCoord0; \n"
127 " gl_Position = qt_Matrix * qt_Vertex; \n"
128 "}")
129 << QByteArray("varying highp vec2 qt_TexCoord0; \n"
130 "uniform sampler2D source; \n"
131 "uniform lowp float qt_Opacity; \n"
132 "void main() { \n"
133 " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
134 "}")
135 << (VertexPresent | TexCoordPresent | MatrixPresent | OpacityPresent | SourcePresent);
136
137 QTest::newRow(dataTag: "empty")
138 << QByteArray(" ") // one space -- if completely empty, default will be used instead.
139 << QByteArray(" ")
140 << 0;
141
142
143 QTest::newRow(dataTag: "inside line comments")
144 << QByteArray("//uniform highp mat4 qt_Matrix;\n"
145 "attribute highp vec4 qt_Vertex;\n"
146 "// attribute highp vec2 qt_MultiTexCoord0;")
147 << QByteArray("uniform int source; // uniform lowp float qt_Opacity;")
148 << (VertexPresent | SourcePresent);
149
150 QTest::newRow(dataTag: "inside block comments")
151 << QByteArray("/*uniform highp mat4 qt_Matrix;\n"
152 "*/attribute highp vec4 qt_Vertex;\n"
153 "/*/attribute highp vec2 qt_MultiTexCoord0;//**/")
154 << QByteArray("/**/uniform int source; /* uniform lowp float qt_Opacity; */")
155 << (VertexPresent | SourcePresent);
156
157 QTest::newRow(dataTag: "inside preprocessor directive")
158 << QByteArray("#define uniform\nhighp mat4 qt_Matrix;\n"
159 "attribute highp vec4 qt_Vertex;\n"
160 "#if\\\nattribute highp vec2 qt_MultiTexCoord0;")
161 << QByteArray("uniform int source;\n"
162 " # undef uniform lowp float qt_Opacity;")
163 << (VertexPresent | SourcePresent);
164
165
166 QTest::newRow(dataTag: "line comments between")
167 << QByteArray("uniform//foo\nhighp//bar\nmat4//baz\nqt_Matrix;\n"
168 "attribute//\nhighp//\nvec4//\nqt_Vertex;\n"
169 " //*/ uniform \n attribute //\\ \n highp //// \n vec2 //* \n qt_MultiTexCoord0;")
170 << QByteArray("uniform// lowp float qt_Opacity;\nsampler2D source;")
171 << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent);
172
173 QTest::newRow(dataTag: "block comments between")
174 << QByteArray("uniform/*foo*/highp/*/bar/*/mat4/**//**/qt_Matrix;\n"
175 "attribute/**/highp/**/vec4/**/qt_Vertex;\n"
176 " /* * */ attribute /*///*/ highp /****/ vec2 /**/ qt_MultiTexCoord0;")
177 << QByteArray("uniform/*/ uniform//lowp/*float qt_Opacity;*/sampler2D source;")
178 << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent);
179
180 QTest::newRow(dataTag: "preprocessor directive between")
181 << QByteArray("uniform\n#foo\nhighp\n#bar\nmat4\n#baz\\\nblimey\nqt_Matrix;\n"
182 "attribute\n#\nhighp\n#\nvec4\n#\nqt_Vertex;\n"
183 " #uniform \n attribute \n # foo \n highp \n # bar \n vec2 \n#baz \n qt_MultiTexCoord0;")
184 << QByteArray("uniform\n#if lowp float qt_Opacity;\nsampler2D source;")
185 << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent);
186
187 QTest::newRow(dataTag: "newline between")
188 << QByteArray("uniform\nhighp\nmat4\nqt_Matrix\n;\n"
189 "attribute \t\r\n highp \n vec4 \n\n qt_Vertex ;\n"
190 " \n attribute \n highp \n vec2 \n qt_Multi\nTexCoord0 \n ;")
191 << QByteArray("uniform\nsampler2D\nsource;"
192 "uniform lowp float qt_Opacity;")
193 << (VertexPresent | MatrixPresent | OpacityPresent | SourcePresent);
194
195
196 QTest::newRow(dataTag: "extra characters #1")
197 << QByteArray("funiform highp mat4 qt_Matrix;\n"
198 "attribute highp vec4 qt_Vertex_;\n"
199 "attribute highp vec2 qqt_MultiTexCoord0;")
200 << QByteArray("uniformm int source;\n"
201 "uniform4 lowp float qt_Opacity;")
202 << 0;
203
204 QTest::newRow(dataTag: "extra characters #2")
205 << QByteArray("attribute phighp vec4 qt_Vertex;\n"
206 "attribute highpi vec2 qt_MultiTexCoord0;"
207 "fattribute highp vec4 qt_Vertex;\n"
208 "attributed highp vec2 qt_MultiTexCoord0;")
209 << QByteArray(" ")
210 << 0;
211
212 QTest::newRow(dataTag: "missing characters #1")
213 << QByteArray("unifor highp mat4 qt_Matrix;\n"
214 "attribute highp vec4 qt_Vert;\n"
215 "attribute highp vec2 MultiTexCoord0;")
216 << QByteArray("niform int source;\n"
217 "uniform qt_Opacity;")
218 << 0;
219
220 QTest::newRow(dataTag: "missing characters #2")
221 << QByteArray("attribute high vec4 qt_Vertex;\n"
222 "attribute ighp vec2 qt_MultiTexCoord0;"
223 "tribute highp vec4 qt_Vertex;\n"
224 "attrib highp vec2 qt_MultiTexCoord0;")
225 << QByteArray(" ")
226 << 0;
227
228 QTest::newRow(dataTag: "precision")
229 << QByteArray("uniform mat4 qt_Matrix;\n"
230 "attribute kindofhighp vec4 qt_Vertex;\n"
231 "attribute highp qt_MultiTexCoord0;\n")
232 << QByteArray("uniform lowp float qt_Opacity;\n"
233 "uniform mediump float source;\n")
234 << (MatrixPresent | OpacityPresent | SourcePresent);
235
236
237 QTest::newRow(dataTag: "property name #1")
238 << QByteArray("uniform highp vec3 _0aA9zZ;")
239 << QByteArray(" ")
240 << int(SourcePresent);
241
242 QTest::newRow(dataTag: "property name #2")
243 << QByteArray("uniform mediump vec2 x86;")
244 << QByteArray(" ")
245 << int(SourcePresent);
246
247 QTest::newRow(dataTag: "property name #3")
248 << QByteArray("uniform lowp float X;")
249 << QByteArray(" ")
250 << int(SourcePresent);
251
252 QTest::newRow(dataTag: "property name #4")
253 << QByteArray("uniform highp mat4 mat4x4;")
254 << QByteArray(" ")
255 << int(SourcePresent);
256}
257
258void tst_qquickshadereffect::lookThroughShaderCode()
259{
260 QFETCH(QByteArray, vertexShader);
261 QFETCH(QByteArray, fragmentShader);
262 QFETCH(int, presenceFlags);
263
264 QQmlEngine engine;
265 QQmlComponent component(&engine);
266 component.setData("import QtQuick 2.0\nimport ShaderEffectTest 1.0\nTestShaderEffect {}", baseUrl: QUrl());
267 QScopedPointer<TestShaderEffect> item(qobject_cast<TestShaderEffect*>(object: component.create()));
268 QCOMPARE(item->signalsConnected, 0);
269
270 QString expected;
271 if ((presenceFlags & VertexPresent) == 0)
272 expected += "Warning: Missing reference to \'qt_Vertex\'.\n";
273 if ((presenceFlags & TexCoordPresent) == 0)
274 expected += "Warning: Missing reference to \'qt_MultiTexCoord0\'.\n";
275 if ((presenceFlags & MatrixPresent) == 0)
276 expected += "Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n";
277 if ((presenceFlags & OpacityPresent) == 0)
278 expected += "Warning: Shaders are missing reference to \'qt_Opacity\'.\n";
279
280 item->setVertexShader(vertexShader);
281 item->setFragmentShader(fragmentShader);
282 QCOMPARE(item->parseLog(), expected);
283
284 // If the uniform was successfully parsed, the notify signal has been connected to an update slot.
285 QCOMPARE(item->signalsConnected, (presenceFlags & SourcePresent) ? 1 : 0);
286}
287
288void tst_qquickshadereffect::deleteSourceItem()
289{
290 // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
291 QQuickView *view = new QQuickView(nullptr);
292 view->setSource(QUrl::fromLocalFile(localfile: testFile(fileName: "deleteSourceItem.qml")));
293 view->show();
294 QVERIFY(QTest::qWaitForWindowExposed(view));
295 QVERIFY(view);
296 QObject *obj = view->rootObject();
297 QVERIFY(obj);
298 QMetaObject::invokeMethod(obj, member: "setDeletedSourceItem");
299 QTest::qWait(ms: 50);
300 delete view;
301}
302
303void tst_qquickshadereffect::deleteShaderEffectSource()
304{
305 // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
306 QQuickView *view = new QQuickView(nullptr);
307 view->setSource(QUrl::fromLocalFile(localfile: testFile(fileName: "deleteShaderEffectSource.qml")));
308 view->show();
309 QVERIFY(QTest::qWaitForWindowExposed(view));
310 QVERIFY(view);
311 QObject *obj = view->rootObject();
312 QVERIFY(obj);
313 QMetaObject::invokeMethod(obj, member: "setDeletedShaderEffectSource");
314 QTest::qWait(ms: 50);
315 delete view;
316}
317
318void tst_qquickshadereffect::twoImagesOneShaderEffect()
319{
320 // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash
321 QQuickView *view = new QQuickView(nullptr);
322 view->setSource(QUrl::fromLocalFile(localfile: testFile(fileName: "twoImagesOneShaderEffect.qml")));
323 view->show();
324 QVERIFY(QTest::qWaitForWindowExposed(view));
325 QVERIFY(view);
326 QObject *obj = view->rootObject();
327 QVERIFY(obj);
328 delete view;
329}
330
331void tst_qquickshadereffect::withoutQmlEngine()
332{
333 // using a shader without QML engine used to crash
334 auto window = new QQuickWindow;
335 auto shaderEffect = new TestShaderEffect(window->contentItem());
336 shaderEffect->setVertexShader("");
337 QVERIFY(shaderEffect->isComponentComplete());
338 delete window;
339}
340
341// QTBUG-86402: hiding the parent of an item that uses an effect should not cause a crash.
342void tst_qquickshadereffect::hideParent()
343{
344 QScopedPointer<QQuickView> view(new QQuickView);
345 view->setSource(testFileUrl(fileName: "hideParent.qml"));
346 QCOMPARE(view->status(), QQuickView::Ready);
347 view->show();
348 QVERIFY(QTest::qWaitForWindowExposed(view.data()));
349 // Should finish without crashing.
350 QTRY_VERIFY(view->rootObject()->property("finished").toBool());
351}
352
353QTEST_MAIN(tst_qquickshadereffect)
354
355#include "tst_qquickshadereffect.moc"
356

source code of qtdeclarative/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp