diff options
Diffstat (limited to 'tests/manual')
6 files changed, 668 insertions, 1 deletions
diff --git a/tests/manual/sandboxed_file_access/CMakeLists.txt b/tests/manual/sandboxed_file_access/CMakeLists.txt new file mode 100644 index 00000000000..8df09401cf9 --- /dev/null +++ b/tests/manual/sandboxed_file_access/CMakeLists.txt @@ -0,0 +1,71 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_manual_sandboxed_file_access LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_standard_project_setup() + +qt_add_executable(tst_manual_sandboxed_file_access + tst_sandboxed_file_access.cpp +) + +target_link_libraries(tst_manual_sandboxed_file_access PRIVATE + Qt::CorePrivate + Qt::Widgets + Qt::Test +) + +enable_language(OBJCXX) +set_source_files_properties(tst_sandboxed_file_access.cpp PROPERTIES LANGUAGE OBJCXX) + +if(MACOS) + target_sources(tst_manual_sandboxed_file_access PRIVATE app.entitlements) + set_target_properties(tst_manual_sandboxed_file_access PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.dev.tst-manual-sandboxed-file-access" + XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/app.entitlements" + XCODE_ATTRIBUTE_COPY_PHASE_STRIP FALSE + ) + if(NOT CMAKE_GENERATOR STREQUAL "Xcode") + set_target_properties(tst_manual_sandboxed_file_access PROPERTIES + RESOURCE "${CMAKE_CURRENT_SOURCE_DIR}/app.entitlements" + ) + endif() + + set(platform_plugin "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_PLUGINS}/platforms/libqcocoa.dylib") + target_sources(tst_manual_sandboxed_file_access PRIVATE ${platform_plugin}) + set_source_files_properties(${platform_plugin} + PROPERTIES + MACOSX_PACKAGE_LOCATION PlugIns/platforms + ) + + target_compile_definitions(tst_manual_sandboxed_file_access PRIVATE + QTEST_THROW_ON_FAIL + QTEST_THROW_ON_SKIP + ) +endif() + +if(IOS) + set(plist_path "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.ios") +else() + set(plist_path "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.macos") +endif() + +set_target_properties(tst_manual_sandboxed_file_access + PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${plist_path}") + +install(TARGETS tst_manual_sandboxed_file_access + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET tst_manual_sandboxed_file_access + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/tests/manual/sandboxed_file_access/Info.plist.ios b/tests/manual/sandboxed_file_access/Info.plist.ios new file mode 100644 index 00000000000..c6072cffa92 --- /dev/null +++ b/tests/manual/sandboxed_file_access/Info.plist.ios @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BuildMachineOSBuild</key> + <string>25B78</string> + <key>CFBundleAllowMixedLocalizations</key> + <true/> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>tst_manual_sandboxed_file_access</string> + <key>CFBundleExecutable</key> + <string>tst_manual_sandboxed_file_access</string> + <key>CFBundleIdentifier</key> + <string>io.qt.fb.tst-manual-sandboxed-file-access</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>tst_manual_sandboxed_file_access</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>6.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>iPhoneOS</string> + </array> + <key>CFBundleVersion</key> + <string>6.0.0</string> + <key>DTCompiler</key> + <string>com.apple.compilers.llvm.clang.1_0</string> + <key>DTPlatformBuild</key> + <string>23B77</string> + <key>DTPlatformName</key> + <string>iphoneos</string> + <key>DTPlatformVersion</key> + <string>26.1</string> + <key>DTSDKBuild</key> + <string>23B77</string> + <key>DTSDKName</key> + <string>iphoneos26.1</string> + <key>DTXcode</key> + <string>2610</string> + <key>DTXcodeBuild</key> + <string>17B55</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>MinimumOSVersion</key> + <string>17</string> + <key>NOTE</key> + <string>This file was generated by Qt's default CMake support.</string> + <key>UIDeviceFamily</key> + <array> + <integer>1</integer> + <integer>2</integer> + </array> + <key>UILaunchStoryboardName</key> + <string>LaunchScreen</string> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>LSItemContentTypes</key> + <array> + <string>public.text</string> + </array> + <!-- These two don't seem to be needed to make things work --> + <key>LSHandlerRank</key> + <string>Default</string> + <key>CFBundleTypeName</key> + <string>Text files</string> + </dict> + </array> + <key>UISupportsDocumentBrowser</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/sandboxed_file_access/Info.plist.macos b/tests/manual/sandboxed_file_access/Info.plist.macos new file mode 100644 index 00000000000..81a93f0353f --- /dev/null +++ b/tests/manual/sandboxed_file_access/Info.plist.macos @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BuildMachineOSBuild</key> + <string>25B78</string> + <key>CFBundleAllowMixedLocalizations</key> + <true/> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>tst_manual_sandboxed_file_access</string> + <key>CFBundleIdentifier</key> + <string>io.qt.dev.tst-manual-sandboxed-file-access</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>tst_manual_sandboxed_file_access</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>6.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>6.0.0</string> + <key>DTCompiler</key> + <string>com.apple.compilers.llvm.clang.1_0</string> + <key>DTPlatformBuild</key> + <string>25B74</string> + <key>DTPlatformName</key> + <string>macosx</string> + <key>DTPlatformVersion</key> + <string>26.1</string> + <key>DTSDKBuild</key> + <string>25B74</string> + <key>DTSDKName</key> + <string>macosx26.1</string> + <key>DTXcode</key> + <string>2610</string> + <key>DTXcodeBuild</key> + <string>17B55</string> + <key>LSMinimumSystemVersion</key> + <string>13</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>LSItemContentTypes</key> + <array> + <string>public.text</string> + </array> + <!-- These two don't seem to be needed to make things work --> + <key>LSHandlerRank</key> + <string>Default</string> + <key>CFBundleTypeName</key> + <string>Text files</string> + </dict> + </array> +</dict> +</plist> diff --git a/tests/manual/sandboxed_file_access/app.entitlements b/tests/manual/sandboxed_file_access/app.entitlements new file mode 100644 index 00000000000..6d968edb4f8 --- /dev/null +++ b/tests/manual/sandboxed_file_access/app.entitlements @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.app-sandbox</key> + <true/> + <key>com.apple.security.files.user-selected.read-write</key> + <true/> +</dict> +</plist> diff --git a/tests/manual/sandboxed_file_access/tst_sandboxed_file_access.cpp b/tests/manual/sandboxed_file_access/tst_sandboxed_file_access.cpp new file mode 100644 index 00000000000..18381ce0c8c --- /dev/null +++ b/tests/manual/sandboxed_file_access/tst_sandboxed_file_access.cpp @@ -0,0 +1,422 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtCore> +#include <QtWidgets> +#include <QtTest> + +#include <Foundation/Foundation.h> + +#if defined(Q_OS_MACOS) && defined(QT_BUILD_INTERNAL) +#include <private/qcore_mac_p.h> +Q_CONSTRUCTOR_FUNCTION(qt_mac_ensureResponsible); +#endif + +class tst_SandboxedFileAccess : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void alwaysAccessibleLocations(); + + void standardPaths_data(); + void standardPaths(); + + void readSingleFile(); + void writeSingleFile(); + void writeSingleFileNonCanonical(); + + void removeFile(); + void trashFile(); + + void readFileAfterRestart(); + + void directoryAccess(); + + void securityScopedTargetFile(); + + void fileOpenEvent(); + +private: + void writeFile(const QString &fileName); + QByteArray readFile(const QString &fileName); + + QString getFileName(QFileDialog::AcceptMode, QFileDialog::FileMode, + const QString &action = QString(), const QString &fileName = QString()); + + QString sandboxPath() const + { + return QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); + } + + QString bundlePath() const + { + QString path = QCoreApplication::applicationDirPath(); +#if defined(Q_OS_MACOS) + path.remove("/Contents/MacOS"); +#endif + return path; + } + + QStringList m_persistedFileNames; + QPointer<QWidget> m_widget; +}; + +void tst_SandboxedFileAccess::initTestCase() +{ + qDebug() << "đĻ App bundle" << bundlePath(); + qDebug() << "đ App container" << sandboxPath(); + + m_widget = new QWidget; + m_widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(m_widget)); +} + +void tst_SandboxedFileAccess::cleanupTestCase() +{ + NSURL *appSupportDir = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)).toNSURL(); + NSURL *bookmarksFile = [appSupportDir URLByAppendingPathComponent:@"SecurityScopedBookmarks.plist"]; + NSError *error = nullptr; + NSMutableDictionary *bookmarks = [[NSDictionary dictionaryWithContentsOfURL:bookmarksFile + error:&error] mutableCopy]; + for (NSString *path in bookmarks.allKeys) { + if (m_persistedFileNames.contains(QString::fromNSString(path))) { + qDebug() << "Keeping knowledge of persisted path" << path; + continue; + } + qDebug() << "Wiping knowledge of path" << path; + [bookmarks removeObjectForKey:path]; + } + [bookmarks writeToURL:bookmarksFile error:&error]; + + qGuiApp->quit(); +} + +void tst_SandboxedFileAccess::alwaysAccessibleLocations() +{ + readFile(QCoreApplication::applicationFilePath()); + + // The documents location is inside the sandbox and writable on both iOS and macOS + auto documents = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + writeFile(documents + "/test-writable-file.txt"); +} + +void tst_SandboxedFileAccess::standardPaths_data() +{ + QTest::addColumn<QStandardPaths::StandardLocation>("location"); + auto standardLocations = QMetaEnum::fromType<QStandardPaths::StandardLocation>(); + for (int i = 0; i < standardLocations.keyCount(); ++i) + QTest::newRow(standardLocations.key(i)) << QStandardPaths::StandardLocation(standardLocations.value(i)); +} + +void tst_SandboxedFileAccess::standardPaths() +{ + QFETCH(QStandardPaths::StandardLocation, location); + auto writableLocation = QStandardPaths::writableLocation(location); + + if (writableLocation.isEmpty()) + QSKIP("There's no writable location for this location"); + + QFileInfo info(writableLocation); + if (info.isSymLink() && !info.symLinkTarget().startsWith(sandboxPath())) + QSKIP("This location is a symlink to outside the sandbox and requires access"); + + QVERIFY(QDir().mkpath(writableLocation)); + +#if !defined(Q_OS_MACOS) + QEXPECT_FAIL("HomeLocation", "The sandbox root is not writable on iOS", Abort); +#endif + writeFile(writableLocation + QString("/test-writable-file-%1.txt").arg(QTest::currentDataTag())); +} + +void tst_SandboxedFileAccess::readSingleFile() +{ + QString filePath = getFileName(QFileDialog::AcceptOpen, + QFileDialog::ExistingFile, "Choose file to read"); + readFile(filePath); + + { + QFile file(QCoreApplication::applicationFilePath()); + QVERIFY(file.open(QFile::ReadOnly)); + QByteArray plistContent = file.read(100); + file.close(); + + // Check that setFileName can target a security scoped file + file.setFileName(filePath); + QVERIFY(file.open(QFile::ReadOnly)); + QVERIFY(file.isReadable()); + QCOMPARE_NE(file.read(100), plistContent); + } + + QDir dir; + QString fileName; + + { + QFileInfo info(filePath); + dir = info.path(); + fileName = info.fileName(); + QVERIFY(dir.exists()); + QVERIFY(!fileName.isEmpty()); + } + + // Check that we're able to access files via non-canonical paths + readFile(dir.absolutePath() + "/../" + dir.dirName() + "/" + fileName); +} + +QByteArray tst_SandboxedFileAccess::readFile(const QString &fileName) +{ + QFile file(fileName); + QVERIFY(file.exists()); + QVERIFY(file.open(QFile::ReadOnly)); + QVERIFY(file.isReadable()); + QByteArray data = file.read(100); + QVERIFY(!data.isEmpty()); + return data; +} + +void tst_SandboxedFileAccess::writeSingleFile() +{ + QString filePath = getFileName(QFileDialog::AcceptSave, QFileDialog::AnyFile, + "Choose a file to write", "write-single-file.txt"); + writeFile(filePath); + readFile(filePath); +} + +void tst_SandboxedFileAccess::writeSingleFileNonCanonical() +{ + QString filePath = getFileName(QFileDialog::AcceptSave, QFileDialog::AnyFile, + "Choose a file to write", "write-single-file-non-canonical.txt"); + QDir dir; + QString fileName; + + { + QFileInfo info(filePath); + dir = info.path(); + fileName = info.fileName(); + QVERIFY(dir.exists()); + QVERIFY(!fileName.isEmpty()); + } + + writeFile(dir.absolutePath() + "/../" + dir.dirName() + "/" + fileName); + readFile(filePath); +} + +void tst_SandboxedFileAccess::writeFile(const QString &fileName) +{ + QFile file(fileName); + QVERIFY(file.open(QFile::WriteOnly)); + QVERIFY(file.isWritable()); + QVERIFY(file.write("Hello world")); +} + +void tst_SandboxedFileAccess::removeFile() +{ + QString fileName = getFileName(QFileDialog::AcceptSave, QFileDialog::AnyFile, + "Choose a file to write and then remove", "write-and-remove-file.txt"); + writeFile(fileName); + + { + QFile file(fileName); + QVERIFY(file.remove()); + } +} + +void tst_SandboxedFileAccess::trashFile() +{ + QString fileName = getFileName(QFileDialog::AcceptSave, QFileDialog::AnyFile, + "Choose a file to write and then trash", "write-and-trash-file.txt"); + writeFile(fileName); + + { + QFile file(fileName); + QVERIFY(file.moveToTrash()); + } +} + +void tst_SandboxedFileAccess::readFileAfterRestart() +{ + // Every other restart of the app will save a file or load a previously saved file + + QSettings settings; + QString savedFile = settings.value("savedFile").toString(); + if (savedFile.isEmpty()) { + QString filePath = getFileName(QFileDialog::AcceptSave, QFileDialog::AnyFile, + "Choose a file to write for reading after restart", "write-and-read-after-restart.txt"); + qDebug() << "Writing" << filePath << "and saving to preferences"; + writeFile(filePath); + settings.setValue("savedFile", filePath); + m_persistedFileNames << filePath; + } else { + qDebug() << "Loading" << savedFile << "from preferences"; + settings.remove("savedFile"); // Remove up front, in case this fails + readFile(savedFile); + QFile file(savedFile); + QVERIFY(file.remove()); + } +} + +void tst_SandboxedFileAccess::directoryAccess() +{ + // Every other restart of the app will re-establish access to the folder, + // or re-use previous access. + + QSettings settings; + QString directory = settings.value("savedDirectory").toString(); + if (directory.isEmpty()) { + directory = getFileName(QFileDialog::AcceptOpen, QFileDialog::Directory, + "Choose a directory we can create some files in"); + auto canonical = QFileInfo(directory).canonicalFilePath(); + QVERIFY(!canonical.isEmpty()); + directory = canonical; + settings.setValue("savedDirectory", directory); + m_persistedFileNames << QFileInfo(directory).canonicalFilePath(); + } else { + settings.remove("savedDirectory"); + } + settings.sync(); + + QString fileInDir; + + { + QDir dir(directory); + QVERIFY(dir.exists()); + QVERIFY(dir.isReadable()); + fileInDir = dir.filePath("file-in-dir.txt"); + } + + writeFile(fileInDir); + readFile(fileInDir); + + { + QDir dir(directory); + QVERIFY(dir.count() > 0); + QVERIFY(dir.entryList().contains("file-in-dir.txt")); + } + + { + QDir dir(directory); + QVERIFY(dir.mkdir("subdirectory")); + QVERIFY(dir.entryList().contains("subdirectory")); + fileInDir = dir.filePath("subdirectory/file-in-subdir.txt"); + } + + writeFile(fileInDir); + readFile(fileInDir); + + // Check that we can write to a non-canonical path within the directory + // we have access to, and then read it from the canonical path. + writeFile(directory + "/subdirectory/../non-existing-non-canonical.txt"); + readFile(directory + "/non-existing-non-canonical.txt"); + + { + QDir dir(directory); + QVERIFY(dir.cd("subdirectory")); + dir.removeRecursively(); + } +} + +void tst_SandboxedFileAccess::securityScopedTargetFile() +{ + // This is a non-security scoped file + auto documents = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString sourceFilePath = documents + "/test-security-scoped-target-file.txt"; + writeFile(sourceFilePath); + QFile sourceFile(sourceFilePath); + + QString directory = getFileName(QFileDialog::AcceptOpen, QFileDialog::Directory, + "Choose a directory we can link/copy some to"); + + QString subDirectory; + { + QDir dir(directory); + QVERIFY(dir.mkdir("subdirectory")); + QVERIFY(dir.entryList().contains("subdirectory")); + subDirectory = dir.filePath("subdirectory"); + } + + QVERIFY(sourceFile.copy(subDirectory + "/copied-file.txt")); + QVERIFY(sourceFile.link(subDirectory + "/linked-file.txt")); + QVERIFY(sourceFile.rename(subDirectory + "/renamed-file.txt")); + + { + QDir dir(directory); + QVERIFY(dir.cd("subdirectory")); + dir.removeRecursively(); + } +} + +void tst_SandboxedFileAccess::fileOpenEvent() +{ + struct OpenEventFilter : public QObject + { + bool eventFilter(QObject *watched, QEvent *event) override + { + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event); + fileName = openEvent->file(); + } + + return QObject::eventFilter(watched, event); + } + + QString fileName; + }; + + OpenEventFilter openEventFilter; + qGuiApp->installEventFilter(&openEventFilter); + + m_widget->setLayout(new QVBoxLayout); + QLabel label; + label.setWordWrap(true); + m_widget->layout()->addWidget(&label); +#if defined(Q_OS_MACOS) + label.setText("Drag a text file to the app's Dock icon, or open in the app via Finder's 'Open With' menu"); +#else + label.setText("Open the Files app, and choose 'Open With' or share a text document with this app"); +#endif + label.show(); + + QTRY_VERIFY_WITH_TIMEOUT(!openEventFilter.fileName.isNull(), 30s); + label.setText("Got file: " + openEventFilter.fileName); + + readFile(openEventFilter.fileName); + + QTest::qWait(3000); +} + +QString tst_SandboxedFileAccess::getFileName(QFileDialog::AcceptMode acceptMode, QFileDialog::FileMode fileMode, + const QString &action, const QString &fileName) +{ + QFileDialog dialog(m_widget); + dialog.setAcceptMode(acceptMode); + dialog.setFileMode(fileMode); + dialog.setWindowTitle(action); + dialog.setLabelText(QFileDialog::Accept, action); + dialog.selectFile(fileName); + if (!action.isEmpty()) + qDebug() << "âšī¸" << action; + dialog.exec(); + auto selectedFiles = dialog.selectedFiles(); + return selectedFiles.count() ? selectedFiles.first() : QString(); +} + +int main(int argc, char** argv) +{ + QApplication app(argc, argv); + + tst_SandboxedFileAccess testObject; + + // Run tests with QApp running + int testExecResult = 0; + QMetaObject::invokeMethod(&testObject, [&]{ + testExecResult = QTest::qExec(&testObject, argc, argv); + }, Qt::QueuedConnection); + + [[maybe_unused]] int appExecResult = app.exec(); + return testExecResult; +} + +#include "tst_sandboxed_file_access.moc" diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp index 1e49847c97f..484c28a484b 100644 --- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp +++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp @@ -9,6 +9,12 @@ #include <emscripten.h> #include <emscripten/threading.h> +#if QT_CONFIG(wasm_jspi) +# define QT_WASM_EMSCRIPTEN_ASYNC ,emscripten::async() +#else +# define QT_WASM_EMSCRIPTEN_ASYNC +#endif + namespace QtWasmTest { namespace { QObject *g_testObject = nullptr; @@ -127,7 +133,7 @@ void passTest() EMSCRIPTEN_BINDINGS(qtwebtestrunner) { emscripten::function("cleanupTestCase", &cleanupTestCase); emscripten::function("getTestFunctions", &getTestFunctions); - emscripten::function("runTestFunction", &runTestFunction, emscripten::async()); + emscripten::function("runTestFunction", &runTestFunction QT_WASM_EMSCRIPTEN_ASYNC); emscripten::function("qtWasmFail", &failTest); emscripten::function("qtWasmPass", &passTest); } |
