diff options
Diffstat (limited to 'tests/manual/wasm')
67 files changed, 1359 insertions, 794 deletions
diff --git a/tests/manual/wasm/CMakeLists.txt b/tests/manual/wasm/CMakeLists.txt index f625defb21..b13f6781b8 100644 --- a/tests/manual/wasm/CMakeLists.txt +++ b/tests/manual/wasm/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(a11y) if(QT_FEATURE_widgets) add_subdirectory(cursors) add_subdirectory(localfiles) +add_subdirectory(localfonts) add_subdirectory(qstdweb) add_subdirectory(clipboard) endif() diff --git a/tests/manual/wasm/a11y/CMakeLists.txt b/tests/manual/wasm/a11y/CMakeLists.txt index 5268d53c8b..dee39e1f5f 100644 --- a/tests/manual/wasm/a11y/CMakeLists.txt +++ b/tests/manual/wasm/a11y/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(QT_FEATURE_widgets) add_subdirectory(basic_widgets) endif() diff --git a/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt b/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt index 11534bdf68..a028f96e1c 100644 --- a/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt +++ b/tests/manual/wasm/a11y/basic_widgets/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_manual_test(a11y_basic_widgets GUI SOURCES diff --git a/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html b/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html index 899e72bb00..091809be5c 100644 --- a/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html +++ b/tests/manual/wasm/a11y/basic_widgets/basic_widgets.html @@ -7,7 +7,7 @@ <script> window.onload = async () => { - let qt_instance = await createQtAppInstance({ + let qt_instance = await a11y_basic_widgets_entry({ qtContainerElements: [document.getElementById("qt_container")], }); } diff --git a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp index eaf736160a..dc1688f5b9 100644 --- a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp +++ b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "basica11ywidget.h" diff --git a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h index c8d9a4157b..b990d163e5 100644 --- a/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h +++ b/tests/manual/wasm/a11y/basic_widgets/basica11ywidget.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtWidgets> #include "tabswidget.h" diff --git a/tests/manual/wasm/a11y/basic_widgets/main.cpp b/tests/manual/wasm/a11y/basic_widgets/main.cpp index 5417195ecf..52d72428bb 100644 --- a/tests/manual/wasm/a11y/basic_widgets/main.cpp +++ b/tests/manual/wasm/a11y/basic_widgets/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> #include <QtWidgets> diff --git a/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp b/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp index 19bd0cf71b..63428c965a 100644 --- a/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp +++ b/tests/manual/wasm/a11y/basic_widgets/tabswidget.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tabswidget.h" diff --git a/tests/manual/wasm/a11y/basic_widgets/tabswidget.h b/tests/manual/wasm/a11y/basic_widgets/tabswidget.h index 0d7983cb42..6405c0dab7 100644 --- a/tests/manual/wasm/a11y/basic_widgets/tabswidget.h +++ b/tests/manual/wasm/a11y/basic_widgets/tabswidget.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TABDIALOG_H #define TABDIALOG_H diff --git a/tests/manual/wasm/clipboard/CMakeLists.txt b/tests/manual/wasm/clipboard/CMakeLists.txt index 80e049097b..40fb8ca308 100644 --- a/tests/manual/wasm/clipboard/CMakeLists.txt +++ b/tests/manual/wasm/clipboard/CMakeLists.txt @@ -10,6 +10,8 @@ qt_internal_add_manual_test(clipboard SOURCES main.cpp mainwindow.cpp mainwindow.h mainwindow.ui + NO_PCH_SOURCES + main.cpp # undef QT_NO_FOREACH LIBRARIES Qt::Core Qt::Gui diff --git a/tests/manual/wasm/clipboard/main.cpp b/tests/manual/wasm/clipboard/main.cpp index 7142125ff7..aa838f6670 100644 --- a/tests/manual/wasm/clipboard/main.cpp +++ b/tests/manual/wasm/clipboard/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "mainwindow.h" diff --git a/tests/manual/wasm/clipboard/mainwindow.cpp b/tests/manual/wasm/clipboard/mainwindow.cpp index 77d6582775..c67c795b05 100644 --- a/tests/manual/wasm/clipboard/mainwindow.cpp +++ b/tests/manual/wasm/clipboard/mainwindow.cpp @@ -1,5 +1,7 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "mainwindow.h" #include "ui_mainwindow.h" @@ -12,6 +14,7 @@ #include <QKeyEvent> #include <QMimeDatabase> #include <QFileInfo> +#include <QCryptographicHash> #ifdef Q_OS_WASM #include <emscripten.h> @@ -262,20 +265,23 @@ void MainWindow::dropEvent(QDropEvent* e) QString urlStr = url.toDisplayString(); int size = urlStr.length(); sizeStr.setNum(size); + + QString fileName = url.toLocalFile(); + QString sha1; + QFile file(fileName); + if (file.exists()) { + file.open(QFile::ReadOnly); + sha1 = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Sha1).toHex(); + } + ui->textEdit_2->insertPlainText(" Drop has url data length: " + sizeStr + "\n"); - ui->textEdit_2->insertPlainText(urlStr + "\n"); - - QString fname = url.toLocalFile(); - QFileInfo info(fname); - if (info.exists()) { // this is a file - QMimeDatabase db; - QMimeType mt = db.mimeTypeForFile(info); - if (mt.name().contains("image")) { - QImage image = QImage(fname); - setImage(image); - } + if (sha1.isEmpty()) { + ui->textEdit->insertPlainText(urlStr); } + ui->textEdit_2->insertPlainText(" " + urlStr + (!sha1.isEmpty() ? " sha1 " + sha1.left(8) : "") + "\n"); + } + ui->textEdit_2->insertPlainText("\n"); if (e->mimeData()->hasImage()) { qsizetype imageSize = qvariant_cast<QImage>(e->mimeData()->imageData()).sizeInBytes(); @@ -292,14 +298,15 @@ void MainWindow::dropEvent(QDropEvent* e) int size = e->mimeData()->html().length(); sizeStr.setNum(size); ui->textEdit_2->insertPlainText(" Drop has html data length: " + sizeStr + "\n"); - ui->textEdit_2->insertPlainText(e->mimeData()->html()+"\n"); - ui->textEdit->insertHtml(e->mimeData()->html()+"<br>"); + for (const auto &line : e->mimeData()->html().split('\n', Qt::SkipEmptyParts)) + ui->textEdit_2->insertPlainText(" " + line + "\n"); } if (e->mimeData()->hasText()) { int size = e->mimeData()->text().length(); sizeStr.setNum(size); ui->textEdit_2->insertPlainText(" Drop has text data length: " + sizeStr + "\n"); - ui->textEdit_2->insertPlainText(e->mimeData()->text()); + for (const auto &line : e->mimeData()->text().split('\n', Qt::SkipEmptyParts)) + ui->textEdit_2->insertPlainText(" " + line + "\n"); } const QString message = tr(" Drop accepted, %1 ") diff --git a/tests/manual/wasm/clipboard/mainwindow.h b/tests/manual/wasm/clipboard/mainwindow.h index d06b213971..fe101ad494 100644 --- a/tests/manual/wasm/clipboard/mainwindow.h +++ b/tests/manual/wasm/clipboard/mainwindow.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef MAINWINDOW_H #define MAINWINDOW_H diff --git a/tests/manual/wasm/cursors/MainWindow.cpp b/tests/manual/wasm/cursors/MainWindow.cpp index b62c6752aa..c6e4fbcca1 100644 --- a/tests/manual/wasm/cursors/MainWindow.cpp +++ b/tests/manual/wasm/cursors/MainWindow.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "MainWindow.h" #include "ui_MainWindow.h" diff --git a/tests/manual/wasm/cursors/MainWindow.h b/tests/manual/wasm/cursors/MainWindow.h index ed570a72c4..ebaeed9e5c 100644 --- a/tests/manual/wasm/cursors/MainWindow.h +++ b/tests/manual/wasm/cursors/MainWindow.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #pragma once #include <QMainWindow> diff --git a/tests/manual/wasm/cursors/main.cpp b/tests/manual/wasm/cursors/main.cpp index 99a1d41524..9a59cdd994 100644 --- a/tests/manual/wasm/cursors/main.cpp +++ b/tests/manual/wasm/cursors/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "MainWindow.h" #include <QApplication> diff --git a/tests/manual/wasm/eventloop/asyncify_exec/main.cpp b/tests/manual/wasm/eventloop/asyncify_exec/main.cpp index ab3018c12e..f09163184d 100644 --- a/tests/manual/wasm/eventloop/asyncify_exec/main.cpp +++ b/tests/manual/wasm/eventloop/asyncify_exec/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> // This test shows how to use asyncify to enable blocking the main diff --git a/tests/manual/wasm/eventloop/dialog_exec/main.cpp b/tests/manual/wasm/eventloop/dialog_exec/main.cpp index adf8a02c37..f5b072fc0b 100644 --- a/tests/manual/wasm/eventloop/dialog_exec/main.cpp +++ b/tests/manual/wasm/eventloop/dialog_exec/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> #include <QtWidgets> diff --git a/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt b/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt index 9bfa875be7..88ddfac4c8 100644 --- a/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt +++ b/tests/manual/wasm/eventloop/eventloop_auto/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause include_directories(../../qtwasmtestlib/) # default buid diff --git a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html index 7ff9d8e7f2..e8e35abcbb 100644 --- a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html +++ b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto.html @@ -3,7 +3,7 @@ <script type="text/javascript" src="eventloop_auto.js"></script> <script> window.onload = () => { - runTestCase(document.getElementById("log")); + runTestCase(eventloop_auto_entry, document.getElementById("log")); }; </script> <p>Running event dispatcher auto test.</p> diff --git a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html index a277c9b9e0..f09b29d85b 100644 --- a/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html +++ b/tests/manual/wasm/eventloop/eventloop_auto/eventloop_auto_asyncify.html @@ -3,7 +3,7 @@ <script type="text/javascript" src="eventloop_auto_asyncify.js"></script> <script> window.onload = () => { - runTestCase(document.getElementById("log")); + runTestCase(eventloop_auto_asyncify_entry, document.getElementById("log")); }; </script> <p>Running event dispatcher auto test.</p> diff --git a/tests/manual/wasm/eventloop/eventloop_auto/main.cpp b/tests/manual/wasm/eventloop/eventloop_auto/main.cpp index b2ff6779fd..32af372b62 100644 --- a/tests/manual/wasm/eventloop/eventloop_auto/main.cpp +++ b/tests/manual/wasm/eventloop/eventloop_auto/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QtCore/QEvent> diff --git a/tests/manual/wasm/eventloop/main_exec/main.cpp b/tests/manual/wasm/eventloop/main_exec/main.cpp index c981fd4c2c..17eccafe18 100644 --- a/tests/manual/wasm/eventloop/main_exec/main.cpp +++ b/tests/manual/wasm/eventloop/main_exec/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> // This example demonstrates how the standard Qt main() diff --git a/tests/manual/wasm/eventloop/main_noexec/main.cpp b/tests/manual/wasm/eventloop/main_noexec/main.cpp index 18d0542137..6ddd88bd14 100644 --- a/tests/manual/wasm/eventloop/main_noexec/main.cpp +++ b/tests/manual/wasm/eventloop/main_noexec/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> // This example demonstrates how to create QGuiApplication diff --git a/tests/manual/wasm/eventloop/thread_exec/main.cpp b/tests/manual/wasm/eventloop/thread_exec/main.cpp index b24be17e5b..589066b34d 100644 --- a/tests/manual/wasm/eventloop/thread_exec/main.cpp +++ b/tests/manual/wasm/eventloop/thread_exec/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> class EventTarget : public QObject diff --git a/tests/manual/wasm/localfiles/main.cpp b/tests/manual/wasm/localfiles/main.cpp index 39d9f21901..862bff50a4 100644 --- a/tests/manual/wasm/localfiles/main.cpp +++ b/tests/manual/wasm/localfiles/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtWidgets/QtWidgets> #include <emscripten/val.h> @@ -91,7 +91,8 @@ private Q_SLOTS: { QFileDialog::getOpenFileContent( m_filterEdit->text(), - std::bind(&AppWindow::onFileContentReady, this, std::placeholders::_1, std::placeholders::_2)); + std::bind(&AppWindow::onFileContentReady, this, std::placeholders::_1, std::placeholders::_2), + &m_loadFileUi); } void onSaveClicked() diff --git a/tests/manual/wasm/localfonts/CMakeLists.txt b/tests/manual/wasm/localfonts/CMakeLists.txt new file mode 100644 index 0000000000..b5df4ad9fa --- /dev/null +++ b/tests/manual/wasm/localfonts/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(fontloading) diff --git a/tests/manual/wasm/localfonts/fontloading/CMakeLists.txt b/tests/manual/wasm/localfonts/fontloading/CMakeLists.txt new file mode 100644 index 0000000000..c3dc37d27d --- /dev/null +++ b/tests/manual/wasm/localfonts/fontloading/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_manual_test(fontloading + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::Widgets +) + +add_custom_command( + TARGET fontloading POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/fontloading.html + ${CMAKE_CURRENT_BINARY_DIR}/fontloading.html) +#add_custom_target(html DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fontloading.html) +#add_dependencies(fontloading html) diff --git a/tests/manual/wasm/localfonts/fontloading/fontloading.html b/tests/manual/wasm/localfonts/fontloading/fontloading.html new file mode 100644 index 0000000000..619217205b --- /dev/null +++ b/tests/manual/wasm/localfonts/fontloading/fontloading.html @@ -0,0 +1,167 @@ +<!doctype html> + +<script src="qtloader.js"></script> +<script src="fontloading.js"></script> + +<style> + body { + padding: 5px; + } + + .container { + display: flex; + } + + .column { + flex: 1; + } +</style> + + +<script> + + // UI + let familyCount; + let families; + + // Data + let fontFamilies = new Set(); + let loadStartTime; + + // App + let instance; + + async function updatePermissionStatus() { + let permissonStatusElement = document.getElementById("permissonStatus"); + let permissionStatus = await navigator.permissions.query({ name: "local-fonts" }) + permissonStatusElement.innerHTML = permissionStatus.state; + } + + window.onload = async () => { + let supported = document.getElementById("supported"); + let permissonStatus = document.getElementById("permissonStatus"); + let permission = document.getElementById("permission"); + let defaultfonts = document.getElementById("defaultfonts"); + let allfonts = document.getElementById("allfonts"); + let start = document.getElementById("start"); + let loadFonts = document.getElementById("loadFonts"); + startupTime = document.getElementById("startupTime"); + familyCount = document.getElementById("familyCount"); + families = document.getElementById("families"); + + fontFamilies.clear(); + + let localFontsAccessSupported = window.queryLocalFonts ? true : false + if (localFontsAccessSupported) { + supported.innerHTML = "True" + } else { + supported.innerHTML = "False" + return; + } + + updatePermissionStatus(); + + const module = WebAssembly.compileStreaming(fetch('fontloading.wasm')); + + start.onclick = async () => { + + // Delete any previous instance. + if (instance) { + instance.deleteapp(); // see main.cpp + instance = null; + } + + loadStartTime = performance.now(); + startupTime.innerHTML = 0; + familyCount.innerHTML = 0; + let localFontFamilyLoadCollection = "NoFontFamilies" + if (defaultfonts.checked) + localFontFamilyLoadCollection = "DefaultFontFamilies" + else if (allfonts.checked) + localFontFamilyLoadCollection = "AllFontFamilies" + + let qtcontainer = document.getElementById("qtcontainer"); + qtcontainer.innerHTML = ""; // clear children + qtcontainer.style.visibility = "hidden"; + + let extraFonts = document.getElementById("extrafonts").value.split(","); + + let config = { + qt: { + module: module, + containerElements: [qtcontainer], + onLoaded: () => { + console.log("JS: onLoaded") + qtcontainer.style.visibility = "visible"; + }, + entryFunction: window.fontloading_entry, + localFonts: { + requestPermission: permission.checked, + familiesCollection: localFontFamilyLoadCollection, + extraFamilies: extraFonts, + } + } + } + instance = await qtLoad(config); + + updatePermissionStatus(); + } + + loadFonts.onclick = async () => { + loadStartTime = null; // disable timer + let fontsFamilies = document.getElementById("extraRuntimeFontFamilies").value.split(","); + console.log("extraRuntimeFontFamilies: " + fontsFamilies); + instance.qtLoadLocalFontFamilies(fontsFamilies); + } + }; + + function fontFamiliesLoaded(count) { + familyCount.innerHTML = count; + if (loadStartTime) { + elapsed = performance.now() - loadStartTime; + startupTime.innerHTML = Math.round(elapsed + 1); + } + } + + function fontFamilyLoaded(family) { + fontFamilies.add(family); + } + +</script> + +<h2>Local Font Loading Test</h2> +<p>Click "Load" button below to load the Qt test app with the specified settings. This test provides additional logs on the JavaScript console.</p> + +<div class="container"> + <div class="column"> + <span>Browser supports the Local Font Access API: </span><span id="supported" style="font-weight: bold;"></span><br> + <span>Local Font Access permission status: </span><span id="permissonStatus" style="font-weight: bold;"></span><br> + <br> + <input type="checkbox" id="permission"><label for="permission">Ask for Local Font access permission on startup</label><br> + <input type="radio" id="nofonts" name="fontset"></input><label for="nofonts">No local fonts</label><br> + <input type="radio" id="defaultfonts" name="fontset" checked></input><label for="defaultfonts">Default local fonts (web-safe fonts)</label><br> + <input type="radio" id="allfonts" name="fontset"></input><label for="allfonts">All local fonts (warning: extremely slow)</label><br> + <br> + <label for="extrafonts">Extra Font Families (comma separated) </label><input type="text" id="extrafonts" value=""></input><br> + <br> + <input type="checkbox" id="permission"><label for="permission">Enable 'Fonts' Logging Category</label><br> + <input type="checkbox" id="permission"><label for="permission">Enable Font Streaming</label><br> + <br> + <button type="button" id="start">Start Application</button><br> + <br> + + <span>Startup time: </span><span id="startupTime"></span><br> + <span>Font family count: </span><span id="familyCount"></span><br> + <span>Font families: </span><span id="families"></span><br> + <br> + + <button type="button" id="loadFonts">Load Extra Fonts</button> + <input type="text" id="extraRuntimeFontFamilies" value=""></input><br> + </div> + + <div class="column"> + <div id="qtcontainer" style="width: 100%; height: 300px; visibility: hidden;"></div> + </div> +</div> + + diff --git a/tests/manual/wasm/localfonts/fontloading/main.cpp b/tests/manual/wasm/localfonts/fontloading/main.cpp new file mode 100644 index 0000000000..3824c8b871 --- /dev/null +++ b/tests/manual/wasm/localfonts/fontloading/main.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtGui> +#include <QtWidgets> + +#include <emscripten/bind.h> +#include <emscripten/val.h> + +using namespace emscripten; + +class FontViewer : public QWidget +{ +public: + FontViewer() { + QTextEdit *edit = new QTextEdit; + edit->setPlainText("The quick brown fox jumps over the lazy dog\nHow quickly daft jumping zebras vex\nPack my box with five dozen liquor jugs"); + + QComboBox *combo = new QComboBox; + combo->addItems(QFontDatabase::families()); + + connect(combo, &QComboBox::currentTextChanged, [=](const QString &family) { + QFont font(family); + edit->setFont(font); + }); + + QObject::connect(qApp, &QGuiApplication::fontDatabaseChanged, [=]() { + QStringList families = QFontDatabase::families(); + combo->clear(); + combo->addItems(families); + }); + + QLayout *layout = new QVBoxLayout; + layout->addWidget(edit); + layout->addWidget(combo); + setLayout(layout); + } +}; + +FontViewer *g_viewer = nullptr; +QApplication *g_app = nullptr; + +void deleteapp() { + delete g_viewer; + delete g_app; +}; + +EMSCRIPTEN_BINDINGS(fonloading) { + function("deleteapp", &deleteapp); +} + +int main(int argc, char **argv) +{ + qDebug() << "C++ main: Creating application"; + g_app = new QApplication(argc, argv); + + // Make sure there is one call to fontFamiliesLoaded at startup, + // even if no further fonts are loaded. + QTimer::singleShot(0, [=]() { + emscripten::val window = emscripten::val::global("window"); + window.call<void>("fontFamiliesLoaded", QFontDatabase::families().count()); + }); + + g_viewer = new FontViewer(); + g_viewer->show(); + + QObject::connect(g_app, &QGuiApplication::fontDatabaseChanged, [=]() { + QStringList families = QFontDatabase::families(); + + emscripten::val window = emscripten::val::global("window"); + + window.call<void>("fontFamiliesLoaded", families.count()); + for (int i = 0; i < families.count(); ++i) { + window.call<void>("fontFamilyLoaded", families[i].toStdString()); + } + }); +} + diff --git a/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt b/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt index 05416c0b66..4b81661c79 100644 --- a/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt +++ b/tests/manual/wasm/network/echo_client_mainthread/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_manual_test(echo_client_mainthread GUI SOURCES diff --git a/tests/manual/wasm/network/echo_client_mainthread/main.cpp b/tests/manual/wasm/network/echo_client_mainthread/main.cpp index 9a0ac1a60a..ef696e5978 100644 --- a/tests/manual/wasm/network/echo_client_mainthread/main.cpp +++ b/tests/manual/wasm/network/echo_client_mainthread/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> #include <QtNetwork> diff --git a/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt b/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt index a1f2bef254..d240e9e70b 100644 --- a/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt +++ b/tests/manual/wasm/network/echo_client_secondarythread/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_manual_test(echo_client_secondarythread GUI SOURCES diff --git a/tests/manual/wasm/network/echo_client_secondarythread/main.cpp b/tests/manual/wasm/network/echo_client_secondarythread/main.cpp index 615706727f..52cea93495 100644 --- a/tests/manual/wasm/network/echo_client_secondarythread/main.cpp +++ b/tests/manual/wasm/network/echo_client_secondarythread/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> #include <QtNetwork> diff --git a/tests/manual/wasm/network/echo_server/CMakeLists.txt b/tests/manual/wasm/network/echo_server/CMakeLists.txt index cf98163fb8..72ec413a0e 100644 --- a/tests/manual/wasm/network/echo_server/CMakeLists.txt +++ b/tests/manual/wasm/network/echo_server/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause project(echo_server) cmake_minimum_required(VERSION 3.19) diff --git a/tests/manual/wasm/network/echo_server/main.cpp b/tests/manual/wasm/network/echo_server/main.cpp index 9ea15b7caa..3a67cabc79 100644 --- a/tests/manual/wasm/network/echo_server/main.cpp +++ b/tests/manual/wasm/network/echo_server/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore> #include <QtNetwork> diff --git a/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt b/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt index f69bd80259..68a3993778 100644 --- a/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt +++ b/tests/manual/wasm/network/sockify_sockets_auto/CMakeLists.txt @@ -1,8 +1,10 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_manual_test(sockify_sockets_auto SOURCES main.cpp ../../qtwasmtestlib/qtwasmtestlib.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Core Qt::Network ) diff --git a/tests/manual/wasm/network/sockify_sockets_auto/main.cpp b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp index 313ac716b6..b6aa232b4a 100644 --- a/tests/manual/wasm/network/sockify_sockets_auto/main.cpp +++ b/tests/manual/wasm/network/sockify_sockets_auto/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtwasmtestlib.h> #include <QtCore> 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 index 5376249a71..080ada94e7 100644 --- a/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html +++ b/tests/manual/wasm/network/sockify_sockets_auto/sockify_sockets_auto.html @@ -3,7 +3,7 @@ <script type="text/javascript" src="sockify_sockets_auto.js"></script> <script> window.onload = async () => { - runTestCase(document.getElementById("log")); + runTestCase(sockify_sockets_auto_entry, document.getElementById("log")); }; </script> <p> Sockify tunneled sockets auto test. diff --git a/tests/manual/wasm/qstdweb/CMakeLists.txt b/tests/manual/wasm/qstdweb/CMakeLists.txt index 234b12bab9..39039c3910 100644 --- a/tests/manual/wasm/qstdweb/CMakeLists.txt +++ b/tests/manual/wasm/qstdweb/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_manual_test(promise_auto SOURCES promise_main.cpp @@ -25,7 +27,7 @@ qt_internal_add_manual_test(files_auto SOURCES files_main.cpp ../qtwasmtestlib/qtwasmtestlib.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Core Qt::CorePrivate Qt::GuiPrivate @@ -49,7 +51,7 @@ qt_internal_add_manual_test(qwasmcompositor_auto SOURCES qwasmcompositor_main.cpp ../qtwasmtestlib/qtwasmtestlib.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Core Qt::CorePrivate Qt::GuiPrivate @@ -70,3 +72,27 @@ add_custom_command( ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js) target_link_options(qwasmcompositor_auto PRIVATE -sASYNCIFY -Os) + +qt_internal_add_manual_test(iodevices_auto + SOURCES + iodevices_main.cpp + ../qtwasmtestlib/qtwasmtestlib.cpp + LIBRARIES + Qt::Core + Qt::CorePrivate + Qt::GuiPrivate +) + +add_custom_command( + TARGET iodevices_auto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/iodevices_auto.html + ${CMAKE_CURRENT_BINARY_DIR}/iodevices_auto.html) + +add_custom_command( + TARGET iodevices_auto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/../qtwasmtestlib/qtwasmtestlib.js + ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js) + +target_link_options(iodevices_auto PRIVATE -sASYNCIFY -Os) diff --git a/tests/manual/wasm/qstdweb/files_auto.html b/tests/manual/wasm/qstdweb/files_auto.html index 71e8088dfb..9027fdc660 100644 --- a/tests/manual/wasm/qstdweb/files_auto.html +++ b/tests/manual/wasm/qstdweb/files_auto.html @@ -6,7 +6,7 @@ <script type="text/javascript" src="files_auto.js"></script> <script> window.onload = () => { - runTestCase(document.getElementById("log")); + runTestCase(files_auto_entry, document.getElementById("log")); }; </script> <p>Running files auto test.</p> diff --git a/tests/manual/wasm/qstdweb/files_main.cpp b/tests/manual/wasm/qstdweb/files_main.cpp index 5c0e0e8292..45939feb10 100644 --- a/tests/manual/wasm/qstdweb/files_main.cpp +++ b/tests/manual/wasm/qstdweb/files_main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QtCore/QEvent> diff --git a/tests/manual/wasm/qstdweb/iodevices_auto.html b/tests/manual/wasm/qstdweb/iodevices_auto.html new file mode 100644 index 0000000000..7937b8a483 --- /dev/null +++ b/tests/manual/wasm/qstdweb/iodevices_auto.html @@ -0,0 +1,10 @@ +<!doctype html> +<script type="text/javascript" src="qtwasmtestlib.js"></script> +<script type="text/javascript" src="iodevices_auto.js"></script> +<script> + window.onload = () => { + runTestCase(iodevices_auto_entry, document.getElementById("log")); + }; +</script> +<p>Running qstdweb iodevices auto test.</p> +<div id="log"></div> diff --git a/tests/manual/wasm/qstdweb/iodevices_main.cpp b/tests/manual/wasm/qstdweb/iodevices_main.cpp new file mode 100644 index 0000000000..0dbdd0084e --- /dev/null +++ b/tests/manual/wasm/qstdweb/iodevices_main.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore/QtCore> +#include <QtCore/private/qstdweb_p.h> + +#include <qtwasmtestlib.h> + +#include "emscripten.h" + +using qstdweb::ArrayBuffer; +using qstdweb::Uint8Array; +using qstdweb::Blob; +using qstdweb::BlobIODevice; +using qstdweb::Uint8ArrayIODevice; + +class WasmIoDevicesTest: public QObject +{ + Q_OBJECT + +private slots: + void blobIODevice(); + void uint8ArrayIODevice(); +}; + +// Creates a test arraybuffer with byte values [0..size] % 256 * 2 +char testByteValue(int i) { return (i % 256) * 2; } +ArrayBuffer createTestArrayBuffer(int size) +{ + ArrayBuffer buffer(size); + Uint8Array array(buffer); + for (int i = 0; i < size; ++i) + array.val().set(i, testByteValue(i)); + return buffer; +} + +void WasmIoDevicesTest::blobIODevice() +{ + if (!qstdweb::canBlockCallingThread()) { + QtWasmTest::completeTestFunction(QtWasmTest::TestResult::Skip, "requires asyncify"); + return; + } + + // Create test buffer and BlobIODevice + const int bufferSize = 16; + BlobIODevice blobDevice(Blob::fromArrayBuffer(createTestArrayBuffer(bufferSize))); + + // Read back byte for byte from the device + QWASMVERIFY(blobDevice.open(QIODevice::ReadOnly)); + for (int i = 0; i < bufferSize; ++i) { + char byte; + blobDevice.seek(i); + blobDevice.read(&byte, 1); + QWASMCOMPARE(byte, testByteValue(i)); + } + + blobDevice.close(); + QWASMVERIFY(!blobDevice.open(QIODevice::WriteOnly)); + QWASMSUCCESS(); +} + +void WasmIoDevicesTest::uint8ArrayIODevice() +{ + // Create test buffer and Uint8ArrayIODevice + const int bufferSize = 1024; + Uint8Array array(createTestArrayBuffer(bufferSize)); + Uint8ArrayIODevice arrayDevice(array); + + // Read back byte for byte from the device + QWASMVERIFY(arrayDevice.open(QIODevice::ReadWrite)); + for (int i = 0; i < bufferSize; ++i) { + char byte; + arrayDevice.seek(i); + arrayDevice.read(&byte, 1); + QWASMCOMPARE(byte, testByteValue(i)); + } + + // Write a different set of bytes + QWASMCOMPARE(arrayDevice.seek(0), true); + for (int i = 0; i < bufferSize; ++i) { + char byte = testByteValue(i + 1); + arrayDevice.seek(i); + QWASMCOMPARE(arrayDevice.write(&byte, 1), 1); + } + + // Verify that the original array was updated + QByteArray copy = QByteArray::fromEcmaUint8Array(array.val()); + for (int i = 0; i < bufferSize; ++i) + QWASMCOMPARE(copy.at(i), testByteValue(i + 1)); + + arrayDevice.close(); + QWASMSUCCESS(); +} + +int main(int argc, char **argv) +{ + auto testObject = std::make_shared<WasmIoDevicesTest>(); + QtWasmTest::initTestCase<QCoreApplication>(argc, argv, testObject); + return 0; +} + +#include "iodevices_main.moc" + diff --git a/tests/manual/wasm/qstdweb/promise_auto.html b/tests/manual/wasm/qstdweb/promise_auto.html index 786145419f..94a8dbb88a 100644 --- a/tests/manual/wasm/qstdweb/promise_auto.html +++ b/tests/manual/wasm/qstdweb/promise_auto.html @@ -3,7 +3,7 @@ <script type="text/javascript" src="promise_auto.js"></script> <script> window.onload = () => { - runTestCase(document.getElementById("log")); + runTestCase(promise_auto_entry, document.getElementById("log")); }; </script> <p>Running promise auto test.</p> diff --git a/tests/manual/wasm/qstdweb/promise_main.cpp b/tests/manual/wasm/qstdweb/promise_main.cpp index 351f06c91d..c5f6f7f412 100644 --- a/tests/manual/wasm/qstdweb/promise_main.cpp +++ b/tests/manual/wasm/qstdweb/promise_main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QtCore/QEvent> diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html index 26daecdf41..f33aab0b9c 100644 --- a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html +++ b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html @@ -3,7 +3,7 @@ <script type="text/javascript" src="qwasmcompositor_auto.js"></script> <script> window.onload = () => { - runTestCase(document.getElementById("log")); + runTestCase(qwasmcompositor_auto_entry, document.getElementById("log")); }; </script> <p>Running files auto test.</p> diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp b/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp index 8b5807a8dd..e1a9cf604d 100644 --- a/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp +++ b/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QEvent> #include <QtCore/QObject> @@ -7,7 +7,7 @@ #include <QtGui/qguiapplication.h> #include <QtGui/qoffscreensurface.h> #include <QtGui/qpa/qwindowsysteminterface.h> -#include <QtGui/private/qrhigles2_p.h> +#include <QtGui/rhi/qrhi.h> #include <qtwasmtestlib.h> @@ -66,7 +66,7 @@ void Window::keyPressEvent(QKeyEvent *) void Window::init() { - QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling; + QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers; m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface()); QRhiGles2InitParams params; @@ -94,10 +94,8 @@ public: QWasmCompositorTest() : m_window(val::global("window")), m_testSupport(val::object()) { m_window.set("testSupport", m_testSupport); - m_testSupport.set("qtAddContainerElement", - emscripten::val::module_property("qtAddContainerElement")); - m_testSupport.set("qtRemoveContainerElement", - emscripten::val::module_property("qtRemoveContainerElement")); + m_testSupport.set("qtSetContainerElements", + emscripten::val::module_property("qtSetContainerElements")); } ~QWasmCompositorTest() noexcept @@ -118,12 +116,12 @@ private: }); m_cleanup.emplace_back([]() mutable { EM_ASM({ - testSupport.qtRemoveContainerElement(testSupport.screenElement); + testSupport.qtSetContainerElements([]); testSupport.screenElement.parentElement.removeChild(testSupport.screenElement); }); }); - EM_ASM({ testSupport.qtAddContainerElement(testSupport.screenElement); }); + EM_ASM({ testSupport.qtSetContainerElements([testSupport.screenElement]); }); } template<class T> diff --git a/tests/manual/wasm/qtloader_integration/CMakeLists.txt b/tests/manual/wasm/qtloader_integration/CMakeLists.txt new file mode 100644 index 0000000000..2603a05135 --- /dev/null +++ b/tests/manual/wasm/qtloader_integration/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_manual_test(tst_qtloader_integration + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::GuiPrivate + Qt::Widgets +) + +set_target_properties(tst_qtloader_integration PROPERTIES QT_WASM_EXTRA_EXPORTED_METHODS "ENV") + +add_custom_command( + TARGET tst_qtloader_integration POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/tst_qtloader_integration.html + ${CMAKE_CURRENT_BINARY_DIR}/tst_qtloader_integration.html) + +add_custom_command( + TARGET tst_qtloader_integration POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/plugins/platforms/wasm/qtloader.js + ${CMAKE_CURRENT_BINARY_DIR}/qtloader.js) + +add_custom_command( + TARGET tst_qtloader_integration POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/../shared/testrunner.js + ${CMAKE_CURRENT_BINARY_DIR}/testrunner.js) + +add_custom_command( + TARGET tst_qtloader_integration POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/test_body.js + ${CMAKE_CURRENT_BINARY_DIR}/test_body.js) + +add_custom_command( + TARGET tst_qtloader_integration POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/preload.json + ${CMAKE_CURRENT_BINARY_DIR}/preload.json) diff --git a/tests/manual/wasm/qtloader_integration/main.cpp b/tests/manual/wasm/qtloader_integration/main.cpp new file mode 100644 index 0000000000..4bb502b69c --- /dev/null +++ b/tests/manual/wasm/qtloader_integration/main.cpp @@ -0,0 +1,183 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <QtWidgets/QtWidgets> + +#include <iostream> +#include <sstream> + +#include <emscripten/bind.h> +#include <emscripten/val.h> +#include <emscripten.h> + +#include <QtGui/qpa/qplatformscreen.h> + +namespace { +constexpr int ExitValueImmediateReturn = 42; +constexpr int ExitValueFromExitApp = 22; + +std::string screenInformation() +{ + auto screens = qGuiApp->screens(); + std::ostringstream out; + out << "["; + const char *separator = ""; + for (const auto &screen : screens) { + out << separator; + out << "[" << std::to_string(screen->geometry().x()) << "," + << std::to_string(screen->geometry().y()) << "," + << std::to_string(screen->geometry().width()) << "," + << std::to_string(screen->geometry().height()) << "]"; + separator = ","; + } + out << "]"; + return out.str(); +} + +std::string logicalDpi() +{ + auto screens = qGuiApp->screens(); + std::ostringstream out; + out << "["; + const char *separator = ""; + for (const auto &screen : screens) { + out << separator; + out << "[" << std::to_string(screen->handle()->logicalDpi().first) << ", " + << std::to_string(screen->handle()->logicalDpi().second) << "]"; + separator = ","; + } + out << "]"; + return out.str(); +} + +std::string preloadedFiles() +{ + QStringList files = QDir("/preload").entryList(QDir::Files); + std::ostringstream out; + out << "["; + const char *separator = ""; + for (const auto &file : files) { + out << separator; + out << file.toStdString(); + separator = ","; + } + out << "]"; + return out.str(); +} + +void crash() +{ + std::abort(); +} + +void stackOverflow() +{ + stackOverflow(); // should eventually termniate with exception +} + +void exitApp() +{ + emscripten_force_exit(ExitValueFromExitApp); +} + +void produceOutput() +{ + fprintf(stdout, "Sample output!\n"); +} + +std::string retrieveArguments() +{ + auto arguments = QApplication::arguments(); + std::ostringstream out; + out << "["; + const char *separator = ""; + for (const auto &argument : arguments) { + out << separator; + out << "'" << argument.toStdString() << "'"; + separator = ","; + } + out << "]"; + return out.str(); +} + +std::string getEnvironmentVariable(std::string name) { + return QString::fromLatin1(qgetenv(name.c_str())).toStdString(); +} +} // namespace + +class AppWindow : public QObject +{ + Q_OBJECT +public: + AppWindow() : m_layout(new QVBoxLayout(&m_ui)) + { + addWidget<QLabel>("Qt Loader integration tests"); + + m_ui.setLayout(m_layout); + } + + void show() { m_ui.show(); } + + ~AppWindow() = default; + +private: + template<class T, class... Args> + T *addWidget(Args... args) + { + T *widget = new T(std::forward<Args>(args)..., &m_ui); + m_layout->addWidget(widget); + return widget; + } + + QWidget m_ui; + QVBoxLayout *m_layout; +}; + +int main(int argc, char **argv) +{ + QApplication application(argc, argv); + const auto arguments = application.arguments(); + const bool exitImmediately = + std::find(arguments.begin(), arguments.end(), QStringLiteral("--exit-immediately")) + != arguments.end(); + if (exitImmediately) + emscripten_force_exit(ExitValueImmediateReturn); + + const bool crashImmediately = + std::find(arguments.begin(), arguments.end(), QStringLiteral("--crash-immediately")) + != arguments.end(); + if (crashImmediately) + crash(); + + const bool stackOverflowImmediately = + std::find(arguments.begin(), arguments.end(), QStringLiteral("--stack-owerflow-immediately")) + != arguments.end(); + if (stackOverflowImmediately) + stackOverflow(); + + const bool noGui = std::find(arguments.begin(), arguments.end(), QStringLiteral("--no-gui")) + != arguments.end(); + + if (!noGui) { + AppWindow window; + window.show(); + return application.exec(); + } + return application.exec(); +} + +EMSCRIPTEN_BINDINGS(qtLoaderIntegrationTest) +{ + emscripten::constant("EXIT_VALUE_IMMEDIATE_RETURN", ExitValueImmediateReturn); + emscripten::constant("EXIT_VALUE_FROM_EXIT_APP", ExitValueFromExitApp); + + emscripten::function("screenInformation", &screenInformation); + emscripten::function("logicalDpi", &logicalDpi); + emscripten::function("preloadedFiles", &preloadedFiles); + emscripten::function("crash", &crash); + emscripten::function("exitApp", &exitApp); + emscripten::function("produceOutput", &produceOutput); + emscripten::function("retrieveArguments", &retrieveArguments); + emscripten::function("getEnvironmentVariable", &getEnvironmentVariable); +} + +#include "main.moc" diff --git a/tests/manual/wasm/qtloader_integration/preload.json b/tests/manual/wasm/qtloader_integration/preload.json new file mode 100644 index 0000000000..d7e09911ff --- /dev/null +++ b/tests/manual/wasm/qtloader_integration/preload.json @@ -0,0 +1,10 @@ +[ + { + "source": "qtloader.js", + "destination": "/preload/qtloader.js" + }, + { + "source": "$QTDIR/qtlogo.svg", + "destination": "/preload/qtlogo.svg" + } +] diff --git a/tests/manual/wasm/qtloader_integration/test_body.js b/tests/manual/wasm/qtloader_integration/test_body.js new file mode 100644 index 0000000000..4fb49c31aa --- /dev/null +++ b/tests/manual/wasm/qtloader_integration/test_body.js @@ -0,0 +1,517 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import { Mock, assert, TestRunner } from './testrunner.js'; + +export class QtLoaderIntegrationTests +{ + #testScreenContainers = [] + + async beforeEach() + { + this.#addScreenContainer('screen-container-0', { width: '200px', height: '300px' }); + } + + async afterEach() + { + this.#testScreenContainers.forEach(screenContainer => + { + document.body.removeChild(screenContainer); + }); + this.#testScreenContainers = []; + } + + async missingConfig() + { + let caughtException; + try { + await qtLoad(); + } catch (e) { + caughtException = e; + } + + assert.isNotUndefined(caughtException); + assert.equal('config is required, expected an object', caughtException.message); + } + + async missingQtSection() + { + let caughtException; + try { + await qtLoad({}); + } catch (e) { + caughtException = e; + } + + assert.isNotUndefined(caughtException); + assert.equal( + 'config.qt is required, expected an object', caughtException.message); + } + + async missingEntryFunction() + { + let caughtException; + try { + await qtLoad({ qt: {}}); + } catch (e) { + caughtException = e; + } + + assert.isNotUndefined(caughtException); + assert.equal( + 'config.qt.entryFunction is required, expected a function', caughtException.message); + } + + async badEntryFunction() + { + let caughtException; + try { + await qtLoad({ qt: { entryFunction: 'invalid' }}); + } catch (e) { + caughtException = e; + } + + assert.isNotUndefined(caughtException); + assert.equal( + 'config.qt.entryFunction is required, expected a function', caughtException.message); + } + + async environmentVariables() + { + const instance = await qtLoad({ + qt: { + environment: { + variable1: 'value1', + variable2: 'value2' + }, + entryFunction: tst_qtloader_integration_entry, + containerElements: [this.#testScreenContainers[0]] + } + }); + assert.isTrue(instance.getEnvironmentVariable('variable1') === 'value1'); + assert.isTrue(instance.getEnvironmentVariable('variable2') === 'value2'); + } + + async screenContainerManipulations() + { + // ... (do other things), then call addContainerElement() to add a new container/screen. + // This can happen either before or after load() is called - loader will route the + // call to instance when it's ready. + this.#addScreenContainer('appcontainer1', { width: '100px', height: '100px' }) + + const instance = await qtLoad({ + qt: { + entryFunction: tst_qtloader_integration_entry, + containerElements: this.#testScreenContainers + } + }); + { + const screenInformation = this.#getScreenInformation(instance); + + assert.equal(2, screenInformation.length); + assert.equal(200, screenInformation[0].width); + assert.equal(300, screenInformation[0].height); + assert.equal(100, screenInformation[1].width); + assert.equal(100, screenInformation[1].height); + } + + this.#addScreenContainer('appcontainer2', { width: '234px', height: '99px' }) + instance.qtSetContainerElements(this.#testScreenContainers); + + { + const screenInformation = this.#getScreenInformation(instance); + + assert.equal(3, screenInformation.length); + assert.equal(200, screenInformation[0].width); + assert.equal(300, screenInformation[0].height); + assert.equal(100, screenInformation[1].width); + assert.equal(100, screenInformation[1].height); + assert.equal(234, screenInformation[2].width); + assert.equal(99, screenInformation[2].height); + } + + document.body.removeChild(this.#testScreenContainers.splice(2, 1)[0]); + instance.qtSetContainerElements(this.#testScreenContainers); + { + const screenInformation = this.#getScreenInformation(instance); + + assert.equal(2, screenInformation.length); + assert.equal(200, screenInformation[0].width); + assert.equal(300, screenInformation[0].height); + assert.equal(100, screenInformation[1].width); + assert.equal(100, screenInformation[1].height); + } + } + + async primaryScreenIsAlwaysFirst() + { + const instance = await qtLoad({ + qt: { + entryFunction: tst_qtloader_integration_entry, + containerElements: this.#testScreenContainers, + } + }); + this.#addScreenContainer( + 'appcontainer3', { width: '12px', height: '24px' }, + container => this.#testScreenContainers.splice(0, 0, container)); + this.#addScreenContainer( + 'appcontainer4', { width: '34px', height: '68px' }, + container => this.#testScreenContainers.splice(1, 0, container)); + + instance.qtSetContainerElements(this.#testScreenContainers); + { + const screenInformation = this.#getScreenInformation(instance); + + assert.equal(3, screenInformation.length); + // The primary screen (at position 0) is always at 0 + assert.equal(12, screenInformation[0].width); + assert.equal(24, screenInformation[0].height); + // Other screens are pushed at the back + assert.equal(200, screenInformation[1].width); + assert.equal(300, screenInformation[1].height); + assert.equal(34, screenInformation[2].width); + assert.equal(68, screenInformation[2].height); + } + + this.#testScreenContainers.forEach(screenContainer => + { + document.body.removeChild(screenContainer); + }); + this.#testScreenContainers = [ + this.#addScreenContainer('appcontainer5', { width: '11px', height: '12px' }), + this.#addScreenContainer('appcontainer6', { width: '13px', height: '14px' }), + ]; + + instance.qtSetContainerElements(this.#testScreenContainers); + { + const screenInformation = this.#getScreenInformation(instance); + + assert.equal(2, screenInformation.length); + assert.equal(11, screenInformation[0].width); + assert.equal(12, screenInformation[0].height); + assert.equal(13, screenInformation[1].width); + assert.equal(14, screenInformation[1].height); + } + } + + async multipleInstances() + { + // Fetch/Compile the module once; reuse for each instance. This is also if the page wants to + // initiate the .wasm file download fetch as early as possible, before the browser has + // finished fetching and parsing testapp.js and qtloader.js + const module = WebAssembly.compileStreaming(fetch('tst_qtloader_integration.wasm')); + + const instances = await Promise.all([1, 2, 3].map(i => qtLoad({ + qt: { + entryFunction: tst_qtloader_integration_entry, + containerElements: [this.#addScreenContainer(`screen-container-${i}`, { + width: `${i * 10}px`, + height: `${i * 10}px`, + })], + module, + } + }))); + // Confirm the identity of instances by querying their screen widths and heights + { + const screenInformation = this.#getScreenInformation(instances[0]); + console.log(); + assert.equal(1, screenInformation.length); + assert.equal(10, screenInformation[0].width); + assert.equal(10, screenInformation[0].height); + } + { + const screenInformation = this.#getScreenInformation(instances[1]); + assert.equal(1, screenInformation.length); + assert.equal(20, screenInformation[0].width); + assert.equal(20, screenInformation[0].height); + } + { + const screenInformation = this.#getScreenInformation(instances[2]); + assert.equal(1, screenInformation.length); + assert.equal(30, screenInformation[0].width); + assert.equal(30, screenInformation[0].height); + } + } + + async consoleMode() + { + // 'Console mode' for autotesting type scenarios + let accumulatedStdout = ''; + const instance = await qtLoad({ + arguments: ['--no-gui'], + print: output => + { + accumulatedStdout += output; + }, + qt: { + entryFunction: tst_qtloader_integration_entry, + } + }); + + this.#callTestInstanceApi(instance, 'produceOutput'); + assert.equal('Sample output!', accumulatedStdout); + } + + async modulePromiseProvided() + { + await qtLoad({ + qt: { + entryFunction: createQtAppInstance, + containerElements: [this.#testScreenContainers[0]], + module: WebAssembly.compileStreaming( + fetch('tst_qtloader_integration.wasm')) + } + }); + } + + async moduleProvided() + { + await qtLoad({ + qt: { + entryFunction: tst_qtloader_integration_entry, + containerElements: [this.#testScreenContainers[0]], + module: await WebAssembly.compileStreaming( + fetch('tst_qtloader_integration.wasm')) + } + }); + } + + async arguments() + { + const instance = await qtLoad({ + arguments: ['--no-gui', 'arg1', 'other', 'yetanotherarg'], + qt: { + entryFunction: tst_qtloader_integration_entry, + } + }); + const args = this.#callTestInstanceApi(instance, 'retrieveArguments'); + assert.equal(5, args.length); + assert.isTrue('arg1' === args[2]); + assert.equal('other', args[3]); + assert.equal('yetanotherarg', args[4]); + } + + async moduleProvided_exceptionThrownInFactory() + { + let caughtException; + try { + await qtLoad({ + qt: { + entryFunction: tst_qtloader_integration_entry, + containerElements: [this.#testScreenContainers[0]], + module: Promise.reject(new Error('Failed to load')), + } + }); + } catch (e) { + caughtException = e; + } + assert.isTrue(caughtException !== undefined); + assert.equal('Failed to load', caughtException.message); + } + + async abort() + { + const onExitMock = new Mock(); + const instance = await qtLoad({ + arguments: ['--no-gui'], + qt: { + onExit: onExitMock, + entryFunction: tst_qtloader_integration_entry, + } + }); + try { + instance.crash(); + } catch { } + assert.equal(1, onExitMock.calls.length); + const exitStatus = onExitMock.calls[0][0]; + assert.isTrue(exitStatus.crashed); + assert.isUndefined(exitStatus.code); + assert.isNotUndefined(exitStatus.text); + } + + async abortImmediately() + { + const onExitMock = new Mock(); + let caughtException; + try { + await qtLoad({ + arguments: ['--no-gui', '--crash-immediately'], + qt: { + onExit: onExitMock, + entryFunction: tst_qtloader_integration_entry, + } + }); + } catch (e) { + caughtException = e; + } + + assert.isTrue(caughtException !== undefined); + assert.equal(1, onExitMock.calls.length); + const exitStatus = onExitMock.calls[0][0]; + assert.isTrue(exitStatus.crashed); + assert.isUndefined(exitStatus.code); + assert.isNotUndefined(exitStatus.text); + } + + async stackOwerflowImmediately() + { + const onExitMock = new Mock(); + let caughtException; + try { + await qtLoad({ + arguments: ['--no-gui', '--stack-owerflow-immediately'], + qt: { + onExit: onExitMock, + entryFunction: tst_qtloader_integration_entry, + } + }); + } catch (e) { + caughtException = e; + } + + assert.isTrue(caughtException !== undefined); + assert.equal(1, onExitMock.calls.length); + const exitStatus = onExitMock.calls[0][0]; + assert.isTrue(exitStatus.crashed); + assert.isUndefined(exitStatus.code); + // text should be "RangeError: Maximum call stack + // size exceeded", or similar. + assert.isNotUndefined(exitStatus.text); + } + + async userAbortCallbackCalled() + { + const onAbortMock = new Mock(); + let instance = await qtLoad({ + arguments: ['--no-gui'], + onAbort: onAbortMock, + qt: { + entryFunction: tst_qtloader_integration_entry, + } + }); + try { + instance.crash(); + } catch (e) { + // emscripten throws an 'Aborted' error here, which we ignore for the sake of the test + } + assert.equal(1, onAbortMock.calls.length); + } + + async exit() + { + const onExitMock = new Mock(); + let instance = await qtLoad({ + arguments: ['--no-gui'], + qt: { + onExit: onExitMock, + entryFunction: tst_qtloader_integration_entry, + } + }); + // The module is running. onExit should not have been called. + assert.equal(0, onExitMock.calls.length); + try { + instance.exitApp(); + } catch (e) { + // emscripten throws a 'Runtime error: unreachable' error here. We ignore it for the + // sake of the test. + } + assert.equal(1, onExitMock.calls.length); + const exitStatus = onExitMock.calls[0][0]; + assert.isFalse(exitStatus.crashed); + assert.equal(instance.EXIT_VALUE_FROM_EXIT_APP, exitStatus.code); + assert.isUndefined(exitStatus.text); + } + + async exitImmediately() + { + const onExitMock = new Mock(); + const instance = await qtLoad({ + arguments: ['--no-gui', '--exit-immediately'], + qt: { + onExit: onExitMock, + entryFunction: tst_qtloader_integration_entry, + } + }); + assert.equal(1, onExitMock.calls.length); + + const exitStatusFromOnExit = onExitMock.calls[0][0]; + + assert.isFalse(exitStatusFromOnExit.crashed); + assert.equal(instance.EXIT_VALUE_IMMEDIATE_RETURN, exitStatusFromOnExit.code); + assert.isUndefined(exitStatusFromOnExit.text); + } + + async userQuitCallbackCalled() + { + const quitMock = new Mock(); + let instance = await qtLoad({ + arguments: ['--no-gui'], + quit: quitMock, + qt: { + entryFunction: tst_qtloader_integration_entry, + } + }); + try { + instance.exitApp(); + } catch (e) { + // emscripten throws a 'Runtime error: unreachable' error here. We ignore it for the + // sake of the test. + } + assert.equal(1, quitMock.calls.length); + const [exitCode, exception] = quitMock.calls[0]; + assert.equal(instance.EXIT_VALUE_FROM_EXIT_APP, exitCode); + assert.equal('ExitStatus', exception.name); + } + + async preloadFiles() + { + const instance = await qtLoad({ + arguments: ["--no-gui"], + qt: { + preload: ['preload.json'], + qtdir: '.', + } + }); + const preloadedFiles = instance.preloadedFiles(); + // Verify that preloaded file list matches files specified in preload.json + assert.equal("[qtloader.js,qtlogo.svg]", preloadedFiles); + } + + #callTestInstanceApi(instance, apiName) + { + return eval(instance[apiName]()); + } + + #getScreenInformation(instance) + { + return this.#callTestInstanceApi(instance, 'screenInformation').map(elem => ({ + x: elem[0], + y: elem[1], + width: elem[2], + height: elem[3], + })); + } + + #addScreenContainer(id, style, inserter) + { + const container = (() => + { + const container = document.createElement('div'); + container.id = id; + container.style.width = style.width; + container.style.height = style.height; + document.body.appendChild(container); + return container; + })(); + inserter ? inserter(container) : this.#testScreenContainers.push(container); + return container; + } +} + +(async () => +{ + const runner = new TestRunner(new QtLoaderIntegrationTests(), { + timeoutSeconds: 10 + }); + await runner.runAll(); +})(); diff --git a/tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html b/tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html new file mode 100644 index 0000000000..7aa7528a1d --- /dev/null +++ b/tests/manual/wasm/qtloader_integration/tst_qtloader_integration.html @@ -0,0 +1,13 @@ +<!doctype html> +<html lang="en-us"> + +<head> + <title>tst_qtloader_integration</title> + <script src='tst_qtloader_integration.js'></script> + <script src="qtloader.js" defer></script> + <script type="module" src="test_body.js" defer></script> +</head> + +<body></body> + +</html> diff --git a/tests/manual/wasm/qtwasmtestlib/README.md b/tests/manual/wasm/qtwasmtestlib/README.md index 515c33ae6a..6de81fe8b4 100644 --- a/tests/manual/wasm/qtwasmtestlib/README.md +++ b/tests/manual/wasm/qtwasmtestlib/README.md @@ -48,7 +48,7 @@ Finally provide an html file which hosts the test runner and calls runTestCase() <script type="text/javascript" src="test_case.js"></script> <script> window.onload = async () => { - runTestCase(document.getElementById("log")); + runTestCase(entryFunction, document.getElementById("log")); }; </script> <p>Running Foo auto test.</p> @@ -67,7 +67,7 @@ html file provides container elements which becomes QScreens for the test code. window.onload = async () => { let log = document.getElementById("log") let containers = [document.getElementById("container")]; - runTestCase(log, containers); + runTestCase(entryFunction, log, containers); }; </script> <p>Running Foo auto test.</p> diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp index d86b8ee2c0..ec03c7209a 100644 --- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp +++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qtwasmtestlib.h" diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h index 5dcc41419e..2307ed1ccd 100644 --- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h +++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.h @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QT_WASM_TESTRUNNER_H #define QT_WASM_TESTRUNNER_H diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js index eaf518d1ef..d4f815b887 100644 --- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js +++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.js @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // A minimal async test runner for Qt async auto tests. // @@ -77,12 +77,13 @@ async function runTestFunction(instance, name) { } } -async function runTestCaseImpl(testFunctionStarted, testFunctionCompleted, qtContainers) { +async function runTestCaseImpl(entryFunction, testFunctionStarted, testFunctionCompleted, qtContainers) +{ // Create test case instance const config = { qtContainerElements: qtContainers || [] } - const instance = await createQtAppInstance(config); + const instance = await entryFunction(config); // Run all test functions const functionsString = instance.getTestFunctions(); @@ -124,10 +125,11 @@ function testFunctionCompleted(status) { g_htmlLogElement.innerHTML += line; } -async function runTestCase(htmlLogElement, qtContainers) { +async function runTestCase(entryFunction, htmlLogElement, qtContainers) +{ g_htmlLogElement = htmlLogElement; try { - await runTestCaseImpl(testFunctionStarted, testFunctionCompleted, qtContainers); + await runTestCaseImpl(entryFunction, testFunctionStarted, testFunctionCompleted, qtContainers); g_htmlLogElement.innerHTML += "<br> DONE" } catch (err) { g_htmlLogElement.innerHTML += err diff --git a/tests/manual/wasm/qwasmwindow/CMakeLists.txt b/tests/manual/wasm/qwasmwindow/CMakeLists.txt deleted file mode 100644 index 73e267331e..0000000000 --- a/tests/manual/wasm/qwasmwindow/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -qt_internal_add_manual_test(qwasmwindow_harness - SOURCES - qwasmwindow_harness.cpp - PUBLIC_LIBRARIES - Qt::Core - Qt::CorePrivate - Qt::GuiPrivate -) - -add_custom_command( - TARGET qwasmwindow_harness POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/qwasmwindow_harness.html - ${CMAKE_CURRENT_BINARY_DIR}/qwasmwindow_harness.html -) - -add_custom_command( - TARGET qwasmwindow_harness POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/qwasmwindow.py - ${CMAKE_CURRENT_BINARY_DIR}/qwasmwindow.py -) - -add_custom_command( - TARGET qwasmwindow_harness POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/run.sh - ${CMAKE_CURRENT_BINARY_DIR}/run.sh -) - -add_custom_command( - TARGET qwasmwindow_harness POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/../../../../util/wasm/qtwasmserver/qtwasmserver.py - ${CMAKE_CURRENT_BINARY_DIR}/qtwasmserver.py -) diff --git a/tests/manual/wasm/qwasmwindow/qwasmwindow.py b/tests/manual/wasm/qwasmwindow/qwasmwindow.py deleted file mode 100644 index a80e394cc2..0000000000 --- a/tests/manual/wasm/qwasmwindow/qwasmwindow.py +++ /dev/null @@ -1,445 +0,0 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -from selenium.webdriver import Chrome -from selenium.webdriver.common.actions.action_builder import ActionBuilder -from selenium.webdriver.common.actions.pointer_actions import PointerActions -from selenium.webdriver.common.actions.interaction import POINTER_TOUCH -from selenium.webdriver.common.actions.pointer_input import PointerInput -from selenium.webdriver.common.by import By -from selenium.webdriver.support.expected_conditions import presence_of_element_located -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.common.action_chains import ActionChains - -import unittest -from enum import Enum, auto - -class WidgetTestCase(unittest.TestCase): - def setUp(self): - self._driver = Chrome() - self._driver.get( - 'http://localhost:8001/qwasmwindow_harness.html') - self._test_sandbox_element = WebDriverWait(self._driver, 30).until( - presence_of_element_located((By.ID, 'test-sandbox')) - ) - self.addTypeEqualityFunc(Rect, assert_rects_equal) - - def test_window_resizing(self): - defaultWindowMinSize = 100 - screen = Screen(self._driver, ScreenPosition.FIXED, - x=0, y=0, width=600, height=600) - window = Window(screen, x=100, y=100, width=200, height=200) - self.assertEqual(window.rect, Rect(x=100, y=100, width=200, height=200)) - - window.drag(Handle.TOP_LEFT, direction=UP(10) + LEFT(10)) - self.assertEqual(window.rect, Rect(x=90, y=90, width=210, height=210)) - - window.drag(Handle.TOP, direction=DOWN(10) + LEFT(100)) - self.assertEqual(window.rect, Rect(x=90, y=100, width=210, height=200)) - - window.drag(Handle.TOP_RIGHT, direction=UP(5) + LEFT(5)) - self.assertEqual(window.rect, Rect(x=90, y=95, width=205, height=205)) - - window.drag(Handle.RIGHT, direction=DOWN(100) + RIGHT(5)) - self.assertEqual(window.rect, Rect(x=90, y=95, width=210, height=205)) - - window.drag(Handle.BOTTOM_RIGHT, direction=UP(5) + LEFT(10)) - self.assertEqual(window.rect, Rect(x=90, y=95, width=200, height=200)) - - window.drag(Handle.BOTTOM, direction=DOWN(20) + LEFT(100)) - self.assertEqual(window.rect, Rect(x=90, y=95, width=200, height=220)) - - window.drag(Handle.BOTTOM_LEFT, direction=DOWN(10) + LEFT(10)) - self.assertEqual(window.rect, Rect(x=80, y=95, width=210, height=230)) - - window.drag(Handle.LEFT, direction=DOWN(343) + LEFT(5)) - self.assertEqual(window.rect, Rect(x=75, y=95, width=215, height=230)) - - window.drag(Handle.BOTTOM_RIGHT, direction=UP(150) + LEFT(150)) - self.assertEqual(window.rect, Rect(x=75, y=95, width=defaultWindowMinSize, height=defaultWindowMinSize)) - - def test_cannot_resize_over_screen_top_edge(self): - screen = Screen(self._driver, ScreenPosition.FIXED, - x=200, y=200, width=300, height=300) - window = Window(screen, x=300, y=300, width=100, height=100) - self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100)) - frame_rect_before_resize = window.frame_rect - - window.drag(Handle.TOP, direction=UP(200)) - self.assertEqual(window.rect.x, 300) - self.assertEqual(window.frame_rect.y, screen.rect.y) - self.assertEqual(window.rect.width, 100) - self.assertEqual(window.frame_rect.y + window.frame_rect.height, - frame_rect_before_resize.y + frame_rect_before_resize.height) - - def test_window_move(self): - screen = Screen(self._driver, ScreenPosition.FIXED, - x=200, y=200, width=300, height=300) - window = Window(screen, x=300, y=300, width=100, height=100) - self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100)) - - window.drag(Handle.TOP_WINDOW_BAR, direction=UP(30)) - self.assertEqual(window.rect, Rect(x=300, y=270, width=100, height=100)) - - window.drag(Handle.TOP_WINDOW_BAR, direction=RIGHT(50)) - self.assertEqual(window.rect, Rect(x=350, y=270, width=100, height=100)) - - window.drag(Handle.TOP_WINDOW_BAR, direction=DOWN(30) + LEFT(70)) - self.assertEqual(window.rect, Rect(x=280, y=300, width=100, height=100)) - - def test_screen_limits_window_moves(self): - screen = Screen(self._driver, ScreenPosition.RELATIVE, - x=200, y=200, width=300, height=300) - window = Window(screen, x=300, y=300, width=100, height=100) - self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100)) - - window.drag(Handle.TOP_WINDOW_BAR, direction=LEFT(300)) - self.assertEqual(window.frame_rect.x, screen.rect.x - window.frame_rect.width / 2) - - def test_screen_in_scroll_container_limits_window_moves(self): - screen = Screen(self._driver, ScreenPosition.IN_SCROLL_CONTAINER, - x=200, y=2000, width=300, height=300, - container_width=500, container_height=7000) - screen.scroll_to() - window = Window(screen, x=300, y=2100, width=100, height=100) - self.assertEqual(window.rect, Rect(x=300, y=2100, width=100, height=100)) - - window.drag(Handle.TOP_WINDOW_BAR, direction=LEFT(300)) - self.assertEqual(window.frame_rect.x, screen.rect.x - window.frame_rect.width / 2) - - def test_maximize(self): - screen = Screen(self._driver, ScreenPosition.RELATIVE, - x=200, y=200, width=300, height=300) - window = Window(screen, x=300, y=300, width=100, height=100, title='Maximize') - self.assertEqual(window.rect, Rect(x=300, y=300, width=100, height=100)) - - window.maximize() - self.assertEqual(window.frame_rect, Rect(x=200, y=200, width=300, height=300)) - - def test_multitouch_window_move(self): - screen = Screen(self._driver, ScreenPosition.FIXED, - x=0, y=0, width=800, height=800) - windows = [Window(screen, x=50, y=50, width=100, height=100, title='First'), - Window(screen, x=400, y=400, width=100, height=100, title='Second'), - Window(screen, x=50, y=400, width=100, height=100, title='Third')] - self.assertEqual(windows[0].rect, Rect(x=50, y=50, width=100, height=100)) - self.assertEqual(windows[1].rect, Rect(x=400, y=400, width=100, height=100)) - self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=100, height=100)) - - actions = [TouchDragAction(origin=windows[0].at(Handle.TOP_WINDOW_BAR), direction=DOWN(20) + RIGHT(20)), - TouchDragAction(origin=windows[1].at(Handle.TOP_WINDOW_BAR), direction=DOWN(20) + LEFT(20)), - TouchDragAction(origin=windows[2].at(Handle.TOP_WINDOW_BAR), direction=UP(20) + RIGHT(20))] - perform_touch_drag_actions(actions) - self.assertEqual(windows[0].rect, Rect(x=70, y=70, width=100, height=100)) - self.assertEqual(windows[1].rect, Rect(x=380, y=420, width=100, height=100)) - self.assertEqual(windows[2].rect, Rect(x=70, y=380, width=100, height=100)) - - def test_multitouch_window_resize(self): - screen = Screen(self._driver, ScreenPosition.FIXED, - x=0, y=0, width=800, height=800) - windows = [Window(screen, x=50, y=50, width=150, height=150, title='First'), - Window(screen, x=400, y=400, width=150, height=150, title='Second'), - Window(screen, x=50, y=400, width=150, height=150, title='Third')] - self.assertEqual(windows[0].rect, Rect(x=50, y=50, width=150, height=150)) - self.assertEqual(windows[1].rect, Rect(x=400, y=400, width=150, height=150)) - self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=150, height=150)) - - actions = [TouchDragAction(origin=windows[0].at(Handle.TOP_LEFT), direction=DOWN(20) + RIGHT(20)), - TouchDragAction(origin=windows[1].at(Handle.TOP), direction=DOWN(20) + LEFT(20)), - TouchDragAction(origin=windows[2].at(Handle.BOTTOM_RIGHT), direction=UP(20) + RIGHT(20))] - perform_touch_drag_actions(actions) - self.assertEqual(windows[0].rect, Rect(x=70, y=70, width=130, height=130)) - self.assertEqual(windows[1].rect, Rect(x=400, y=420, width=150, height=130)) - self.assertEqual(windows[2].rect, Rect(x=50, y=400, width=170, height=130)) - - def tearDown(self): - self._driver.quit() - - -class ScreenPosition(Enum): - FIXED = auto() - RELATIVE = auto() - IN_SCROLL_CONTAINER = auto() - - -class Screen: - def __init__(self, driver, positioning, x, y, width, height, container_width=0, container_height=0): - self.driver = driver - self.x = x - self.y = y - self.width = width - self.height = height - if positioning == ScreenPosition.FIXED: - command = f'initializeScreenWithFixedPosition({self.x}, {self.y}, {self.width}, {self.height})' - elif positioning == ScreenPosition.RELATIVE: - command = f'initializeScreenWithRelativePosition({self.x}, {self.y}, {self.width}, {self.height})' - elif positioning == ScreenPosition.IN_SCROLL_CONTAINER: - command = f'initializeScreenInScrollContainer({container_width}, {container_height}, {self.x}, {self.y}, {self.width}, {self.height})' - self.element = self.driver.execute_script( - f''' - return testSupport.{command}; - ''' - ) - if positioning == ScreenPosition.IN_SCROLL_CONTAINER: - self.element = self.element[1] - - screen_information = call_instance_function( - self.driver, 'screenInformation') - if len(screen_information) != 1: - raise AssertionError('Expecting exactly one screen_information!') - self.screen_info = screen_information[0] - - @property - def rect(self): - self.screen_info = call_instance_function( - self.driver, 'screenInformation')[0] - geo = self.screen_info['geometry'] - return Rect(geo['x'], geo['y'], geo['width'], geo['height']) - - def scroll_to(self): - ActionChains(self.driver).scroll_to_element(self.element).perform() - - -class Window: - def __init__(self, screen, x, y, width, height, title='title'): - self.driver = screen.driver - self.title = title - self.driver.execute_script( - f''' - instance.createWindow({x}, {y}, {width}, {height}, '{screen.screen_info["name"]}', '{title}'); - ''' - ) - self._window_id = self.__window_information()['id'] - self.element = screen.element.shadow_root.find_element( - By.CSS_SELECTOR, f'#qt-window-{self._window_id}') - - def __window_information(self): - information = call_instance_function(self.driver, 'windowInformation') - return next(filter(lambda e: e['title'] == self.title, information)) - - @property - def rect(self): - geo = self.__window_information()["geometry"] - return Rect(geo['x'], geo['y'], geo['width'], geo['height']) - - @property - def frame_rect(self): - geo = self.__window_information()["frameGeometry"] - return Rect(geo['x'], geo['y'], geo['width'], geo['height']) - - def drag(self, handle, direction): - ActionChains(self.driver) \ - .move_to_element_with_offset(self.element, *self.at(handle)['offset']) \ - .click_and_hold() \ - .move_by_offset(*translate_direction_to_offset(direction)) \ - .release().perform() - - def maximize(self): - maximize_button = self.element.find_element( - By.CSS_SELECTOR, f'.title-bar :nth-child(6)') - maximize_button.click() - - def at(self, handle): - """ Returns (window, offset) for given handle on window""" - width = self.frame_rect.width - height = self.frame_rect.height - - if handle == Handle.TOP_LEFT: - offset = (-width/2, -height/2) - elif handle == Handle.TOP: - offset = (0, -height/2) - elif handle == Handle.TOP_RIGHT: - offset = (width/2, -height/2) - elif handle == Handle.LEFT: - offset = (-width/2, 0) - elif handle == Handle.RIGHT: - offset = (width/2, 0) - elif handle == Handle.BOTTOM_LEFT: - offset = (-width/2, height/2) - elif handle == Handle.BOTTOM: - offset = (0, height/2) - elif handle == Handle.BOTTOM_RIGHT: - offset = (width/2, height/2) - elif handle == Handle.TOP_WINDOW_BAR: - frame_top = self.frame_rect.y - client_area_top = self.rect.y - top_frame_bar_width = client_area_top - frame_top - offset = (0, -height/2 + top_frame_bar_width/2) - return {'window': self, 'offset': offset} - - -class TouchDragAction: - def __init__(self, origin, direction): - self.origin = origin - self.direction = direction - self.step = 2 - - -def perform_touch_drag_actions(actions): - driver = actions[0].origin['window'].driver - touch_action_builder = ActionBuilder(driver) - pointers = [PointerActions(source=touch_action_builder.add_pointer_input( - POINTER_TOUCH, f'touch_input_{i}')) for i in range(len(actions))] - - for action, pointer in zip(actions, pointers): - pointer.move_to( - action.origin['window'].element, *action.origin['offset']) - pointer.pointer_down(width=10, height=10, pressure=1) - moves = [translate_direction_to_offset(a.direction) for a in actions] - - def movement_finished(): - for move in moves: - if move != (0, 0): - return False - return True - - def sign(num): - if num > 0: - return 1 - elif num < 0: - return -1 - return 0 - - while not movement_finished(): - for i in range(len(actions)): - pointer = pointers[i] - move = moves[i] - step = actions[i].step - - current_move = ( - min(abs(move[0]), step) * sign(move[0]), min(abs(move[1]), step) * sign(move[1])) - moves[i] = (move[0] - current_move[0], move[1] - current_move[1]) - pointer.move_by(current_move[0], - current_move[1], width=10, height=10) - for pointer in pointers: - pointer.pointer_up() - - touch_action_builder.perform() - - -class TouchDragAction: - def __init__(self, origin, direction): - self.origin = origin - self.direction = direction - self.step = 2 - - -def perform_touch_drag_actions(actions): - driver = actions[0].origin['window'].driver - touch_action_builder = ActionBuilder(driver) - pointers = [PointerActions(source=touch_action_builder.add_pointer_input( - POINTER_TOUCH, f'touch_input_{i}')) for i in range(len(actions))] - - for action, pointer in zip(actions, pointers): - pointer.move_to( - action.origin['window'].element, *action.origin['offset']) - pointer.pointer_down(width=10, height=10, pressure=1) - - moves = [translate_direction_to_offset(a.direction) for a in actions] - - def movement_finished(): - for move in moves: - if move != (0, 0): - return False - return True - - def sign(num): - if num > 0: - return 1 - elif num < 0: - return -1 - return 0 - - while not movement_finished(): - for i in range(len(actions)): - pointer = pointers[i] - move = moves[i] - step = actions[i].step - - current_move = ( - min(abs(move[0]), step) * sign(move[0]), min(abs(move[1]), step) * sign(move[1])) - moves[i] = (move[0] - current_move[0], move[1] - current_move[1]) - pointer.move_by(current_move[0], - current_move[1], width=10, height=10) - - for pointer in pointers: - pointer.pointer_up() - - touch_action_builder.perform() - - -def translate_direction_to_offset(direction): - return (direction.val[1] - direction.val[3], direction.val[2] - direction.val[0]) - - -def call_instance_function(driver, name): - return driver.execute_script( - f'''let result; - window.{name}Callback = data => result = data; - instance.{name}(); - return eval(result);''') - - -class Direction: - def __init__(self): - self.val = (0, 0, 0, 0) - - def __init__(self, north, east, south, west): - self.val = (north, east, south, west) - - def __add__(self, other): - return Direction(self.val[0] + other.val[0], - self.val[1] + other.val[1], - self.val[2] + other.val[2], - self.val[3] + other.val[3]) - - -class UP(Direction): - def __init__(self, step=1): - self.val = (step, 0, 0, 0) - - -class RIGHT(Direction): - def __init__(self, step=1): - self.val = (0, step, 0, 0) - - -class DOWN(Direction): - def __init__(self, step=1): - self.val = (0, 0, step, 0) - - -class LEFT(Direction): - def __init__(self, step=1): - self.val = (0, 0, 0, step) - - -class Handle(Enum): - TOP_LEFT = auto() - TOP = auto() - TOP_RIGHT = auto() - LEFT = auto() - RIGHT = auto() - BOTTOM_LEFT = auto() - BOTTOM = auto() - BOTTOM_RIGHT = auto() - TOP_WINDOW_BAR = auto() - - -class Rect: - def __init__(self, x, y, width, height) -> None: - self.x = x - self.y = y - self.width = width - self.height = height - - def __str__(self): - return f'(x: {self.x}, y: {self.y}, width: {self.width}, height: {self.height})' - - -def assert_rects_equal(geo1, geo2, msg=None): - if geo1.x != geo2.x or geo1.y != geo2.y or geo1.width != geo2.width or geo1.height != geo2.height: - raise AssertionError(f'Rectangles not equal: \n{geo1} \nvs \n{geo2}') - - -unittest.main() diff --git a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp b/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp deleted file mode 100644 index 3a8f0693df..0000000000 --- a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <QtCore/QEvent> -#include <QtCore/qobject.h> -#include <QtCore/qregularexpression.h> -#include <QtGui/qscreen.h> -#include <QtGui/qwindow.h> -#include <QtGui/qguiapplication.h> - -#include <emscripten.h> -#include <emscripten/bind.h> -#include <emscripten/val.h> - -#include <memory> -#include <sstream> -#include <vector> - -class DeleteOnCloseWindow : public QWindow -{ - Q_OBJECT - -private: - void closeEvent(QCloseEvent *ev) override - { - Q_UNUSED(ev); - delete this; - } -}; - -using namespace emscripten; - -std::string toJSArray(const std::vector<std::string> &elements) -{ - std::ostringstream out; - out << "["; - bool comma = false; - for (const auto &element : elements) { - out << (comma ? "," : ""); - out << element; - comma = true; - } - out << "]"; - return out.str(); -} - -std::string toJSString(const QString &qstring) -{ - Q_ASSERT_X(([qstring]() { - static QRegularExpression unescapedQuoteRegex(R"re((?:^|[^\\])')re"); - return qstring.indexOf(unescapedQuoteRegex) == -1; - })(), - Q_FUNC_INFO, "Unescaped single quotes found"); - return "'" + qstring.toStdString() + "'"; -} - -std::string rectToJSObject(const QRect &rect) -{ - std::ostringstream out; - out << "{" - << " x: " << std::to_string(rect.x()) << "," - << " y: " << std::to_string(rect.y()) << "," - << " width: " << std::to_string(rect.width()) << "," - << " height: " << std::to_string(rect.height()) << "}"; - return out.str(); -} - -std::string screenToJSObject(const QScreen &screen) -{ - std::ostringstream out; - out << "{" - << " name: " << toJSString(screen.name()) << "," - << " geometry: " << rectToJSObject(screen.geometry()) << "}"; - return out.str(); -} - -std::string windowToJSObject(const QWindow &window) -{ - std::ostringstream out; - out << "{" - << " id: " << std::to_string(window.winId()) << "," - << " geometry: " << rectToJSObject(window.geometry()) << "," - << " frameGeometry: " << rectToJSObject(window.frameGeometry()) << "," - << " title: '" << window.title().toStdString() << "' }"; - return out.str(); -} - -void windowInformation() -{ - auto windows = qGuiApp->allWindows(); - - std::vector<std::string> windowsAsJsObjects; - windowsAsJsObjects.reserve(windows.size()); - std::transform(windows.begin(), windows.end(), std::back_inserter(windowsAsJsObjects), - [](const QWindow *window) { return windowToJSObject(*window); }); - - emscripten::val::global("window").call<void>("windowInformationCallback", - emscripten::val(toJSArray(windowsAsJsObjects))); -} - -void screenInformation() -{ - auto screens = qGuiApp->screens(); - - std::vector<std::string> screensAsJsObjects; - screensAsJsObjects.reserve(screens.size()); - std::transform(screens.begin(), screens.end(), std::back_inserter(screensAsJsObjects), - [](const QScreen *screen) { return screenToJSObject(*screen); }); - emscripten::val::global("window").call<void>("screenInformationCallback", - emscripten::val(toJSArray(screensAsJsObjects))); -} - -void createWindow(int x, int y, int w, int h, std::string screenId, std::string title) -{ - auto screens = qGuiApp->screens(); - auto screen_it = std::find_if(screens.begin(), screens.end(), [&screenId](QScreen *screen) { - return screen->name() == QString::fromLatin1(screenId); - }); - if (screen_it == screens.end()) { - qWarning() << "No such screen: " << screenId; - return; - } - - auto *window = new DeleteOnCloseWindow; - - window->setFlag(Qt::WindowTitleHint); - window->setFlag(Qt::WindowMaximizeButtonHint); - window->setTitle(QString::fromLatin1(title)); - window->setGeometry(x, y, w, h); - window->setScreen(*screen_it); - window->showNormal(); -} - -EMSCRIPTEN_BINDINGS(qwasmwindow) -{ - emscripten::function("screenInformation", &screenInformation); - emscripten::function("windowInformation", &windowInformation); - emscripten::function("createWindow", &createWindow); -} - -int main(int argc, char **argv) -{ - QGuiApplication app(argc, argv); - - app.exec(); - return 0; -} - -#include "qwasmwindow_harness.moc" diff --git a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html b/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html deleted file mode 100644 index 9eb82618b9..0000000000 --- a/tests/manual/wasm/qwasmwindow/qwasmwindow_harness.html +++ /dev/null @@ -1,66 +0,0 @@ -<!doctype html> - -<head> - <script type="text/javascript" src="qwasmwindow_harness.js"></script> - <script> - (async () => { - const instance = await createQtAppInstance({}); - window.instance = instance; - - const testSandbox = document.createElement('div'); - testSandbox.id = 'test-sandbox'; - document.body.appendChild(testSandbox); - - const makeSizedDiv = (left, top, width, height) => { - const screenDiv = document.createElement('div'); - - screenDiv.style.left = `${left}px`; - screenDiv.style.top = `${top}px`; - screenDiv.style.width = `${width}px`; - screenDiv.style.height = `${height}px`; - screenDiv.style.backgroundColor = 'lightblue'; - - return screenDiv; - }; - - window.testSupport = { - initializeScreenWithFixedPosition: (left, top, width, height) => { - const screenDiv = makeSizedDiv(left, top, width, height); - testSandbox.appendChild(screenDiv); - - screenDiv.style.position = 'fixed'; - instance.qtAddContainerElement(screenDiv); - - return screenDiv; - }, - initializeScreenWithRelativePosition: (left, top, width, height) => { - const screenDiv = makeSizedDiv(left, top, width, height); - testSandbox.appendChild(screenDiv); - - screenDiv.style.position = 'relative'; - instance.qtAddContainerElement(screenDiv); - - return screenDiv; - }, - initializeScreenInScrollContainer: - (scrollWidth, scrollHeight, left, top, width, height) => { - const scrollContainer = document.createElement('div'); - scrollContainer.style.height = `${scrollHeight}px`; - scrollContainer.style.width = `${scrollWidth}px`; - testSandbox.appendChild(scrollContainer); - - const screenDiv = makeSizedDiv(left, top, width, height); - scrollContainer.appendChild(screenDiv); - screenDiv.style.position = 'relative'; - - instance.qtAddContainerElement(screenDiv); - - return [scrollContainer, screenDiv]; - } - }; - })(); - </script> -</head> - -<body> -</body> diff --git a/tests/manual/wasm/qwasmwindow/run.sh b/tests/manual/wasm/qwasmwindow/run.sh deleted file mode 100755 index f6271d6131..0000000000 --- a/tests/manual/wasm/qwasmwindow/run.sh +++ /dev/null @@ -1,23 +0,0 @@ -#! /bin/bash - -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -set -m - -function removeServer() -{ - [ -z "$cleanupPid" ] || kill $cleanupPid -} - -trap removeServer EXIT - -script_dir=`dirname ${BASH_SOURCE[0]}` -cd "$script_dir" -python3 qtwasmserver.py -p 8001 > /dev/null 2>&1 & -cleanupPid=$! - -python3 qwasmwindow.py $@ - -echo 'Press any key to continue...' >&2 -read -n 1 diff --git a/tests/manual/wasm/rasterwindow/main.cpp b/tests/manual/wasm/rasterwindow/main.cpp index 38921c8c30..576b73112b 100644 --- a/tests/manual/wasm/rasterwindow/main.cpp +++ b/tests/manual/wasm/rasterwindow/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> #include "rasterwindow.h" diff --git a/tests/manual/wasm/rasterwindow/rasterwindow.cpp b/tests/manual/wasm/rasterwindow/rasterwindow.cpp index b8da476d46..8fd036c274 100644 --- a/tests/manual/wasm/rasterwindow/rasterwindow.cpp +++ b/tests/manual/wasm/rasterwindow/rasterwindow.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "rasterwindow.h" RasterWindow::RasterWindow() diff --git a/tests/manual/wasm/rasterwindow/rasterwindow.h b/tests/manual/wasm/rasterwindow/rasterwindow.h index b01efb9ddb..e488420440 100644 --- a/tests/manual/wasm/rasterwindow/rasterwindow.h +++ b/tests/manual/wasm/rasterwindow/rasterwindow.h @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef RASTERWINDOW_H #define RASTERWINDOW_H diff --git a/tests/manual/wasm/shared/run.sh b/tests/manual/wasm/shared/run.sh index a8af94e957..f04e45278c 100755 --- a/tests/manual/wasm/shared/run.sh +++ b/tests/manual/wasm/shared/run.sh @@ -1,7 +1,7 @@ #! /bin/bash # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 set -m diff --git a/tests/manual/wasm/shared/testrunner.js b/tests/manual/wasm/shared/testrunner.js index da87026b03..197e3bfa6d 100644 --- a/tests/manual/wasm/shared/testrunner.js +++ b/tests/manual/wasm/shared/testrunner.js @@ -1,6 +1,84 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +function parseQuery() +{ + const trimmed = window.location.search.substring(1); + return new Map( + trimmed.length === 0 ? + [] : + trimmed.split('&').map(paramNameAndValue => + { + const [name, value] = paramNameAndValue.split('='); + return [decodeURIComponent(name), value ? decodeURIComponent(value) : '']; + })); +} + +export class assert +{ + static isFalse(value) + { + if (value !== false) + throw new Error(`Assertion failed, expected to be false, was ${value}`); + } + + static isTrue(value) + { + if (value !== true) + throw new Error(`Assertion failed, expected to be true, was ${value}`); + } + + static isUndefined(value) + { + if (typeof value !== 'undefined') + throw new Error(`Assertion failed, expected to be undefined, was ${value}`); + } + + static isNotUndefined(value) + { + if (typeof value === 'undefined') + throw new Error(`Assertion failed, expected not to be undefined, was ${value}`); + } + + static equal(expected, actual) + { + if (expected !== actual) + throw new Error(`Assertion failed, expected to be ${expected}, was ${actual}`); + } + + static notEqual(expected, actual) + { + if (expected === actual) + throw new Error(`Assertion failed, expected not to be ${expected}`); + } +} + +export class Mock extends Function +{ + #calls = []; + + constructor() + { + super() + const proxy = new Proxy(this, { + apply: (target, _, args) => target.onCall(...args) + }); + proxy.thisMock = this; + + return proxy; + } + + get calls() + { + return this.thisMock.#calls; + } + + onCall(...args) + { + this.#calls.push(args); + } +} + function output(message) { const outputLine = document.createElement('div'); @@ -15,10 +93,12 @@ function output(message) export class TestRunner { #testClassInstance + #timeoutSeconds - constructor(testClassInstance) + constructor(testClassInstance, config) { this.#testClassInstance = testClassInstance; + this.#timeoutSeconds = config?.timeoutSeconds ?? 2; } async run(testCase) @@ -39,8 +119,8 @@ export class TestRunner const timeout = window.setTimeout(() => { rejected = true; - reject(new Error('Timeout after 2 seconds')); - }, 2000); + reject(new Error(`Timeout after ${this.#timeoutSeconds} seconds`)); + }, this.#timeoutSeconds * 1000); prototype[testCase].apply(this.#testClassInstance).then(() => { if (!rejected) { @@ -61,12 +141,15 @@ export class TestRunner async runAll() { + const query = parseQuery(); + const testFilter = query.has('testfilter') ? new RegExp(query.get('testfilter')) : undefined; + const SPECIAL_FUNCTIONS = ['beforeEach', 'afterEach', 'beforeAll', 'afterAll', 'constructor']; const prototype = Object.getPrototypeOf(this.#testClassInstance); const testFunctions = Object.getOwnPropertyNames(prototype).filter( - entry => SPECIAL_FUNCTIONS.indexOf(entry) === -1); + entry => SPECIAL_FUNCTIONS.indexOf(entry) === -1 && (!testFilter || entry.match(testFilter))); if (prototype.beforeAll) await prototype.beforeAll.apply(this.#testClassInstance); |