qt 6.6.0 clean

This commit is contained in:
kleuter
2023-11-01 22:23:55 +01:00
parent 7b5ada15e7
commit 5d8194efa7
1449 changed files with 134276 additions and 31391 deletions

View File

@ -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)

View File

@ -0,0 +1,171 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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 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 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"

View File

@ -0,0 +1,10 @@
[
{
"source": "qtloader.js",
"destination": "/preload/qtloader.js"
},
{
"source": "$QTDIR/qtlogo.svg",
"destination": "/preload/qtlogo.svg"
}
]

View File

@ -0,0 +1,469 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDXLicenseIdentifier: LicenseRefQtCommercial OR GPL3.0only
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 useDefaultOnMissingEntryFunction()
{
const instance = await qtLoad({ arguments: ['--no-gui'], qt: {}});
assert.isNotUndefined(instance);
}
async environmentVariables()
{
const instance = await qtLoad({
qt: {
environment: {
variable1: 'value1',
variable2: 'value2'
},
entryFunction: createQtAppInstance,
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: createQtAppInstance,
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: createQtAppInstance,
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: createQtAppInstance,
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: createQtAppInstance,
}
});
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: createQtAppInstance,
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: createQtAppInstance,
}
});
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: createQtAppInstance,
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: createQtAppInstance,
}
});
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: createQtAppInstance,
}
});
} catch (e) {
caughtException = e;
}
assert.isUndefined(caughtException);
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 userAbortCallbackCalled()
{
const onAbortMock = new Mock();
let instance = await qtLoad({
arguments: ['--no-gui'],
onAbort: onAbortMock,
qt: {
entryFunction: createQtAppInstance,
}
});
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: createQtAppInstance,
}
});
// 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: createQtAppInstance,
}
});
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: createQtAppInstance,
}
});
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();
})();

View File

@ -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>