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 | |
39 | class 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 | |
48 | public: |
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 | |
58 | protected: |
59 | void connectNotify(const QMetaMethod &) override { ++signalsConnected; } |
60 | void disconnectNotify(const QMetaMethod &) override { --signalsConnected; } |
61 | |
62 | signals: |
63 | void dummyChanged(); |
64 | void sourceChanged(); |
65 | |
66 | private: |
67 | }; |
68 | |
69 | class tst_qquickshadereffect : public QQmlDataTest |
70 | { |
71 | Q_OBJECT |
72 | public: |
73 | tst_qquickshadereffect(); |
74 | |
75 | private 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 | |
90 | private: |
91 | enum PresenceFlags { |
92 | VertexPresent = 0x01, |
93 | TexCoordPresent = 0x02, |
94 | MatrixPresent = 0x04, |
95 | OpacityPresent = 0x08, |
96 | SourcePresent = 0x10 |
97 | }; |
98 | }; |
99 | |
100 | tst_qquickshadereffect::tst_qquickshadereffect() |
101 | { |
102 | qmlRegisterType<TestShaderEffect>(uri: "ShaderEffectTest" , versionMajor: 1, versionMinor: 0, qmlName: "TestShaderEffect" ); |
103 | } |
104 | |
105 | void tst_qquickshadereffect::initTestCase() |
106 | { |
107 | QQmlDataTest::initTestCase(); |
108 | } |
109 | |
110 | void tst_qquickshadereffect::cleanupTestCase() |
111 | { |
112 | } |
113 | |
114 | void 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 | |
258 | void 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 | |
288 | void 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 | |
303 | void 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 | |
318 | void 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 | |
331 | void 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. |
342 | void 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 | |
353 | QTEST_MAIN(tst_qquickshadereffect) |
354 | |
355 | #include "tst_qquickshadereffect.moc" |
356 | |