summaryrefslogtreecommitdiffstats
path: root/tests/manual/wasm/qtloader_integration/test_body.js
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manual/wasm/qtloader_integration/test_body.js')
-rw-r--r--tests/manual/wasm/qtloader_integration/test_body.js517
1 files changed, 517 insertions, 0 deletions
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..e08ffdefbb
--- /dev/null
+++ b/tests/manual/wasm/qtloader_integration/test_body.js
@@ -0,0 +1,517 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDXLicenseIdentifier: 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();
+})();