| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <qtest.h> |
| |
| #include <QList> |
| #include <QByteArray> |
| #include <private/qquickopenglshadereffect_p.h> |
| #include <QMatrix4x4> |
| #include <QtQuick/QQuickView> |
| #include <QtQml/QQmlEngine> |
| #include "../../shared/util.h" |
| |
| class TestShaderEffect : public QQuickShaderEffect |
| { |
| Q_OBJECT |
| Q_PROPERTY(QVariant source READ dummyRead NOTIFY sourceChanged) |
| Q_PROPERTY(QVariant _0aA9zZ READ dummyRead NOTIFY dummyChanged) |
| Q_PROPERTY(QVariant x86 READ dummyRead NOTIFY dummyChanged) |
| Q_PROPERTY(QVariant X READ dummyRead NOTIFY dummyChanged) |
| Q_PROPERTY(QMatrix4x4 mat4x4 READ mat4x4Read NOTIFY dummyChanged) |
| |
| public: |
| TestShaderEffect(QQuickItem* parent = nullptr) : QQuickShaderEffect(parent) |
| { |
| } |
| |
| QMatrix4x4 mat4x4Read() const { return QMatrix4x4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1); } |
| QVariant dummyRead() const { return QVariant(); } |
| |
| int signalsConnected = 0; |
| |
| protected: |
| void connectNotify(const QMetaMethod &) override { ++signalsConnected; } |
| void disconnectNotify(const QMetaMethod &) override { --signalsConnected; } |
| |
| signals: |
| void dummyChanged(); |
| void sourceChanged(); |
| |
| private: |
| }; |
| |
| class tst_qquickshadereffect : public QQmlDataTest |
| { |
| Q_OBJECT |
| public: |
| tst_qquickshadereffect(); |
| |
| private slots: |
| void initTestCase(); |
| void cleanupTestCase(); |
| |
| void lookThroughShaderCode_data(); |
| void lookThroughShaderCode(); |
| |
| void deleteSourceItem(); |
| void deleteShaderEffectSource(); |
| void twoImagesOneShaderEffect(); |
| |
| void withoutQmlEngine(); |
| |
| private: |
| enum PresenceFlags { |
| VertexPresent = 0x01, |
| TexCoordPresent = 0x02, |
| MatrixPresent = 0x04, |
| OpacityPresent = 0x08, |
| SourcePresent = 0x10 |
| }; |
| }; |
| |
| tst_qquickshadereffect::tst_qquickshadereffect() |
| { |
| qmlRegisterType<TestShaderEffect>("ShaderEffectTest", 1, 0, "TestShaderEffect"); |
| } |
| |
| void tst_qquickshadereffect::initTestCase() |
| { |
| QQmlDataTest::initTestCase(); |
| } |
| |
| void tst_qquickshadereffect::cleanupTestCase() |
| { |
| } |
| |
| void tst_qquickshadereffect::lookThroughShaderCode_data() |
| { |
| QTest::addColumn<QByteArray>("vertexShader"); |
| QTest::addColumn<QByteArray>("fragmentShader"); |
| QTest::addColumn<int>("presenceFlags"); |
| |
| QTest::newRow("default") |
| << QByteArray("uniform highp mat4 qt_Matrix; \n" |
| "attribute highp vec4 qt_Vertex; \n" |
| "attribute highp vec2 qt_MultiTexCoord0; \n" |
| "varying highp vec2 qt_TexCoord0; \n" |
| "void main() { \n" |
| " qt_TexCoord0 = qt_MultiTexCoord0; \n" |
| " gl_Position = qt_Matrix * qt_Vertex; \n" |
| "}") |
| << QByteArray("varying highp vec2 qt_TexCoord0; \n" |
| "uniform sampler2D source; \n" |
| "uniform lowp float qt_Opacity; \n" |
| "void main() { \n" |
| " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n" |
| "}") |
| << (VertexPresent | TexCoordPresent | MatrixPresent | OpacityPresent | SourcePresent); |
| |
| QTest::newRow("empty") |
| << QByteArray(" ") // one space -- if completely empty, default will be used instead. |
| << QByteArray(" ") |
| << 0; |
| |
| |
| QTest::newRow("inside line comments") |
| << QByteArray("//uniform highp mat4 qt_Matrix;\n" |
| "attribute highp vec4 qt_Vertex;\n" |
| "// attribute highp vec2 qt_MultiTexCoord0;") |
| << QByteArray("uniform int source; // uniform lowp float qt_Opacity;") |
| << (VertexPresent | SourcePresent); |
| |
| QTest::newRow("inside block comments") |
| << QByteArray("/*uniform highp mat4 qt_Matrix;\n" |
| "*/attribute highp vec4 qt_Vertex;\n" |
| "/*/attribute highp vec2 qt_MultiTexCoord0;//**/") |
| << QByteArray("/**/uniform int source; /* uniform lowp float qt_Opacity; */") |
| << (VertexPresent | SourcePresent); |
| |
| QTest::newRow("inside preprocessor directive") |
| << QByteArray("#define uniform\nhighp mat4 qt_Matrix;\n" |
| "attribute highp vec4 qt_Vertex;\n" |
| "#if\\\nattribute highp vec2 qt_MultiTexCoord0;") |
| << QByteArray("uniform int source;\n" |
| " # undef uniform lowp float qt_Opacity;") |
| << (VertexPresent | SourcePresent); |
| |
| |
| QTest::newRow("line comments between") |
| << QByteArray("uniform//foo\nhighp//bar\nmat4//baz\nqt_Matrix;\n" |
| "attribute//\nhighp//\nvec4//\nqt_Vertex;\n" |
| " //*/ uniform \n attribute //\\ \n highp //// \n vec2 //* \n qt_MultiTexCoord0;") |
| << QByteArray("uniform// lowp float qt_Opacity;\nsampler2D source;") |
| << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent); |
| |
| QTest::newRow("block comments between") |
| << QByteArray("uniform/*foo*/highp/*/bar/*/mat4/**//**/qt_Matrix;\n" |
| "attribute/**/highp/**/vec4/**/qt_Vertex;\n" |
| " /* * */ attribute /*///*/ highp /****/ vec2 /**/ qt_MultiTexCoord0;") |
| << QByteArray("uniform/*/ uniform//lowp/*float qt_Opacity;*/sampler2D source;") |
| << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent); |
| |
| QTest::newRow("preprocessor directive between") |
| << QByteArray("uniform\n#foo\nhighp\n#bar\nmat4\n#baz\\\nblimey\nqt_Matrix;\n" |
| "attribute\n#\nhighp\n#\nvec4\n#\nqt_Vertex;\n" |
| " #uniform \n attribute \n # foo \n highp \n # bar \n vec2 \n#baz \n qt_MultiTexCoord0;") |
| << QByteArray("uniform\n#if lowp float qt_Opacity;\nsampler2D source;") |
| << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent); |
| |
| QTest::newRow("newline between") |
| << QByteArray("uniform\nhighp\nmat4\nqt_Matrix\n;\n" |
| "attribute \t\r\n highp \n vec4 \n\n qt_Vertex ;\n" |
| " \n attribute \n highp \n vec2 \n qt_Multi\nTexCoord0 \n ;") |
| << QByteArray("uniform\nsampler2D\nsource;" |
| "uniform lowp float qt_Opacity;") |
| << (VertexPresent | MatrixPresent | OpacityPresent | SourcePresent); |
| |
| |
| QTest::newRow("extra characters #1") |
| << QByteArray("funiform highp mat4 qt_Matrix;\n" |
| "attribute highp vec4 qt_Vertex_;\n" |
| "attribute highp vec2 qqt_MultiTexCoord0;") |
| << QByteArray("uniformm int source;\n" |
| "uniform4 lowp float qt_Opacity;") |
| << 0; |
| |
| QTest::newRow("extra characters #2") |
| << QByteArray("attribute phighp vec4 qt_Vertex;\n" |
| "attribute highpi vec2 qt_MultiTexCoord0;" |
| "fattribute highp vec4 qt_Vertex;\n" |
| "attributed highp vec2 qt_MultiTexCoord0;") |
| << QByteArray(" ") |
| << 0; |
| |
| QTest::newRow("missing characters #1") |
| << QByteArray("unifor highp mat4 qt_Matrix;\n" |
| "attribute highp vec4 qt_Vert;\n" |
| "attribute highp vec2 MultiTexCoord0;") |
| << QByteArray("niform int source;\n" |
| "uniform qt_Opacity;") |
| << 0; |
| |
| QTest::newRow("missing characters #2") |
| << QByteArray("attribute high vec4 qt_Vertex;\n" |
| "attribute ighp vec2 qt_MultiTexCoord0;" |
| "tribute highp vec4 qt_Vertex;\n" |
| "attrib highp vec2 qt_MultiTexCoord0;") |
| << QByteArray(" ") |
| << 0; |
| |
| QTest::newRow("precision") |
| << QByteArray("uniform mat4 qt_Matrix;\n" |
| "attribute kindofhighp vec4 qt_Vertex;\n" |
| "attribute highp qt_MultiTexCoord0;\n") |
| << QByteArray("uniform lowp float qt_Opacity;\n" |
| "uniform mediump float source;\n") |
| << (MatrixPresent | OpacityPresent | SourcePresent); |
| |
| |
| QTest::newRow("property name #1") |
| << QByteArray("uniform highp vec3 _0aA9zZ;") |
| << QByteArray(" ") |
| << int(SourcePresent); |
| |
| QTest::newRow("property name #2") |
| << QByteArray("uniform mediump vec2 x86;") |
| << QByteArray(" ") |
| << int(SourcePresent); |
| |
| QTest::newRow("property name #3") |
| << QByteArray("uniform lowp float X;") |
| << QByteArray(" ") |
| << int(SourcePresent); |
| |
| QTest::newRow("property name #4") |
| << QByteArray("uniform highp mat4 mat4x4;") |
| << QByteArray(" ") |
| << int(SourcePresent); |
| } |
| |
| void tst_qquickshadereffect::lookThroughShaderCode() |
| { |
| QFETCH(QByteArray, vertexShader); |
| QFETCH(QByteArray, fragmentShader); |
| QFETCH(int, presenceFlags); |
| |
| QQmlEngine engine; |
| QQmlComponent component(&engine); |
| component.setData("import QtQuick 2.0\nimport ShaderEffectTest 1.0\nTestShaderEffect {}", QUrl()); |
| QScopedPointer<TestShaderEffect> item(qobject_cast<TestShaderEffect*>(component.create())); |
| QCOMPARE(item->signalsConnected, 0); |
| |
| QString expected; |
| if ((presenceFlags & VertexPresent) == 0) |
| expected += "Warning: Missing reference to \'qt_Vertex\'.\n"; |
| if ((presenceFlags & TexCoordPresent) == 0) |
| expected += "Warning: Missing reference to \'qt_MultiTexCoord0\'.\n"; |
| if ((presenceFlags & MatrixPresent) == 0) |
| expected += "Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n"; |
| if ((presenceFlags & OpacityPresent) == 0) |
| expected += "Warning: Shaders are missing reference to \'qt_Opacity\'.\n"; |
| |
| item->setVertexShader(vertexShader); |
| item->setFragmentShader(fragmentShader); |
| QCOMPARE(item->parseLog(), expected); |
| |
| // If the uniform was successfully parsed, the notify signal has been connected to an update slot. |
| QCOMPARE(item->signalsConnected, (presenceFlags & SourcePresent) ? 1 : 0); |
| } |
| |
| void tst_qquickshadereffect::deleteSourceItem() |
| { |
| // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash |
| QQuickView *view = new QQuickView(nullptr); |
| view->setSource(QUrl::fromLocalFile(testFile("deleteSourceItem.qml"))); |
| view->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(view)); |
| QVERIFY(view); |
| QObject *obj = view->rootObject(); |
| QVERIFY(obj); |
| QMetaObject::invokeMethod(obj, "setDeletedSourceItem"); |
| QTest::qWait(50); |
| delete view; |
| } |
| |
| void tst_qquickshadereffect::deleteShaderEffectSource() |
| { |
| // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash |
| QQuickView *view = new QQuickView(nullptr); |
| view->setSource(QUrl::fromLocalFile(testFile("deleteShaderEffectSource.qml"))); |
| view->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(view)); |
| QVERIFY(view); |
| QObject *obj = view->rootObject(); |
| QVERIFY(obj); |
| QMetaObject::invokeMethod(obj, "setDeletedShaderEffectSource"); |
| QTest::qWait(50); |
| delete view; |
| } |
| |
| void tst_qquickshadereffect::twoImagesOneShaderEffect() |
| { |
| // purely to ensure that deleting the sourceItem of a shader doesn't cause a crash |
| QQuickView *view = new QQuickView(nullptr); |
| view->setSource(QUrl::fromLocalFile(testFile("twoImagesOneShaderEffect.qml"))); |
| view->show(); |
| QVERIFY(QTest::qWaitForWindowExposed(view)); |
| QVERIFY(view); |
| QObject *obj = view->rootObject(); |
| QVERIFY(obj); |
| delete view; |
| } |
| |
| void tst_qquickshadereffect::withoutQmlEngine() |
| { |
| // using a shader without QML engine used to crash |
| auto window = new QQuickWindow; |
| auto shaderEffect = new TestShaderEffect(window->contentItem()); |
| shaderEffect->setVertexShader(""); |
| QVERIFY(shaderEffect->isComponentComplete()); |
| delete window; |
| } |
| |
| QTEST_MAIN(tst_qquickshadereffect) |
| |
| #include "tst_qquickshadereffect.moc" |