diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-06-04 16:11:50 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-06-04 16:11:50 +0200 |
commit | 6f979c0e259c55f76bcd04748e3f7080ed35045f (patch) | |
tree | 7a6eb91b83ffcd7a2bdb30e8deff1b48d60b26f9 | |
parent | c8c54c868204fd6159bcc8e6c8e71d76907f8c31 (diff) | |
parent | 6e768c55e605008398168d1524cb02045e047c3d (diff) |
Merge remote-tracking branch 'origin/5.11' into dev
Conflicts:
.qmake.conf
Change-Id: Ia44a20a78bc327ce199f02e1c1d2fb6c246d6675
-rw-r--r-- | src/plugins/platforms/webgl/qwebglmain.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/webgl/webqt.jsx | 88 | ||||
-rw-r--r-- | tests/global/global.cfg | 5 | ||||
-rw-r--r-- | tests/plugins/platforms/platforms.pro | 4 | ||||
-rw-r--r-- | tests/plugins/platforms/webgl/BLACKLIST | 1 | ||||
-rw-r--r-- | tests/plugins/platforms/webgl/basic_scene.qml | 3 | ||||
-rw-r--r-- | tests/plugins/platforms/webgl/parameters.h | 114 | ||||
-rw-r--r-- | tests/plugins/platforms/webgl/tst_webgl.cpp | 286 | ||||
-rw-r--r-- | tests/plugins/platforms/webgl/webgl.pro | 19 | ||||
-rw-r--r-- | tests/plugins/plugins.pro | 4 | ||||
-rw-r--r-- | tests/tests.pro | 2 |
11 files changed, 485 insertions, 45 deletions
diff --git a/src/plugins/platforms/webgl/qwebglmain.cpp b/src/plugins/platforms/webgl/qwebglmain.cpp index dd14bec..6c9c324 100644 --- a/src/plugins/platforms/webgl/qwebglmain.cpp +++ b/src/plugins/platforms/webgl/qwebglmain.cpp @@ -52,13 +52,13 @@ QPlatformIntegration* QWebGLIntegrationPlugin::create(const QString& system, const QStringList parts = parameter.split('='); if (parts.first() == QStringLiteral("port")) { if (parts.size() != 2) { - qCCritical(lcWebGL, "QWebGLIntegrationPlugin::create: No port specified"); + qCCritical(lcWebGL, "Port parameter specified with no value"); return nullptr; } bool ok; port = parts.last().toUShort(&ok); if (!ok) { - qCCritical(lcWebGL, "QWebGLIntegrationPlugin::create: Invalid port number"); + qCCritical(lcWebGL, "Invalid port number"); return nullptr; } } diff --git a/src/plugins/platforms/webgl/webqt.jsx b/src/plugins/platforms/webgl/webqt.jsx index d2c5b0b..cf0bae4 100644 --- a/src/plugins/platforms/webgl/webqt.jsx +++ b/src/plugins/platforms/webgl/webqt.jsx @@ -274,29 +274,30 @@ window.onload = function () { canvas.addEventListener('DOMMouseScroll', handleMouseWheel, { passive: true }); function handleTouch(event) { - var object = {}; - object["type"] = "touch"; - object["name"] = name; - object["time"] = new Date().getTime(); - object["event"] = event.type; - object["changedTouches"] = []; - object["stationaryTouches"] = []; - + var object = { + "type" : "touch", + "name" : name, + "time" : new Date().getTime(), + "event" : event.type, + "changedTouches" : [], + "stationaryTouches" : [], + }; var addTouch = function(changedTouch, isChanged) { - var touch = {}; - touch["clientX"] = changedTouch.clientX; - touch["clientY"] = changedTouch.clientY; - touch["force"] = changedTouch.force; - touch["identifier"] = changedTouch.identifier; - touch["pageX"] = changedTouch.pageX; - touch["pageY"] = changedTouch.pageY; - touch["radiousX"] = changedTouch.radiousX; - touch["radiousY"] = changedTouch.radiousY; - touch["rotatingAngle"] = changedTouch.rotatingAngle; - touch["screenX"] = changedTouch.screenX; - touch["screenY"] = changedTouch.screenY; - touch["normalPositionX"] = changedTouch.screenX / screen.width; - touch["normalPositionY"] = changedTouch.screenY / screen.height; + var touch = { + "clientX" : changedTouch.clientX, + "clientY" : changedTouch.clientY, + "force" : changedTouch.force, + "identifier" : changedTouch.identifier, + "pageX" : changedTouch.pageX, + "pageY" : changedTouch.pageY, + "radiousX" : changedTouch.radiousX, + "radiousY" : changedTouch.radiousY, + "rotatingAngle" : changedTouch.rotatingAngle, + "screenX" : changedTouch.screenX, + "screenY" : changedTouch.screenY, + "normalPositionX" : changedTouch.screenX / screen.width, + "normalPositionY" : changedTouch.screenY / screen.height, + }; if (isChanged) object.changedTouches.push(touch); else @@ -786,14 +787,15 @@ window.onload = function () { d.drawArrayBuf = gl.createBuffer(); gl._bindBuffer(gl.ARRAY_BUFFER, d.drawArrayBuf); for (var i = 4; i < arguments.length; i += 6) { - var subData = {}; - subData["index"] = arguments[i + 0]; - subData["size"] = arguments[i + 1]; - subData["type"] = arguments[i + 2]; - subData["normalized"] = arguments[i + 3]; - subData["stride"] = arguments[i + 4]; - subData["offset"] = 0; - subData["data"] = arguments[i + 5]; + var subData = { + "index" : arguments[i + 0], + "size" : arguments[i + 1], + "type" : arguments[i + 2], + "normalized" : arguments[i + 3], + "stride" : arguments[i + 4], + "offset" : 0, + "data" : arguments[i + 5], + }; subDataParts.push(subData); bufferSize += subData.data.length; } @@ -921,20 +923,20 @@ window.onload = function () { var view = new DataView(event.data); var offset = 0; var obj = { "parameters" : [] }; - obj["function"] = supportedFunctions[view.getUint8(offset)]; + obj.function = supportedFunctions[view.getUint8(offset)]; offset += 1; if (obj.function in commandsNeedingResponse) { - obj["id"] = view.getUint32(offset); + obj.id = view.getUint32(offset); offset += 4; } - if (obj["function"] === "makeCurrent") - obj["parameterCount"] = 4; - else if (obj["function"] === "swapBuffers") - obj["parameterCount"] = 0; - else if (obj["function"] == "drawArrays") - obj["parameterCount"] = null; // glDrawArrays has a variable number of arguments + if (obj.function === "makeCurrent") + obj.parameterCount = 4; + else if (obj.function === "swapBuffers") + obj.parameterCount = 0; + else if (obj.function == "drawArrays") + obj.parameterCount = null; // glDrawArrays has a variable number of arguments else - obj["parameterCount"] = gl[obj["function"]].length; + obj.parameterCount = gl[obj.function].length; function deserialize(container, count) { for (var i = 0; count != null ? i < count : offset + 4 < event.data.byteLength; ++i) { var character = view.getUint8(offset); @@ -1120,11 +1122,11 @@ window.onload = function () { } else if (obj.type === "change_title") { document.title = obj.text; } else if (obj.type === "connect") { - supportedFunctions = obj["supportedFunctions"]; - var sysinfo = obj["sysinfo"]; - if (obj["debug"]) + supportedFunctions = obj.supportedFunctions; + var sysinfo = obj.sysinfo; + if (obj.debug) DEBUG = 1; - if (obj["loadingScreen"] === "0") + if (obj.loadingScreen === "0") LOADINGSCREEN = 0; console.log(sysinfo); } else { diff --git a/tests/global/global.cfg b/tests/global/global.cfg new file mode 100644 index 0000000..903a388 --- /dev/null +++ b/tests/global/global.cfg @@ -0,0 +1,5 @@ +<config> +<modules> +<module name="QtWebGLStreaming" qtname="webgl"/> +</modules> +</config> diff --git a/tests/plugins/platforms/platforms.pro b/tests/plugins/platforms/platforms.pro new file mode 100644 index 0000000..836b27b --- /dev/null +++ b/tests/plugins/platforms/platforms.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + webgl diff --git a/tests/plugins/platforms/webgl/BLACKLIST b/tests/plugins/platforms/webgl/BLACKLIST new file mode 100644 index 0000000..fbe6866 --- /dev/null +++ b/tests/plugins/platforms/webgl/BLACKLIST @@ -0,0 +1 @@ +windows 32bit ci diff --git a/tests/plugins/platforms/webgl/basic_scene.qml b/tests/plugins/platforms/webgl/basic_scene.qml new file mode 100644 index 0000000..617bdaa --- /dev/null +++ b/tests/plugins/platforms/webgl/basic_scene.qml @@ -0,0 +1,3 @@ +import QtQuick 2.0 + +Item {} diff --git a/tests/plugins/platforms/webgl/parameters.h b/tests/plugins/platforms/webgl/parameters.h new file mode 100644 index 0000000..5f07e9e --- /dev/null +++ b/tests/plugins/platforms/webgl/parameters.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt WebGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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 <QtCore/qdatastream.h> +#include <QtCore/qvariant.h> + +#ifndef PARAMETERS_H +#define PARAMETERS_H + +QT_BEGIN_NAMESPACE + +namespace Parameters +{ + +QVariantList read(const QByteArray &data, QDataStream &stream, quint32 &offset, int count); + +template<typename T> +T readNext(QDataStream &stream, quint32 &offset) +{ + T value; + stream >> value; + offset += sizeof(T); + return value; +} + +template<> +QString readNext(QDataStream &stream, quint32 &offset) +{ + QString value; + stream >> value; + offset += quint32(int(sizeof(qint32)) + value.size()); + return value; +} + +template<> +QByteArray readNext(QDataStream &stream, quint32 &offset) +{ + QByteArray data; + stream >> data; + offset += quint32(int(sizeof(qint32)) + data.size()); + return data; +} + +QVariantList readNextArray(const QByteArray &data, QDataStream &stream, quint32 &offset) +{ + quint8 count; + stream >> count; + offset += sizeof(count); + return read(data, stream, offset, count); +} + +QVariant readNext(const QByteArray &data, QDataStream &stream, quint32 &offset) +{ + char type; + offset += quint32(stream.readRawData(&type, 1)); + switch (type) { + case 'i': return readNext<qint32>(stream, offset); + case 'u': return readNext<quint32>(stream, offset); + case 'd': return readNext<double>(stream, offset); + case 'b': return readNext<quint8>(stream, offset); + case 's': return readNext<QString>(stream, offset); + case 'x': return readNext<QByteArray>(stream, offset); + case 'a': return readNextArray(data, stream, offset); + } + return QVariant(); +} + +QVariantList read(const QByteArray &data, QDataStream &stream, quint32 &offset) +{ + QVariantList parameters; + for (const auto size = data.size(); int(offset + 4) < size;) + parameters.append(readNext(data, stream, offset)); + return parameters; +} + +QVariantList read(const QByteArray &data, QDataStream &stream, quint32 &offset, int count) +{ + QVariantList parameters; + for (int i = 0; i < count; ++i) + parameters.append(readNext(data, stream, offset)); + return parameters; +} + +} + +QT_END_NAMESPACE + +#endif // PARAMETERS_H diff --git a/tests/plugins/platforms/webgl/tst_webgl.cpp b/tests/plugins/platforms/webgl/tst_webgl.cpp new file mode 100644 index 0000000..306d6fd --- /dev/null +++ b/tests/plugins/platforms/webgl/tst_webgl.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt WebGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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 <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qobject.h> +#include <QtCore/qprocess.h> +#include <QtCore/qregularexpression.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/qopengl.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtWebSockets/qwebsocket.h> + +#include "parameters.h" + +#include <memory> + +#define PORT 8080 +#define PORTSTRING QT_STRINGIFY(PORT) + +class tst_WebGL : public QObject +{ + Q_OBJECT + + QNetworkAccessManager manager; + QWebSocket webSocket; + QStringList functions; + QProcess process; + +signals: + void command(const QString &name, const QVariantList ¶meters); + void queryCommand(const QString &name, int id, const QVariantList ¶meters); + +public slots: + void parseTextMessage(const QString &text); + void parseBinaryMessage(const QByteArray &data); + +private slots: + void initTestCase(); + + void init(); + void cleanup(); + + void checkFunctionCount_data(); + void checkFunctionCount(); + + void waitForSwapBuffers_data(); + void waitForSwapBuffers(); +}; + +void tst_WebGL::parseTextMessage(const QString &text) +{ + const auto document = QJsonDocument::fromJson(text.toUtf8()); + if (document["type"].toString() == "connect") { + const auto supportedFunctions = document["supportedFunctions"].toArray(); + functions.clear(); + for (const auto &function : supportedFunctions) + functions.append(function.toString()); + } else if (document["type"] == "create_canvas") { + const QJsonDocument defaultValuesMessage { + QJsonObject { + { QLatin1String("type"), QLatin1String("default_context_parameters") }, + { QString::number(GL_EXTENSIONS), + QLatin1String("GL_OES_element_index_uint " + "GL_OES_standard_derivatives " + "GL_OES_depth_texture GL_OES_packed_depth_stencil") }, + { QString::number(GL_BLEND), false }, + { QString::number(GL_DEPTH_TEST), false }, + { QString::number(GL_MAX_TEXTURE_SIZE), 512 }, + { QString::number(GL_MAX_VERTEX_ATTRIBS), 16}, + { QString::number(GL_RENDERER), "Test WebGL"}, + { QString::number(GL_SCISSOR_TEST), false }, + { QString::number(GL_STENCIL_TEST), false }, + { QString::number(GL_UNPACK_ALIGNMENT), 4 }, + { QString::number(GL_VENDOR), "Qt" }, + { QString::number(GL_VIEWPORT), QJsonArray{ 0, 0, 640, 480 } }, + { QLatin1String("name"), document["winId"] } + }, + }; + webSocket.sendTextMessage(defaultValuesMessage.toJson()); + } +} + +void tst_WebGL::parseBinaryMessage(const QByteArray &data) +{ + const QSet<QString> commandsNeedingResponse { + QLatin1String("swapBuffers"), + QLatin1String("checkFramebufferStatus"), + QLatin1String("createProgram"), + QLatin1String("createShader"), + QLatin1String("genBuffers"), + QLatin1String("genFramebuffers"), + QLatin1String("genRenderbuffers"), + QLatin1String("genTextures"), + QLatin1String("getAttachedShaders"), + QLatin1String("getAttribLocation"), + QLatin1String("getBooleanv"), + QLatin1String("getError"), + QLatin1String("getFramebufferAttachmentParameteriv"), + QLatin1String("getIntegerv"), + QLatin1String("getParameter"), + QLatin1String("getProgramInfoLog"), + QLatin1String("getProgramiv"), + QLatin1String("getRenderbufferParameteriv"), + QLatin1String("getShaderiv"), + QLatin1String("getShaderPrecisionFormat"), + QLatin1String("getString"), + QLatin1String("getTexParameterfv"), + QLatin1String("getTexParameteriv"), + QLatin1String("getUniformfv"), + QLatin1String("getUniformLocation"), + QLatin1String("getUniformiv"), + QLatin1String("getVertexAttribfv"), + QLatin1String("getVertexAttribiv"), + QLatin1String("getShaderSource"), + QLatin1String("getShaderInfoLog"), + QLatin1String("isRenderbuffer") + }; + + quint32 offset = 0; + QString function; + int id = -1; + QDataStream stream(data); + { + quint8 functionIndex; + stream >> functionIndex; + offset += sizeof(functionIndex); + function = functions[functionIndex]; + if (commandsNeedingResponse.contains(function)) { + stream >> id; + offset += sizeof(id); + } + } + const auto parameters = Parameters::read(data, stream, offset); + { + quint32 magic = 0; + stream >> magic; + offset += sizeof(magic); + QCOMPARE(magic, 0xbaadf00d); + } + QCOMPARE(int(offset), data.size()); + if (id == -1) + emit command(function, parameters); + else + emit queryCommand(function, id, parameters); +} + +void tst_WebGL::initTestCase() +{ + connect(&webSocket, &QWebSocket::binaryMessageReceived, this, &tst_WebGL::parseBinaryMessage); + connect(&webSocket, &QWebSocket::textMessageReceived, this, &tst_WebGL::parseTextMessage); +} + +void tst_WebGL::init() +{ + QFETCH(QString, scene); + const auto tryToConnect = [=](quint16 port = PORT) { + QTcpSocket socket; + socket.connectToHost("localhost", port); + QTRY_LOOP_IMPL(socket.state() == QTcpSocket::ConnectedState || + socket.state() == QTcpSocket::UnconnectedState, 1000, 50); + return socket.state() == QTcpSocket::ConnectedState; + }; + + QVERIFY2(!tryToConnect(), "An application is listening on port " PORTSTRING); + + QString executableName = QLatin1String("qmlscene"); +#if defined(Q_OS_WIN) + executableName += QString::fromLatin1(".exe"); +#endif + + process.setProcessChannelMode(QProcess::MergedChannels); + process.setProgram(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QChar('/') + + executableName); + process.setArguments(QStringList { QDir::toNativeSeparators(scene) }); + process.setEnvironment(QProcess::systemEnvironment() + << "QT_QPA_PLATFORM=webgl:port=" PORTSTRING + << "QT_LOGGING_RULES=qt.qpa.webgl.*=true"); + process.start(); + process.waitForStarted(); + QVERIFY(process.isOpen()); + connect(&process, &QProcess::readyReadStandardOutput, [=]() { + while (process.bytesAvailable()) + qDebug() << process.pid() << process.readLine(); + }); + QTRY_VERIFY(tryToConnect()); + const QJsonDocument connectMessage { + QJsonObject { + { QLatin1String("type"), QLatin1String("connect") }, + { QLatin1String("width"), 1920 }, + { QLatin1String("height"), 1080 }, + { QLatin1String("physicalWidth"), 531.3 }, + { QLatin1String("physicalHeight"), 298.9 } + } + }; + + auto reply = manager.get(QNetworkRequest(QUrl("http://localhost:" PORTSTRING "/webqt.js"))); + QSignalSpy replyFinishedSpy(reply, &QNetworkReply::finished); + QTRY_VERIFY(!replyFinishedSpy.isEmpty()); + reply->readLine(); + const auto portString = reply->readLine().trimmed(); + QVERIFY(portString.size()); + const QRegularExpression rx("var port = (\\d+);"); + const auto match = rx.match(portString); + QVERIFY(!match.captured(1).isEmpty()); + QVERIFY(match.captured(1).toUInt() <= std::numeric_limits<quint16>::max()); + const auto websocketPort = quint16(match.captured(1).toUInt()); + + QTRY_VERIFY(tryToConnect(websocketPort)); + QSignalSpy connected(&webSocket, &QWebSocket::connected); + webSocket.open(QUrl(QString::fromLatin1("ws://localhost:%1").arg(websocketPort))); + QTRY_VERIFY(!connected.isEmpty()); + webSocket.sendTextMessage(connectMessage.toJson()); + QVERIFY(webSocket.state() == QAbstractSocket::ConnectedState); +} + +void tst_WebGL::cleanup() +{ + webSocket.close(); + + process.kill(); + process.waitForFinished(); +} + +void tst_WebGL::checkFunctionCount_data() +{ + QTest::addColumn<QString>("scene"); // Fetched in tst_WebGL::init + QTest::newRow("Basic scene") << QFINDTESTDATA("basic_scene.qml"); +} + +void tst_WebGL::checkFunctionCount() +{ + QCOMPARE(functions.size(), 147); +} + +void tst_WebGL::waitForSwapBuffers_data() +{ + QTest::addColumn<QString>("scene"); // Fetched in tst_WebGL::init + QTest::newRow("Basic scene") << QFINDTESTDATA("basic_scene.qml"); +} + +void tst_WebGL::waitForSwapBuffers() +{ + QSignalSpy spy(this, &tst_WebGL::queryCommand); + QTRY_VERIFY(std::find_if(spy.cbegin(), spy.cend(), [](const QList<QVariant> &list) { + // Our connect message changed the scene's size, forcing a swapBuffers() call. + return list.first() == QLatin1String("swapBuffers"); + }) != spy.cend()); +} + +QTEST_MAIN(tst_WebGL) + +#include "tst_webgl.moc" diff --git a/tests/plugins/platforms/webgl/webgl.pro b/tests/plugins/platforms/webgl/webgl.pro new file mode 100644 index 0000000..add4016 --- /dev/null +++ b/tests/plugins/platforms/webgl/webgl.pro @@ -0,0 +1,19 @@ +CONFIG += testcase + +QT += \ + testlib \ + quick \ + websockets + +TARGET = tst_webgl + +HEADERS += \ + parameters.h + +SOURCES += \ + tst_webgl.cpp + +TESTDATA = *.qml + +OTHER_FILES += \ + basic_scene.qml diff --git a/tests/plugins/plugins.pro b/tests/plugins/plugins.pro new file mode 100644 index 0000000..2616dc4 --- /dev/null +++ b/tests/plugins/plugins.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + platforms diff --git a/tests/tests.pro b/tests/tests.pro index 566e172..22508cc 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -1,2 +1,4 @@ TEMPLATE = subdirs +SUBDIRS += \ + plugins |