From 953272eb450e2d6b00128308bfc3ce4cacbb641d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20S=C3=B8rvig?= Date: Wed, 6 Jul 2022 23:05:15 +0200 Subject: wasm: add manual auto-test for websockify-tunneled sockets Test TCP sockets usage on the main thread, on secondary threads, and with asyncify. Pick-to: 6.4 Change-Id: I466df8c253c6a18a9c12d44fa8f53e76f81a0437 Reviewed-by: Lorn Potter --- .../network/sockify_sockets_auto/CMakeLists.txt | 22 ++ .../wasm/network/sockify_sockets_auto/main.cpp | 365 +++++++++++++++++++++ .../sockify_sockets_auto/sockify_sockets_auto.html | 17 + 3 files changed, 404 insertions(+) create mode 100644 tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt create mode 100644 tests/manual/wasm/network/sockify_sockets_auto/main.cpp create mode 100644 tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html (limited to 'tests/manual') diff --git a/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt b/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt new file mode 100644 index 0000000000..f69bd80259 --- /dev/null +++ b/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt @@ -0,0 +1,22 @@ +qt_internal_add_manual_test(sockify_sockets_auto + SOURCES + main.cpp + ../../qtwasmtestlib/qtwasmtestlib.cpp + PUBLIC_LIBRARIES + Qt::Core + Qt::Network +) + +include_directories(../../qtwasmtestlib/) + +add_custom_command( + TARGET sockify_sockets_auto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/sockify_sockets_auto.html + ${CMAKE_CURRENT_BINARY_DIR}/sockify_sockets_auto.html) + +add_custom_command( + TARGET sockify_sockets_auto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/../../qtwasmtestlib/qtwasmtestlib.js + ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js) diff --git a/tests/manual/wasm/network/sockify_sockets_auto/main.cpp b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp new file mode 100644 index 0000000000..2bd4f2c3ac --- /dev/null +++ b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +const int socketWait = 1000; +const QString hostName = "localhost"; +const int port = 1515; + +class SockifySocketsTest: public QObject +{ + Q_OBJECT + +private slots: + void echo(); + void echoMultipleMessages(); + void echoMultipleSockets(); + void remoteClose(); + +#if QT_CONFIG(thread) + void thread_echo(); + void thread_remoteClose(); + void thread_echoMultipleSockets(); +#endif + +#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY + void asyncify_echo(); + void asyncify_remoteClose(); +#endif +}; + +class CompleteTestFunctionRefGuard { +public: + CompleteTestFunctionRefGuard(CompleteTestFunctionRefGuard const&) = delete; + CompleteTestFunctionRefGuard& operator=(CompleteTestFunctionRefGuard const&) = delete; + + static CompleteTestFunctionRefGuard *create() { + return new CompleteTestFunctionRefGuard(); + } + + void ref() { + QMutexLocker lock(&mutex); + ++counter; + } + + void deref() { + bool itsTheFinalDeref = [this] { + QMutexLocker lock(&mutex); + return --counter == 0; + }(); + + if (itsTheFinalDeref) { + delete this; + QtWasmTest::completeTestFunction(); + } + } +private: + CompleteTestFunctionRefGuard() { }; + + QMutex mutex; + int counter = 0; +}; + +#if QT_CONFIG(thread) + +class TestThread : public QThread +{ +public: + static QThread *create(std::function started, std::function finished) + { + TestThread *thread = new TestThread(); + connect(thread, &QThread::started, [started]() { + started(); + }); + connect(thread, &QThread::finished, [thread, finished]() { + finished(); + thread->deleteLater(); + }); + thread->start(); + return thread; + } +}; + +#endif + +void blockingEchoTest() +{ + QTcpSocket socket; + socket.connectToHost(hostName, port); + if (!socket.waitForConnected(socketWait)) + qFatal("socket connect error"); + + QByteArray message = "Hello, echo server!"; + + QByteArray command = "echo:" + message + ';'; + socket.write(command); + socket.flush(); + + socket.waitForReadyRead(socketWait); + QByteArray expectedReply = message + ';'; + QByteArray reply = socket.readAll(); + if (reply != expectedReply) + qFatal("echo_multiple received incorrect reply"); + socket.disconnectFromHost(); +} + +void blockingRemoteClose() +{ + QTcpSocket socket; + + qDebug() << "## connectToHost"; + socket.connectToHost(hostName, port); + + qDebug() << "## waitForConnected"; + socket.waitForConnected(socketWait); + socket.write("close;"); + socket.flush(); + + qDebug() << "## waitForBytesWritten"; + socket.waitForBytesWritten(socketWait); + + qDebug() << "## waitForReadyRead"; + socket.waitForReadyRead(200); + + qDebug() << "## waitForDisconnected"; + socket.waitForDisconnected(socketWait); + qDebug() << "## done"; +} + +// Verify that sending one echo command and receiving the reply works +void SockifySocketsTest::echo() +{ + QTcpSocket *socket = new QTcpSocket(); + socket->connectToHost(hostName, port); + + QByteArray message = "Hello, echo server!"; + + QObject::connect(socket, &QAbstractSocket::connected, [socket, message]() { + QByteArray command = "echo:" + message + ';'; + socket->write(command); + socket->flush(); + }); + + QByteArray *reply = new QByteArray(); + QObject::connect(socket, &QIODevice::readyRead, [socket, reply, message]() { + *reply += socket->readAll(); + if (reply->contains(';')) { + bool match = (*reply == message + ';'); + socket->disconnectFromHost(); + socket->deleteLater(); + delete reply; + QtWasmTest::completeTestFunction(match ? QtWasmTest::TestResult::Pass : QtWasmTest::TestResult::Fail, std::string()); + } + }); +} + +void SockifySocketsTest::echoMultipleMessages() +{ + const int count = 20; + + QTcpSocket *socket = new QTcpSocket(); + socket->connectToHost(hostName, port); + QByteArray message = "Hello, echo server!"; + + QObject::connect(socket, &QAbstractSocket::connected, [socket, message]() { + QByteArray command = "echo:" + message + ';'; + for (int i = 0; i < count; ++i) { + quint64 written = socket->write(command); + if (written != quint64(command.size())) + qFatal("Unable to write to socket"); + } + socket->flush(); + }); + + QByteArray expectedReply; + for (int i = 0; i < count; ++i) + expectedReply += (message + ';'); + QByteArray *receivedReply = new QByteArray; + QObject::connect(socket, &QIODevice::readyRead, [socket, receivedReply, expectedReply]() { + QByteArray reply = socket->readAll(); + *receivedReply += reply; + + if (*receivedReply == expectedReply) { + socket->disconnectFromHost(); + socket->deleteLater(); + delete receivedReply; + QtWasmTest::completeTestFunction(); + } + }); +} + +void SockifySocketsTest::echoMultipleSockets() +{ + const int connections = 5; + auto guard = CompleteTestFunctionRefGuard::create(); + + QByteArray message = "Hello, echo server!"; + + for (int i = 0; i < connections; ++i) { + guard->ref(); + + QTcpSocket *socket = new QTcpSocket(); + socket->connectToHost(hostName, port); + + QObject::connect(socket, &QAbstractSocket::connected, [socket, message]() { + QByteArray command = "echo:" + message + ';'; + socket->write(command); + socket->flush(); + }); + + QObject::connect(socket, &QIODevice::readyRead, [guard, socket, message]() { + QByteArray reply = socket->readAll(); + socket->disconnectFromHost(); + socket->deleteLater(); + if (reply != (message + ';')) + qFatal("echo_multiple received incorrect reply"); + guard->deref(); + }); + } +} + +void SockifySocketsTest::remoteClose() +{ + QTcpSocket *socket = new QTcpSocket(); + socket->connectToHost(hostName, port); + QObject::connect(socket, &QAbstractSocket::connected, [socket]() { + socket->write("close;"); + socket->flush(); + }); + QObject::connect(socket, &QAbstractSocket::disconnected, [socket]() { + qDebug() << "disconnected"; + socket->deleteLater(); + QtWasmTest::completeTestFunction(); + }); +} + +#if QT_CONFIG(thread) + +void SockifySocketsTest::thread_echo() +{ + auto started = []() { + blockingEchoTest(); + QThread::currentThread()->quit(); + }; + + auto finished = [](){ + QtWasmTest::completeTestFunction(); + }; + + TestThread::create(started, finished); +} + +void SockifySocketsTest::thread_echoMultipleSockets() +{ + const int connections = 2; // TODO: test more threads + auto guard = CompleteTestFunctionRefGuard::create(); + guard->ref(); + + for (int i = 0; i < connections; ++i) { + guard->ref(); + auto started = [](){ + blockingEchoTest(); + QThread::currentThread()->quit(); + }; + + auto finished = [guard](){ + guard->deref(); + }; + + TestThread::create(started, finished); + } + + guard->deref(); +} + +void SockifySocketsTest::thread_remoteClose() +{ + auto started = [](){ + blockingRemoteClose(); + QThread::currentThread()->quit(); + }; + + auto finished = [](){ + QtWasmTest::completeTestFunction(); + }; + + TestThread::create(started, finished); +} + +#endif + +#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY + +// Post an event to the main thread and asyncify wait for it +void SockifySocketsTest::asyncify_echo() +{ + blockingEchoTest(); + QtWasmTest::completeTestFunction(); +} + +void SockifySocketsTest::asyncify_remoteClose() +{ + blockingRemoteClose(); + QtWasmTest::completeTestFunction(); +} + +#endif + +int main(int argc, char **argv) +{ + auto testObject = std::make_shared(); + QtWasmTest::initTestCase(argc, argv, testObject); + return 0; +} + +#include "main.moc" diff --git a/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html new file mode 100644 index 0000000000..5376249a71 --- /dev/null +++ b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html @@ -0,0 +1,17 @@ + + + + +

Sockify tunneled sockets auto test. + +

This test requires running echo_server and websockify (or equivalent) on the host: +

+    /path/to/qtbase/tests/manual/wasm/network/echo_server/echo_server
+    websockify 1515 localhost:1516
+
+ +
-- cgit v1.2.3