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 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
30#include <QtTest/QtTest>
31
32#include <QtCore/qbuffer.h>
33
34#include <QtGui/private/qshadernodesloader_p.h>
35#include <QtGui/private/qshaderlanguage_p.h>
36
37using QBufferPointer = QSharedPointer<QBuffer>;
38Q_DECLARE_METATYPE(QBufferPointer);
39
40using NodeHash = QHash<QString, QShaderNode>;
41Q_DECLARE_METATYPE(NodeHash);
42
43namespace
44{
45 QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly)
46 {
47 auto buffer = QBufferPointer::create();
48 buffer->setData(data);
49 if (openMode != QIODevice::NotOpen)
50 buffer->open(openMode);
51 return buffer;
52 }
53
54 QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion,
55 const QStringList &extensions = QStringList(),
56 const QString &vendor = QString())
57 {
58 auto format = QShaderFormat();
59 format.setApi(api);
60 format.setVersion(QVersionNumber(majorVersion, minorVersion));
61 format.setExtensions(extensions);
62 format.setVendor(vendor);
63 return format;
64 }
65
66 QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
67 {
68 auto port = QShaderNodePort();
69 port.direction = portDirection;
70 port.name = portName;
71 return port;
72 }
73
74 QShaderNode createNode(const QVector<QShaderNodePort> &ports)
75 {
76 auto node = QShaderNode();
77 for (const auto &port : ports)
78 node.addPort(port);
79 return node;
80 }
81}
82
83class tst_QShaderNodesLoader : public QObject
84{
85 Q_OBJECT
86private slots:
87 void shouldManipulateLoaderMembers();
88 void shouldLoadFromJsonStream_data();
89 void shouldLoadFromJsonStream();
90};
91
92void tst_QShaderNodesLoader::shouldManipulateLoaderMembers()
93{
94 // GIVEN
95 auto loader = QShaderNodesLoader();
96
97 // THEN (default state)
98 QCOMPARE(loader.status(), QShaderNodesLoader::Null);
99 QVERIFY(!loader.device());
100 QVERIFY(loader.nodes().isEmpty());
101
102 // WHEN
103 auto device1 = createBuffer(data: QByteArray("..........."), openMode: QIODevice::NotOpen);
104 loader.setDevice(device1.data());
105
106 // THEN
107 QCOMPARE(loader.status(), QShaderNodesLoader::Error);
108 QCOMPARE(loader.device(), device1.data());
109 QVERIFY(loader.nodes().isEmpty());
110
111 // WHEN
112 auto device2 = createBuffer(data: QByteArray("..........."), openMode: QIODevice::ReadOnly);
113 loader.setDevice(device2.data());
114
115 // THEN
116 QCOMPARE(loader.status(), QShaderNodesLoader::Waiting);
117 QCOMPARE(loader.device(), device2.data());
118 QVERIFY(loader.nodes().isEmpty());
119}
120
121void tst_QShaderNodesLoader::shouldLoadFromJsonStream_data()
122{
123 QTest::addColumn<QBufferPointer>(name: "device");
124 QTest::addColumn<NodeHash>(name: "nodes");
125 QTest::addColumn<QShaderNodesLoader::Status>(name: "status");
126
127 QTest::newRow(dataTag: "empty") << createBuffer(data: "", openMode: QIODevice::ReadOnly) << NodeHash() << QShaderNodesLoader::Error;
128
129 const auto smallJson = "{"
130 " \"inputValue\": {"
131 " \"outputs\": ["
132 " \"value\""
133 " ],"
134 " \"parameters\": {"
135 " \"name\": \"defaultName\","
136 " \"qualifier\": {"
137 " \"type\": \"QShaderLanguage::StorageQualifier\","
138 " \"value\": \"QShaderLanguage::Uniform\""
139 " },"
140 " \"type\": {"
141 " \"type\": \"QShaderLanguage::VariableType\","
142 " \"value\": \"QShaderLanguage::Vec3\""
143 " },"
144 " \"defaultValue\": {"
145 " \"type\": \"float\","
146 " \"value\": \"1.25\""
147 " }"
148 " },"
149 " \"rules\": ["
150 " {"
151 " \"format\": {"
152 " \"api\": \"OpenGLES\","
153 " \"major\": 2,"
154 " \"minor\": 0"
155 " },"
156 " \"substitution\": \"highp vec3 $value = $name;\","
157 " \"headerSnippets\": [ \"varying highp vec3 $name;\" ]"
158 " },"
159 " {"
160 " \"format\": {"
161 " \"api\": \"OpenGLCompatibilityProfile\","
162 " \"major\": 2,"
163 " \"minor\": 1"
164 " },"
165 " \"substitution\": \"vec3 $value = $name;\","
166 " \"headerSnippets\": [ \"in vec3 $name;\" ]"
167 " }"
168 " ]"
169 " },"
170 " \"fragColor\": {"
171 " \"inputs\": ["
172 " \"fragColor\""
173 " ],"
174 " \"rules\": ["
175 " {"
176 " \"format\": {"
177 " \"api\": \"OpenGLES\","
178 " \"major\": 2,"
179 " \"minor\": 0"
180 " },"
181 " \"substitution\": \"gl_fragColor = $fragColor;\""
182 " },"
183 " {"
184 " \"format\": {"
185 " \"api\": \"OpenGLNoProfile\","
186 " \"major\": 4,"
187 " \"minor\": 0"
188 " },"
189 " \"substitution\": \"fragColor = $fragColor;\","
190 " \"headerSnippets\": [ \"out vec4 fragColor;\" ]"
191 " }"
192 " ]"
193 " },"
194 " \"lightModel\": {"
195 " \"inputs\": ["
196 " \"baseColor\","
197 " \"position\","
198 " \"lightIntensity\""
199 " ],"
200 " \"outputs\": ["
201 " \"outputColor\""
202 " ],"
203 " \"rules\": ["
204 " {"
205 " \"format\": {"
206 " \"api\": \"OpenGLES\","
207 " \"major\": 2,"
208 " \"minor\": 0,"
209 " \"extensions\": [ \"ext1\", \"ext2\" ],"
210 " \"vendor\": \"kdab\""
211 " },"
212 " \"substitution\": \"highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\","
213 " \"headerSnippets\": [ \"#pragma include es2/lightmodel.frag.inc\" ]"
214 " },"
215 " {"
216 " \"format\": {"
217 " \"api\": \"OpenGLCoreProfile\","
218 " \"major\": 3,"
219 " \"minor\": 3"
220 " },"
221 " \"substitution\": \"vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\","
222 " \"headerSnippets\": [ \"#pragma include gl3/lightmodel.frag.inc\" ]"
223 " }"
224 " ]"
225 " }"
226 "}";
227
228 const auto smallProtos = [this]{
229 const auto openGLES2 = createFormat(api: QShaderFormat::OpenGLES, majorVersion: 2, minorVersion: 0);
230 const auto openGLES2Extended = createFormat(api: QShaderFormat::OpenGLES, majorVersion: 2, minorVersion: 0, extensions: {"ext1", "ext2"}, vendor: "kdab");
231 const auto openGL2 = createFormat(api: QShaderFormat::OpenGLCompatibilityProfile, majorVersion: 2, minorVersion: 1);
232 const auto openGL3 = createFormat(api: QShaderFormat::OpenGLCoreProfile, majorVersion: 3, minorVersion: 3);
233 const auto openGL4 = createFormat(api: QShaderFormat::OpenGLNoProfile, majorVersion: 4, minorVersion: 0);
234
235 auto protos = NodeHash();
236
237 auto inputValue = createNode(ports: {
238 createPort(portDirection: QShaderNodePort::Output, portName: "value")
239 });
240 inputValue.setParameter(name: "name", value: "defaultName");
241 inputValue.setParameter(name: "qualifier", value: QVariant::fromValue<QShaderLanguage::StorageQualifier>(value: QShaderLanguage::Uniform));
242 inputValue.setParameter(name: "type", value: QVariant::fromValue<QShaderLanguage::VariableType>(value: QShaderLanguage::Vec3));
243 inputValue.setParameter(name: "defaultValue", value: QVariant(1.25f));
244 inputValue.addRule(format: openGLES2, rule: QShaderNode::Rule("highp vec3 $value = $name;",
245 QByteArrayList() << "varying highp vec3 $name;"));
246 inputValue.addRule(format: openGL2, rule: QShaderNode::Rule("vec3 $value = $name;",
247 QByteArrayList() << "in vec3 $name;"));
248 protos.insert(akey: "inputValue", avalue: inputValue);
249
250 auto fragColor = createNode(ports: {
251 createPort(portDirection: QShaderNodePort::Input, portName: "fragColor")
252 });
253 fragColor.addRule(format: openGLES2, rule: QShaderNode::Rule("gl_fragColor = $fragColor;"));
254 fragColor.addRule(format: openGL4, rule: QShaderNode::Rule("fragColor = $fragColor;",
255 QByteArrayList() << "out vec4 fragColor;"));
256 protos.insert(QStringLiteral("fragColor"), avalue: fragColor);
257
258 auto lightModel = createNode(ports: {
259 createPort(portDirection: QShaderNodePort::Input, portName: "baseColor"),
260 createPort(portDirection: QShaderNodePort::Input, portName: "position"),
261 createPort(portDirection: QShaderNodePort::Input, portName: "lightIntensity"),
262 createPort(portDirection: QShaderNodePort::Output, portName: "outputColor")
263 });
264 lightModel.addRule(format: openGLES2Extended, rule: QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
265 QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
266 lightModel.addRule(format: openGL3, rule: QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
267 QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
268 protos.insert(akey: "lightModel", avalue: lightModel);
269
270 return protos;
271 }();
272
273 QTest::newRow(dataTag: "NotOpen") << createBuffer(data: smallJson, openMode: QIODevice::NotOpen) << NodeHash() << QShaderNodesLoader::Error;
274 QTest::newRow(dataTag: "CorrectJSON") << createBuffer(data: smallJson) << smallProtos << QShaderNodesLoader::Ready;
275}
276
277void tst_QShaderNodesLoader::shouldLoadFromJsonStream()
278{
279 // GIVEN
280 QFETCH(QBufferPointer, device);
281
282 auto loader = QShaderNodesLoader();
283
284 // WHEN
285 loader.setDevice(device.data());
286 loader.load();
287
288 // THEN
289 QFETCH(QShaderNodesLoader::Status, status);
290 QCOMPARE(loader.status(), status);
291
292 QFETCH(NodeHash, nodes);
293 const auto sortedKeys = [](const NodeHash &nodes) {
294 auto res = nodes.keys();
295 res.sort();
296 return res;
297 };
298 const auto sortedParameters = [](const QShaderNode &node) {
299 auto res = node.parameterNames();
300 res.sort();
301 return res;
302 };
303 QCOMPARE(sortedKeys(loader.nodes()), sortedKeys(nodes));
304 for (const auto &key : nodes.keys()) {
305 const auto actual = loader.nodes().value(akey: key);
306 const auto expected = nodes.value(akey: key);
307
308 QVERIFY(actual.uuid().isNull());
309 QCOMPARE(actual.ports(), expected.ports());
310 QCOMPARE(sortedParameters(actual), sortedParameters(expected));
311 for (const auto &name : expected.parameterNames()) {
312 QCOMPARE(actual.parameter(name), expected.parameter(name));
313 }
314 QCOMPARE(actual.availableFormats(), expected.availableFormats());
315 for (const auto &format : expected.availableFormats()) {
316 QCOMPARE(actual.rule(format), expected.rule(format));
317 }
318 }
319}
320
321QTEST_MAIN(tst_QShaderNodesLoader)
322
323#include "tst_qshadernodesloader.moc"
324

source code of qtbase/tests/auto/gui/util/qshadernodesloader/tst_qshadernodesloader.cpp