diff options
Diffstat (limited to 'tests/manual/wasm/network')
10 files changed, 569 insertions, 0 deletions
diff --git a/tests/manual/wasm/network/CMakeLists.txt b/tests/manual/wasm/network/CMakeLists.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/manual/wasm/network/CMakeLists.txt diff --git a/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt b/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt new file mode 100644 index 0000000000..05416c0b66 --- /dev/null +++ b/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt @@ -0,0 +1,8 @@ +qt_internal_add_manual_test(echo_client_mainthread + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Network +) diff --git a/tests/manual/wasm/network/echo_client_mainthread/main.cpp b/tests/manual/wasm/network/echo_client_mainthread/main.cpp new file mode 100644 index 0000000000..ef696e5978 --- /dev/null +++ b/tests/manual/wasm/network/echo_client_mainthread/main.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore> +#include <QtNetwork> + +int main(int argc, char **argv) { + + QCoreApplication app(argc, argv); + + // This example connects to localhost, but note that the host can + // be any host reachable from the client using webscokets, at any port. + QString hostName = "localhost"; + int port = 1515; + qDebug() << "This example connects to a server at" << hostName << "port" << port << "," + << "where it expects to find a WebSockify server, which forwards to the fortune server."; + + auto echo = [hostName, port]() { + QTcpSocket *socket = new QTcpSocket(); + + QObject::connect(socket, &QAbstractSocket::connected, [socket]() { + qDebug() << "Connected"; + socket->write("Hello, echo server!"); + socket->flush(); + }); + + QObject::connect(socket, &QIODevice::readyRead, [socket]() { + QByteArray data = socket->readAll(); + qDebug() << "Ready Read, got echo:" << data; + socket->disconnectFromHost(); + socket->deleteLater(); + }); + + QObject::connect(socket, &QAbstractSocket::errorOccurred, [socket]() { + qDebug() << "Error Occurred" << socket->error(); + }); + + QObject::connect(socket, &QAbstractSocket::disconnected, [socket]() { + qDebug() << "Disconnected"; + socket->deleteLater(); + }); + + qDebug() << "Connect to host" << hostName << port; + socket->connectToHost(hostName, port); + }; + + QTimer::singleShot(500, [echo](){ + echo(); + }); + + return app.exec(); +} diff --git a/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt b/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt new file mode 100644 index 0000000000..a1f2bef254 --- /dev/null +++ b/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt @@ -0,0 +1,8 @@ +qt_internal_add_manual_test(echo_client_secondarythread + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Network +) diff --git a/tests/manual/wasm/network/echo_client_secondarythread/main.cpp b/tests/manual/wasm/network/echo_client_secondarythread/main.cpp new file mode 100644 index 0000000000..52cea93495 --- /dev/null +++ b/tests/manual/wasm/network/echo_client_secondarythread/main.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore> +#include <QtNetwork> + +int main(int argc, char **argv) { + + QCoreApplication app(argc, argv); + + // This example connects to localhost, but note that the host can + // be any host reachable from the client using webscokets, at any port. + QString hostName = "localhost"; + int port = 1515; + qDebug() << "## This example connects to a server at" << hostName << "port" << port << "," + << "where it expects to find a WebSockify server, which forwards to the fortune server."; + + auto echo = [hostName, port]() { + qDebug() << "Connecting to" << hostName << port; + + QTcpSocket socket; + socket.connectToHost(hostName, port); + bool connected = socket.waitForConnected(3000); + if (!connected) { + qDebug() << "connect failure"; + return; + } + + qDebug() << "Connected"; + socket.write("echo:Hello, echo server!;"); + socket.flush(); + + qDebug() << "Calling waitForReadyRead()"; + socket.waitForReadyRead(20000); + QByteArray data = socket.readAll(); + qDebug() << "Got echo:" << data; + + socket.disconnectFromHost(); + socket.deleteLater(); + qDebug() << "Disconnected"; + }; + + QThread thread; + QObject::connect(&thread, &QThread::started, [echo](){ + echo(); + }); + thread.start(); + + app.exec(); +} diff --git a/tests/manual/wasm/network/echo_server/CMakeLists.txt b/tests/manual/wasm/network/echo_server/CMakeLists.txt new file mode 100644 index 0000000000..cf98163fb8 --- /dev/null +++ b/tests/manual/wasm/network/echo_server/CMakeLists.txt @@ -0,0 +1,14 @@ +project(echo_server) +cmake_minimum_required(VERSION 3.19) + +find_package(Qt6 COMPONENTS Core) +find_package(Qt6 COMPONENTS network) + +qt_add_executable(echo_server + main.cpp +) + +target_link_libraries(echo_server PUBLIC + Qt::Core + Qt::Network +) diff --git a/tests/manual/wasm/network/echo_server/main.cpp b/tests/manual/wasm/network/echo_server/main.cpp new file mode 100644 index 0000000000..3a67cabc79 --- /dev/null +++ b/tests/manual/wasm/network/echo_server/main.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore> +#include <QtNetwork> + +const int timeout = 60 * 1000; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QTcpServer server; + QObject::connect(&server, &QTcpServer::newConnection, [&server](){ + qDebug() << "new connection"; + + QByteArray *receiveBuffer = new QByteArray(); + + QTcpSocket *socket = server.nextPendingConnection(); + QObject::connect(socket, &QIODevice::readyRead, [socket, receiveBuffer](){ + + // This implements a very simple command protocol, where the server + // processes a stream of commands delimited by ';', and then performs + // an action in reply. The supported commands with actions are: + // + // echo:<message>; writes the received <message> back + // close; closes the socket + // + + // We might receive multiple or partial commands; read all available data + // and then scan the buffer for complete commands. + QByteArray newData = socket->readAll(); + *receiveBuffer += newData; + + int pos = receiveBuffer->indexOf(";"); + while (pos != -1) { + QByteArray command = receiveBuffer->left(pos); + receiveBuffer->remove(0, pos + 1); + pos = receiveBuffer->indexOf(";"); + + if (command.startsWith("echo")) { + // Echo expects echo:<message> + QList<QByteArray> parts = command.split(':'); + QByteArray reply = parts.last() + ';'; + qDebug() << "Command: echo:" << parts.last(); + socket->write(reply); + socket->flush(); + + } else if (command.startsWith("close")) { + qDebug() << "Command: close"; + socket->write("bye!;"); + socket->flush(); + socket->close(); + break; + } else { + qDebug() << "Unknown command:" << command; + } + } + }); + + QObject::connect(socket, &QAbstractSocket::disconnected, [socket, receiveBuffer](){ + delete receiveBuffer; + socket->deleteLater(); + }); + }); + + // This is example is intended to be used together with WebSockify on + // the server and acts as a counterpart to the client examples which + // run in the browser. (This example does not run in the browser). + + qDebug() << "\nStarting echo server at port 1516. You should now start the" + << "\nWebSockify forwarding server, and then connect from one of" + << "\nthe client examples." + << "\n websockify 1515 localhost:1516"; + + server.listen(QHostAddress::Any, 1516); + + return app.exec(); +} + 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..fb9a9f8543 --- /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 + 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..b6aa232b4a --- /dev/null +++ b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp @@ -0,0 +1,318 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <qtwasmtestlib.h> +#include <QtCore> +#include <QtNetwork> + +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<void()> started, std::function<void()> 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<SockifySocketsTest>(); + QtWasmTest::initTestCase<QCoreApplication>(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..080ada94e7 --- /dev/null +++ b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html @@ -0,0 +1,17 @@ +<!doctype html> +<script type="text/javascript" src="qtwasmtestlib.js"></script> +<script type="text/javascript" src="sockify_sockets_auto.js"></script> +<script> + window.onload = async () => { + runTestCase(sockify_sockets_auto_entry, document.getElementById("log")); + }; +</script> +<p> Sockify tunneled sockets auto test. + +<p>This test requires running echo_server and <a href=https://github.com/novnc/websockify>websockify</a> (or equivalent) on the host: +<pre> + /path/to/qtbase/tests/manual/wasm/network/echo_server/echo_server + websockify 1515 localhost:1516 +</pre> + +<div id="log"></div> |