summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/cmake/CMakeLists.txt104
-rw-r--r--tests/auto/cmake/test_android_signing/CMakeLists.txt53
-rw-r--r--tests/auto/cmake/test_android_signing/main.cpp4
-rw-r--r--tests/auto/cmake/test_android_signing/verify_signature.cmake25
-rw-r--r--tests/auto/corelib/io/qfile/tst_qfile.cpp32
-rw-r--r--tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp29
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp4
-rw-r--r--tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp28
-rw-r--r--tests/auto/corelib/platform/CMakeLists.txt4
-rw-r--r--tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle2
-rw-r--r--tests/auto/corelib/text/qlocale/tst_qlocale.cpp6
-rw-r--r--tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp89
-rw-r--r--tests/auto/gui/image/qicoimageformat/tst_qicoimageformat.cpp36
-rw-r--r--tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp3
-rw-r--r--tests/auto/gui/kernel/qwindow/BLACKLIST1
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_qwindow.cpp2
-rw-r--r--tests/auto/gui/qopengl/tst_qopengl.cpp3
-rw-r--r--tests/auto/gui/qopenglconfig/tst_qopenglconfig.cpp3
-rw-r--r--tests/auto/gui/text/qfont/BLACKLIST3
-rw-r--r--tests/auto/network/kernel/qnetworkinterface/BLACKLIST5
-rw-r--r--tests/auto/other/android/CMakeLists.txt8
-rw-r--r--tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp9
-rw-r--r--tests/auto/other/android/dynamic_feature/CMakeLists.txt33
-rw-r--r--tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt8
-rw-r--r--tests/auto/other/android/dynamic_feature/feature/qtlogo.pngbin0 -> 1214 bytes
-rw-r--r--tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java175
-rw-r--r--tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java92
-rw-r--r--tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java26
-rw-r--r--tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp227
-rw-r--r--tests/auto/other/android/dynamic_feature/storeloader/storeloader.h65
-rw-r--r--tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp37
-rw-r--r--tests/auto/other/android/package_source_dir/CMakeLists.txt54
-rw-r--r--tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml51
-rw-r--r--tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml51
-rw-r--r--tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml51
-rw-r--r--tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in54
-rw-r--r--tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp47
-rw-r--r--tests/auto/other/android/permissions/CMakeLists.txt23
-rw-r--r--tests/auto/other/android/permissions/tst_android_permissions.cpp80
-rw-r--r--tests/auto/testlib/selftests/mouse/tst_mouse.cpp4
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp2
-rw-r--r--tests/auto/tools/qmake/testdata/windows_resources/REUSE.toml2
-rw-r--r--tests/auto/tools/uic/baseline/REUSE.toml2
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp3
-rw-r--r--tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp16
-rw-r--r--tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp224
-rw-r--r--tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp34
-rw-r--r--tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp35
-rw-r--r--tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp6
-rw-r--r--tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp2
50 files changed, 1668 insertions, 189 deletions
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index 7c4f33cddb6..f4789d115a6 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -70,7 +70,7 @@ enable_testing()
# doesn't seem to be the case.
# Until this is figured out, disable the tests when cross-compiling to Linux.
if(UNIX AND NOT APPLE AND NOT WIN32 AND CMAKE_CROSSCOMPILING AND NOT QT_ENABLE_CMAKE_BOOT2QT_TESTS
- AND NOT QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS)
+ AND NOT QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS AND NOT ANDROID)
message(STATUS "Running CMake tests is disabled when cross-compiling to Linux / Boot2Qt.")
return()
endif()
@@ -83,6 +83,12 @@ if(WIN32 AND CMAKE_CROSSCOMPILING)
return()
endif()
+if(TARGET Qt6::Core)
+ # Tests are built as part of the qtbase build tree.
+ # Setup paths so that the Qt packages are found, similar to examples.
+ qt_internal_set_up_build_dir_package_paths()
+endif()
+
set(required_packages Core Network Xml Sql Test TestInternalsPrivate)
set(optional_packages DBus Gui Widgets PrintSupport OpenGL Concurrent)
@@ -117,53 +123,73 @@ endif()
include("${_Qt6CTestMacros}")
-# Test only multi-abi specific functionality when QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS is ON.
-# Qt::Gui is the prerequisite for all Android tests.
-if(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS AND NOT NO_GUI)
- unset(multi_abi_vars)
- foreach(abi IN LISTS QT_ANDROID_ABIS)
- list(APPEND multi_abi_vars "-DQT_PATH_ANDROID_ABI_${abi}=${QT_PATH_ANDROID_ABI_${abi}}")
- endforeach()
- if(QT_ANDROID_BUILD_ALL_ABIS)
- list(APPEND multi_abi_vars "-DQT_ANDROID_BUILD_ALL_ABIS=${QT_ANDROID_BUILD_ALL_ABIS}")
+
+if(ANDROID)
+ # Qt::Gui is the prerequisite for all Android tests.
+ if(NO_GUI)
+ return()
endif()
- list(APPEND multi_abi_vars "-DQT_HOST_PATH=${QT_HOST_PATH}")
+ set(common_android_vars "-DQT_HOST_PATH=${QT_HOST_PATH}")
- set(multi_abi_forward_vars
- TEST_SINGLE_VALUE_ARG
- TEST_SPACES_VALUE_ARG
- TEST_LIST_VALUE_ARG
- TEST_ESCAPING_VALUE_ARG
+ _qt_internal_test_expect_pass(test_android_signing
+ BUILD_OPTIONS "${common_android_vars}"
+ BINARY ${CMAKE_CTEST_COMMAND}
+ BINARY_ARGS -V
)
- string(REPLACE ";" "[[;]]" multi_abi_forward_vars "${multi_abi_forward_vars}")
-
- set(single_value "TestValue")
- set(list_value "TestValue[[;]]TestValue2[[;]]TestValue3")
- set(escaping_value "TestValue\\\\[[;]]TestValue2\\\\[[;]]TestValue3")
- set(spaces_value "TestValue TestValue2 TestValue3")
- _qt_internal_test_expect_pass(test_android_multi_abi_forward_vars
- BUILD_OPTIONS
- ${multi_abi_vars}
- "-DQT_ANDROID_MULTI_ABI_FORWARD_VARS=${multi_abi_forward_vars}"
- "-DTEST_SINGLE_VALUE_ARG=${single_value}"
- "-DTEST_LIST_VALUE_ARG=${list_value}"
- "-DTEST_ESCAPING_VALUE_ARG=${escaping_value}"
- "-DTEST_SPACES_VALUE_ARG=${spaces_value}"
+ set_property(TEST test_android_signing APPEND PROPERTY ENVIRONMENT
+ "QT_ANDROID_KEYSTORE_PATH=${CMAKE_CURRENT_BINARY_DIR}/test_android_signing/qttest.jks"
+ "QT_ANDROID_KEYSTORE_ALIAS=qttest"
+ "QT_ANDROID_KEYSTORE_STORE_PASS=qttest"
+ "QT_ANDROID_KEYSTORE_KEY_PASS=qttest"
)
- #Run test_android_aar only if the host is Unix and zipinfo is available
- find_program(ZIPINFO_EXECUTABLE zipinfo)
- if(CMAKE_HOST_UNIX AND ZIPINFO_EXECUTABLE)
- _qt_internal_test_expect_pass(test_android_aar
+ # Test only multi-abi specific functionality when QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS is
+ # ON.
+ if(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS)
+ set(multi_abi_vars "${common_android_vars}")
+ foreach(abi IN LISTS QT_ANDROID_ABIS)
+ list(APPEND multi_abi_vars "-DQT_PATH_ANDROID_ABI_${abi}=${QT_PATH_ANDROID_ABI_${abi}}")
+ endforeach()
+ if(QT_ANDROID_BUILD_ALL_ABIS)
+ list(APPEND multi_abi_vars "-DQT_ANDROID_BUILD_ALL_ABIS=${QT_ANDROID_BUILD_ALL_ABIS}")
+ endif()
+
+ set(multi_abi_forward_vars
+ TEST_SINGLE_VALUE_ARG
+ TEST_SPACES_VALUE_ARG
+ TEST_LIST_VALUE_ARG
+ TEST_ESCAPING_VALUE_ARG
+ )
+ string(REPLACE ";" "[[;]]" multi_abi_forward_vars "${multi_abi_forward_vars}")
+
+ set(single_value "TestValue")
+ set(list_value "TestValue[[;]]TestValue2[[;]]TestValue3")
+ set(escaping_value "TestValue\\\\[[;]]TestValue2\\\\[[;]]TestValue3")
+ set(spaces_value "TestValue TestValue2 TestValue3")
+ _qt_internal_test_expect_pass(test_android_multi_abi_forward_vars
BUILD_OPTIONS
${multi_abi_vars}
- --build-target verify_aar
+ "-DQT_ANDROID_MULTI_ABI_FORWARD_VARS=${multi_abi_forward_vars}"
+ "-DTEST_SINGLE_VALUE_ARG=${single_value}"
+ "-DTEST_LIST_VALUE_ARG=${list_value}"
+ "-DTEST_ESCAPING_VALUE_ARG=${escaping_value}"
+ "-DTEST_SPACES_VALUE_ARG=${spaces_value}"
)
- else()
- message(WARNING
- "Skipping test_android_aar CMake build test because \
- the host is not Unix or zipinfo is not found")
+
+ #Run test_android_aar only if the host is Unix and zipinfo is available
+ find_program(ZIPINFO_EXECUTABLE zipinfo)
+ if(CMAKE_HOST_UNIX AND ZIPINFO_EXECUTABLE)
+ _qt_internal_test_expect_pass(test_android_aar
+ BUILD_OPTIONS
+ ${multi_abi_vars}
+ --build-target verify_aar
+ )
+ else()
+ message(WARNING
+ "Skipping test_android_aar CMake build test because \
+ the host is not Unix or zipinfo is not found")
+ endif()
endif()
return()
diff --git a/tests/auto/cmake/test_android_signing/CMakeLists.txt b/tests/auto/cmake/test_android_signing/CMakeLists.txt
new file mode 100644
index 00000000000..b2d67676a69
--- /dev/null
+++ b/tests/auto/cmake/test_android_signing/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.20)
+
+project(test_android_signing)
+
+enable_testing()
+
+find_program(keytool NAMES keytool)
+
+if(NOT keytool)
+ message("Skipping test_android_signing: keytool not found.")
+ return()
+endif()
+
+if("$ENV{QT_ANDROID_KEYSTORE_PATH}" STREQUAL ""
+ OR "$ENV{QT_ANDROID_KEYSTORE_STORE_PASS}" STREQUAL ""
+ OR "$ENV{QT_ANDROID_KEYSTORE_ALIAS}" STREQUAL ""
+ OR "$ENV{QT_ANDROID_KEYSTORE_KEY_PASS}" STREQUAL "")
+ message(FATAL_ERROR "Environment variables QT_ANDROID_KEYSTORE_PATH,"
+ " QT_ANDROID_KEYSTORE_STORE_PASS, QT_ANDROID_KEYSTORE_ALIAS, and"
+ " QT_ANDROID_KEYSTORE_KEY_PASS must be set to run this test.")
+endif()
+
+find_package(Qt6 COMPONENTS Core Gui REQUIRED)
+
+set(QT_ANDROID_SIGN_AAB ON)
+set(QT_ANDROID_SIGN_APK ON)
+
+execute_process(COMMAND ${keytool}
+ -genkey -alias "$ENV{QT_ANDROID_KEYSTORE_ALIAS}" -keyalg RSA
+ -keystore "$ENV{QT_ANDROID_KEYSTORE_PATH}"
+ -noprompt
+ -dname "CN=qttest, OU=ID, O=Qt, L=Qt, S=Test, C=DE"
+ -storepass "$ENV{QT_ANDROID_KEYSTORE_STORE_PASS}"
+ -keypass "$ENV{QT_ANDROID_KEYSTORE_KEY_PASS}"
+ RESULT_VARIABLE keytool_result
+)
+
+
+qt6_add_executable(test_android_signing main.cpp)
+
+target_link_libraries(test_android_signing PRIVATE
+ Qt6::Core
+ Qt6::Gui
+)
+
+add_test(NAME test_android_signing_verify_signature
+ COMMAND "${CMAKE_COMMAND}" "-DKEYTOOL_PATH=${keytool}"
+ "-DAPK_FILE=${CMAKE_CURRENT_BINARY_DIR}/android-build/test_android_signing.apk"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/verify_signature.cmake"
+)
diff --git a/tests/auto/cmake/test_android_signing/main.cpp b/tests/auto/cmake/test_android_signing/main.cpp
new file mode 100644
index 00000000000..2112a0de739
--- /dev/null
+++ b/tests/auto/cmake/test_android_signing/main.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+int main(int, char *[]) { return 0; }
diff --git a/tests/auto/cmake/test_android_signing/verify_signature.cmake b/tests/auto/cmake/test_android_signing/verify_signature.cmake
new file mode 100644
index 00000000000..d4405a30201
--- /dev/null
+++ b/tests/auto/cmake/test_android_signing/verify_signature.cmake
@@ -0,0 +1,25 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT KEYTOOL_PATH)
+ message(FATAL_ERROR "KEYTOOL_PATH is missing.")
+endif()
+
+if(NOT APK_FILE)
+ message(FATAL_ERROR "APK_FILE is missing.")
+endif()
+
+execute_process(
+ COMMAND ${KEYTOOL_PATH} -printcert -jarfile ${APK_FILE}
+ RESULT_VARIABLE keytool_result
+ OUTPUT_VARIABLE keytool_output
+ ERROR_VARIABLE keytool_error
+)
+
+if(NOT keytool_result EQUAL 0)
+ message(FATAL_ERROR "Keytool command failed with error: ${keytool_error}")
+endif()
+
+if(NOT keytool_output MATCHES ".+Certificate #1:.+")
+ message(FATAL_ERROR "APK is not signed.")
+endif()
diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp
index 781f64a31ab..66c3db2e2f0 100644
--- a/tests/auto/corelib/io/qfile/tst_qfile.cpp
+++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp
@@ -228,6 +228,7 @@ private slots:
void getCharFF();
void remove_and_exists();
void removeOpenFile();
+ void removedFileDoesntExist();
void fullDisk();
void writeLargeDataBlock_data();
void writeLargeDataBlock();
@@ -2526,6 +2527,37 @@ void tst_QFile::removeOpenFile()
}
}
+void tst_QFile::removedFileDoesntExist()
+{
+#ifdef Q_OS_WIN
+ QSKIP("Not relevant for Windows - can't remove still-open files");
+#endif
+ QFile::remove("remove_unclosed.txt");
+ QFile f("remove_unclosed.txt");
+ QVERIFY(!f.exists());
+ bool opened = f.open(QIODevice::ReadWrite | QIODevice::Unbuffered);
+ QVERIFY(opened);
+ f.write("blah blah blah");
+
+ QVERIFY(f.exists());
+
+ // delete by path, not using f.remove() (that's tested above)
+ QVERIFY(QFile::remove(f.fileName()));
+ QVERIFY(!QFile::exists(f.fileName()));
+ QVERIFY(!f.exists());
+
+#ifdef Q_OS_LINUX
+ QString procPath = u"/proc/self/fd/"_s;
+ if (QFile::exists(procPath)) {
+ // reopen the deleted file
+ procPath += QString::number(f.handle());
+ QFile f2(procPath);
+ QVERIFY(f2.open(QIODevice::ReadOnly));
+ QVERIFY(!f2.exists());
+ }
+#endif
+}
+
void tst_QFile::fullDisk()
{
QFile file("/dev/full");
diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
index 761f0552eec..e2190477441 100644
--- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
+++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
@@ -141,6 +141,7 @@ private slots:
void exists_data();
void exists();
+ void deletedFileLinuxProcExists();
void absolutePath_data();
void absolutePath();
@@ -615,6 +616,34 @@ void tst_QFileInfo::exists()
QVERIFY(!exists);
}
+void tst_QFileInfo::deletedFileLinuxProcExists()
+{
+#ifdef Q_OS_LINUX
+ static const char msg[] = "Hello, World\n";
+ QFileInfo fi("/proc/self/fd/");
+ if (!fi.isDir())
+ QSKIP("/proc appears not to be mounted");
+
+ QFile f("removed_file.txt");
+ QVERIFY(f.open(QIODevice::ReadWrite | QIODevice::Unbuffered));
+ f.write(msg, strlen(msg));
+
+ fi.setFile(fi.filePath() + QString::number(f.handle()));
+ QVERIFY(fi.exists());
+ QCOMPARE(fi.size(), strlen(msg));
+
+ QFile::remove("removed_file.txt");
+ fi.refresh();
+ QVERIFY(fi.exists());
+
+ fi.refresh();
+ QCOMPARE(fi.size(), strlen(msg)); // this stats, so may change flags
+ QVERIFY(fi.exists());
+#else
+ QSKIP("Linux-only test");
+#endif
+}
+
void tst_QFileInfo::absolutePath_data()
{
QTest::addColumn<QString>("file");
diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp
index 282c7f2e360..71389abc976 100644
--- a/tests/auto/corelib/io/qurl/tst_qurl.cpp
+++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp
@@ -513,6 +513,7 @@ void tst_QUrl::comparison2()
QCOMPARE(url1.toString(), url2.toString());
QCOMPARE(url1, url2);
QCOMPARE(qHash(url1), qHash(url2));
+ QCOMPARE(qHash(url1, 1), qHash(url2, 1));
} else if (ordering < 0) {
QCOMPARE_LT(url1.toString(), url2.toString());
QCOMPARE_NE(url1, url2);
@@ -1387,12 +1388,14 @@ void tst_QUrl::toString_constructed()
QUrl parsed(asString);
QCOMPARE(url, parsed);
QCOMPARE(qHash(url), qHash(parsed));
+ QCOMPARE(qHash(url, 1), qHash(parsed, 1));
}
// clear it and ensure no memory of the previous state remains
url.setUrl(QString());
QCOMPARE(url, QUrl());
QCOMPARE(qHash(url), qHash(QUrl()));
+ QCOMPARE(qHash(url, 1), qHash(QUrl(), 1));
}
void tst_QUrl::toDisplayString_PreferLocalFile_data()
@@ -4265,6 +4268,7 @@ void tst_QUrl::setComponents()
QUrl recreated(toString);
QCOMPARE(copy, recreated);
QCOMPARE(qHash(copy), qHash(recreated));
+ QCOMPARE(qHash(copy, 1), qHash(recreated, 1));
} else {
QVERIFY(copy.toString().isEmpty());
}
diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
index b1e2e8164af..a5f23014b94 100644
--- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
+++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
@@ -708,6 +708,24 @@ void tst_QMetaObjectBuilder::property()
prop2.setEnumOrFlag(false); \
prop2.setConstant(false); \
prop2.setFinal(false); \
+ prop2.setBindable(false); \
+ prop2.setRequired(false); \
+ } while (0)
+#define SET_ALL_FLAGS() \
+ do { \
+ prop2.setReadable(true); \
+ prop2.setWritable(true); \
+ prop2.setResettable(true); \
+ prop2.setDesignable(true); \
+ prop2.setScriptable(true); \
+ prop2.setStored(true); \
+ prop2.setUser(true); \
+ prop2.setStdCppSet(true); \
+ prop2.setEnumOrFlag(true); \
+ prop2.setConstant(true); \
+ prop2.setFinal(true); \
+ prop2.setBindable(true); \
+ prop2.setRequired(true); \
} while (0)
#define COUNT_FLAGS() \
((prop2.isReadable() ? 1 : 0) + \
@@ -720,15 +738,19 @@ void tst_QMetaObjectBuilder::property()
(prop2.hasStdCppSet() ? 1 : 0) + \
(prop2.isEnumOrFlag() ? 1 : 0) + \
(prop2.isConstant() ? 1 : 0) + \
- (prop2.isFinal() ? 1 : 0))
+ (prop2.isFinal() ? 1 : 0) + \
+ (prop2.isBindable() ? 1 : 0) + \
+ (prop2.isRequired() ? 1 : 0))
#define CHECK_FLAG(setFunc,isFunc) \
do { \
+ ++flagCounter; \
CLEAR_FLAGS(); \
QCOMPARE(COUNT_FLAGS(), 0); \
prop2.setFunc(true); \
QVERIFY(prop2.isFunc()); \
QCOMPARE(COUNT_FLAGS(), 1); \
} while (0)
+ int flagCounter = 0;
CHECK_FLAG(setReadable, isReadable);
CHECK_FLAG(setWritable, isWritable);
CHECK_FLAG(setResettable, isResettable);
@@ -739,7 +761,11 @@ void tst_QMetaObjectBuilder::property()
CHECK_FLAG(setStdCppSet, hasStdCppSet);
CHECK_FLAG(setEnumOrFlag, isEnumOrFlag);
CHECK_FLAG(setConstant, isConstant);
+ CHECK_FLAG(setBindable, isBindable);
CHECK_FLAG(setFinal, isFinal);
+ CHECK_FLAG(setRequired, isRequired);
+ SET_ALL_FLAGS();
+ QCOMPARE(COUNT_FLAGS(), flagCounter);
// Check that nothing else changed.
QVERIFY(checkForSideEffects(builder, QMetaObjectBuilder::Properties));
diff --git a/tests/auto/corelib/platform/CMakeLists.txt b/tests/auto/corelib/platform/CMakeLists.txt
index 3a66ec2eae6..9810947e3c6 100644
--- a/tests/auto/corelib/platform/CMakeLists.txt
+++ b/tests/auto/corelib/platform/CMakeLists.txt
@@ -5,7 +5,9 @@ if(ANDROID)
add_subdirectory(android)
add_subdirectory(android_appless)
add_subdirectory(androiditemmodel)
- add_subdirectory(android_legacy_packaging)
+ if(NOT QT_USE_ANDROID_MODERN_BUNDLE)
+ add_subdirectory(android_legacy_packaging)
+ endif()
endif()
if(WIN32)
add_subdirectory(windows)
diff --git a/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle b/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle
index cbdd833e2ae..1d796d9b6b9 100644
--- a/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle
+++ b/tests/auto/corelib/platform/android_legacy_packaging/testdata/build.gradle
@@ -70,8 +70,8 @@ android {
abortOnError = false
}
- // Do not compress Qt binary resources file
aaptOptions {
+ // Do not compress Qt binary resources file
noCompress 'rcc'
}
diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp
index 3756937c94a..bfc6074689b 100644
--- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp
+++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp
@@ -2470,7 +2470,8 @@ void tst_QLocale::formatTimeZone()
// Time definitely in Standard Time
const QStringList knownCETus = {
u"GMT+1"_s, // ICU
- u"CET"_s // Standard abbreviation
+ u"CET"_s, // Standard abbreviation
+ u"UTC+0100"_s, // used by Emscripten
};
const QString cet = enUS.toString(QDate(2013, 1, 1).startOfDay(), u"t");
QVERIFY2(knownCETus.contains(cet), cet.isEmpty() ? "[empty]" : qPrintable(cet));
@@ -2478,7 +2479,8 @@ void tst_QLocale::formatTimeZone()
// Time definitely in Daylight Time
const QStringList knownCESTus = {
u"GMT+2"_s, // ICU
- u"CEST"_s // Standard abbreviation
+ u"CEST"_s, // Standard abbreviation
+ u"UTC+0200"_s, // used by Emscripten
};
const QString cest = enUS.toString(QDate(2013, 6, 1).startOfDay(), u"t");
QVERIFY2(knownCESTus.contains(cest), cest.isEmpty() ? "[empty]" : qPrintable(cest));
diff --git a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp
index 89bd1d7ff61..b53362b43e9 100644
--- a/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp
+++ b/tests/auto/corelib/tools/qatomicscopedvaluerollback/tst_qatomicscopedvaluerollback.cpp
@@ -5,6 +5,10 @@
#include <QTest>
+#include <chrono>
+
+using namespace std::chrono_literals;
+
class tst_QAtomicScopedValueRollback : public QObject
{
Q_OBJECT
@@ -15,6 +19,7 @@ private Q_SLOTS:
void rollbackToPreviousCommit();
void exceptions();
void earlyExitScope();
+ void mixedTypes();
private:
void earlyExitScope_helper(int exitpoint, std::atomic<int> &member);
};
@@ -132,6 +137,90 @@ void tst_QAtomicScopedValueRollback::earlyExitScope()
}
}
+template <typename T>
+struct Wrap {
+#if __cpp_deduction_guides < 201907L // no CTAD for aggregates
+ Q_IMPLICIT Wrap(T &t) : t{t} {}
+ Q_IMPLICIT Wrap(T &&t) : t{std::move(t)} {}
+#endif
+ T t;
+ Q_IMPLICIT operator T() const { return t; }
+};
+
+void tst_QAtomicScopedValueRollback::mixedTypes()
+{
+ const auto relaxed = std::memory_order_relaxed;
+ {
+ std::atomic a{10'000ms};
+ {
+ QAtomicScopedValueRollback rb(a, 5s);
+ QCOMPARE(a.load(relaxed), 5s);
+ }
+ QCOMPARE(a.load(relaxed), 10s);
+ {
+ QAtomicScopedValueRollback rb(a, 5s, relaxed);
+ QCOMPARE(a.load(relaxed), 5s);
+ }
+ QCOMPARE(a.load(relaxed), 10s);
+ }
+ {
+ QBasicAtomicInteger a{10'000};
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{5'000});
+ QCOMPARE(a.loadRelaxed(), 5'000);
+ }
+ QCOMPARE(a.loadRelaxed(), 10'000);
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{5'000}, relaxed);
+ QCOMPARE(a.loadRelaxed(), 5'000);
+ }
+ QCOMPARE(a.loadRelaxed(), 10'000);
+ }
+ {
+ QAtomicInteger a{10'000};
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{5'000});
+ QCOMPARE(a.loadRelaxed(), 5'000);
+ }
+ QCOMPARE(a.loadRelaxed(), 10'000);
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{5'000}, relaxed);
+ QCOMPARE(a.loadRelaxed(), 5'000);
+ }
+ QCOMPARE(a.loadRelaxed(), 10'000);
+ }
+ {
+ int i = 10'000;
+ int j = 5'000;
+ QBasicAtomicPointer a{&i};
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{&j});
+ QCOMPARE(a.loadRelaxed(), &j);
+ }
+ QCOMPARE(a.loadRelaxed(), &i);
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{&j}, relaxed);
+ QCOMPARE(a.loadRelaxed(), &j);
+ }
+ QCOMPARE(a.loadRelaxed(), &i);
+ }
+ {
+ int i = 10'000;
+ int j = 5'000;
+ QAtomicPointer a{&i};
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{&j});
+ QCOMPARE(a.loadRelaxed(), &j);
+ }
+ QCOMPARE(a.loadRelaxed(), &i);
+ {
+ QAtomicScopedValueRollback rb(a, Wrap{&j}, relaxed);
+ QCOMPARE(a.loadRelaxed(), &j);
+ }
+ QCOMPARE(a.loadRelaxed(), &i);
+ }
+}
+
static void operator*=(std::atomic<int> &lhs, int rhs)
{
int expected = lhs.load();
diff --git a/tests/auto/gui/image/qicoimageformat/tst_qicoimageformat.cpp b/tests/auto/gui/image/qicoimageformat/tst_qicoimageformat.cpp
index 36cf8ac8fd8..18fe43fbcea 100644
--- a/tests/auto/gui/image/qicoimageformat/tst_qicoimageformat.cpp
+++ b/tests/auto/gui/image/qicoimageformat/tst_qicoimageformat.cpp
@@ -35,6 +35,8 @@ private slots:
void write();
void icoMask_data();
void icoMask();
+ void origBitDepth_data();
+ void origBitDepth();
private:
QString m_IconPath;
@@ -354,6 +356,40 @@ void tst_QIcoImageFormat::icoMask()
QCOMPARE(inImage, outImage);
}
+void tst_QIcoImageFormat::origBitDepth_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QList<int>>("origBitDepths");
+
+ QTest::newRow("35FLOPPY") << "35FLOPPY.ICO" << QList<int>{4};
+ QTest::newRow("abcardWindow") << "abcardWindow.ico" << QList<int>{8};
+ QTest::newRow("AddPerf") << "AddPerfMon.ico" << QList<int>{4};
+ QTest::newRow("App") << "App.ico" << QList<int>{4};
+ QTest::newRow("Obj_N2_Internal_Mem") << "Obj_N2_Internal_Mem.ico" << QList<int>{4, 8, 32};
+ QTest::newRow("Qt") << "Qt.ico" << QList<int>{32};
+ QTest::newRow("semitransparent") << "semitransparent.ico" << QList<int>{4};
+ QTest::newRow("Status_Play") << "Status_Play.ico" << QList<int>{4, 8, 32};
+ QTest::newRow("TIMER01") << "TIMER01.ICO" << QList<int>{4};
+ QTest::newRow("trolltechlogo_tiny") << "trolltechlogo_tiny.ico" << QList<int>{8};
+ QTest::newRow("WORLD") << "WORLD.ico" << QList<int>{8, 4, 4};
+ QTest::newRow("yellow") << "yellow.cur" << QList<int>{32};
+}
+
+void tst_QIcoImageFormat::origBitDepth()
+{
+ QFETCH(QString, file);
+ QFETCH(QList<int>, origBitDepths);
+
+ QImage image;
+ QImageReader reader(m_IconPath + QLatin1String("/valid/") + file);
+
+ for (int depth : origBitDepths) {
+ reader.read(&image);
+ QCOMPARE(image.text("_q_icoOrigDepth").toInt(), depth);
+ reader.jumpToNextImage();
+ }
+}
+
QTEST_MAIN(tst_QIcoImageFormat)
#include "tst_qicoimageformat.moc"
diff --git a/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp b/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
index 6019261f792..06a1ffb2960 100644
--- a/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
+++ b/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp
@@ -26,9 +26,6 @@ private slots:
void tst_QOpenGLWindow::initTestCase()
{
-#if !QT_CONFIG(run_opengl_tests)
- QSKIP("Skip test as run-opengl-tests feature is off.");
-#endif
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
QSKIP("OpenGL is not supported on this platform.");
}
diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST
index a92d89c6123..4f994fb3059 100644
--- a/tests/auto/gui/kernel/qwindow/BLACKLIST
+++ b/tests/auto/gui/kernel/qwindow/BLACKLIST
@@ -25,6 +25,5 @@ android
windows-10
windows-11
android
-rhel
[windowExposedAfterReparent]
xcb ubuntu-24.04 # QTBUG-129023
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
index 1c8dd14eba4..66bb0fa84d8 100644
--- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
@@ -11,6 +11,7 @@
#include <QtGui/QPainter>
#include <QTest>
+#include <QtTest/private/qtesthelpers_p.h>
#include <QSignalSpy>
#include <QEvent>
#include <QStyleHints>
@@ -1237,6 +1238,7 @@ void tst_QWindow::testInputEvents()
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
window.showNormal();
QTRY_VERIFY(window.isActive());
+ QVERIFY(QTestPrivate::ensurePositionTopLeft(&window));
QTest::keyClick(&window, Qt::Key_A, Qt::NoModifier);
QCoreApplication::processEvents();
diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp
index b9b4c3aa11c..36ff1d1f2e9 100644
--- a/tests/auto/gui/qopengl/tst_qopengl.cpp
+++ b/tests/auto/gui/qopengl/tst_qopengl.cpp
@@ -185,9 +185,6 @@ static QSurface *createSurface(int surfaceClass)
void tst_QOpenGL::initTestCase()
{
-#if !QT_CONFIG(run_opengl_tests)
- QSKIP("Skip test as run-opengl-tests feature is off.");
-#endif
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
QSKIP("OpenGL is not supported on this platform.");
}
diff --git a/tests/auto/gui/qopenglconfig/tst_qopenglconfig.cpp b/tests/auto/gui/qopenglconfig/tst_qopenglconfig.cpp
index f597dac53d7..423f9419daf 100644
--- a/tests/auto/gui/qopenglconfig/tst_qopenglconfig.cpp
+++ b/tests/auto/gui/qopenglconfig/tst_qopenglconfig.cpp
@@ -139,9 +139,6 @@ static void dumpConfiguration(QTextStream &str)
void tst_QOpenGlConfig::initTestCase()
{
-#if !QT_CONFIG(run_opengl_tests)
- QSKIP("Skip test as run-opengl-tests feature is off.");
-#endif
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
QSKIP("OpenGL is not supported on this platform.");
}
diff --git a/tests/auto/gui/text/qfont/BLACKLIST b/tests/auto/gui/text/qfont/BLACKLIST
index 2d2440255ae..3f63b678bb5 100644
--- a/tests/auto/gui/text/qfont/BLACKLIST
+++ b/tests/auto/gui/text/qfont/BLACKLIST
@@ -1,12 +1,9 @@
[defaultFamily:cursive]
centos
b2qt
-rhel
[defaultFamily:fantasy]
centos
b2qt
-rhel
-
# QTBUG-130738
[familyNameWithCommaQuote:weird]
vxworks
diff --git a/tests/auto/network/kernel/qnetworkinterface/BLACKLIST b/tests/auto/network/kernel/qnetworkinterface/BLACKLIST
deleted file mode 100644
index e9f4678e8ef..00000000000
--- a/tests/auto/network/kernel/qnetworkinterface/BLACKLIST
+++ /dev/null
@@ -1,5 +0,0 @@
-# QTBUG-130070
-[localAddress:enet0-fe80::8f41:f072:e573:3caa%enet0]
-vxworks
-[localAddress:localhost-ipv6]
-vxworks
diff --git a/tests/auto/other/android/CMakeLists.txt b/tests/auto/other/android/CMakeLists.txt
index bcbf5b657d7..0eb2d00b46a 100644
--- a/tests/auto/other/android/CMakeLists.txt
+++ b/tests/auto/other/android/CMakeLists.txt
@@ -2,3 +2,11 @@
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(deployment_settings)
+add_subdirectory(permissions)
+
+if(QT_USE_TARGET_ANDROID_BUILD_DIR)
+ add_subdirectory(package_source_dir)
+ if(QT_USE_ANDROID_MODERN_BUNDLE)
+ add_subdirectory(dynamic_feature)
+ endif()
+endif()
diff --git a/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp b/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp
index 1687ed9de92..571570e370f 100644
--- a/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp
+++ b/tests/auto/other/android/deployment_settings/tst_android_deployment_settings.cpp
@@ -83,10 +83,11 @@ void tst_android_deployment_settings::DeploymentSettings_data()
<< "org.qtproject.android_deployment_settings_test";
QTest::newRow("android-app-name") << "android-app-name"
<< "Android Deployment Settings Test";
- QTest::newRow("permissions") << "permissions"
- << "[{\"name\":\"PERMISSION_WITH_ATTRIBUTES\","
- "\"extras\":\"android:minSdkVersion='32' android:maxSdkVersion='34' \"},"
- "{\"name\":\"PERMISSION_WITHOUT_ATTRIBUTES\"}]";
+ QTest::newRow("permissions")
+ << "permissions"
+ << "[{\"maxSdkVersion\":\"34\",\"minSdkVersion\":\"32\",\"name\":\"PERMISSION_WITH_"
+ "ATTRIBUTES\"},{\"name\":\"PERMISSION_WITHOUT_ATTRIBUTES\"},{\"name\":\"android."
+ "permission.INTERNET\"},{\"name\":\"android.permission.WRITE_EXTERNAL_STORAGE\"}]";
}
void tst_android_deployment_settings::DeploymentSettings()
diff --git a/tests/auto/other/android/dynamic_feature/CMakeLists.txt b/tests/auto/other/android/dynamic_feature/CMakeLists.txt
new file mode 100644
index 00000000000..b12c2af4dc7
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_android_dynamic_feature LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+add_subdirectory(feature)
+
+qt_internal_add_test(tst_android_dynamic_feature
+ SOURCES
+ tst_android_dynamic_feature.cpp
+ storeloader/storeloader.h
+ storeloader/storeloader.cpp
+ INCLUDE_DIRECTORIES
+ storeloader
+ LIBRARIES
+ Qt6::Test
+ Qt6::Gui
+ Qt6::CorePrivate
+)
+
+set_property(TARGET tst_android_dynamic_feature PROPERTY
+ QT_ANDROID_PACKAGE_NAME "org.qtproject.example.android_dynamic_feature")
+set_property(TARGET tst_android_dynamic_feature APPEND PROPERTY
+ _qt_android_gradle_java_source_dirs "${CMAKE_CURRENT_SOURCE_DIR}/storeloader/java")
+
+qt6_add_android_dynamic_features(tst_android_dynamic_feature FEATURE_TARGETS
+ tst_android_dynamic_feature_resources)
diff --git a/tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt b/tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt
new file mode 100644
index 00000000000..bfe29cc0f5e
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/feature/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.16)
+
+qt6_add_library(tst_android_dynamic_feature_resources SHARED)
+qt6_add_resources(tst_android_dynamic_feature_resources "dynamic_resources"
+ PREFIX "/dynamic_resources"
+ FILES "qtlogo.png")
+
+target_link_libraries(tst_android_dynamic_feature_resources PRIVATE Qt6::Core)
diff --git a/tests/auto/other/android/dynamic_feature/feature/qtlogo.png b/tests/auto/other/android/dynamic_feature/feature/qtlogo.png
new file mode 100644
index 00000000000..b63f1384b11
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/feature/qtlogo.png
Binary files differ
diff --git a/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java
new file mode 100644
index 00000000000..5a72351c802
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoader.java
@@ -0,0 +1,175 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+package org.qtproject.example.android_dynamic_feature;
+
+import com.google.android.play.core.splitcompat.SplitCompat;
+import com.google.android.play.core.splitinstall.SplitInstallManager;
+import com.google.android.play.core.splitinstall.SplitInstallManagerFactory;
+import com.google.android.play.core.splitinstall.SplitInstallRequest;
+import com.google.android.play.core.splitinstall.SplitInstallHelper;
+import com.google.android.play.core.splitinstall.testing.FakeSplitInstallManagerFactory;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.io.File;
+import android.util.Log;
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * Android implementation of store loader.
+ */
+public class StoreLoader implements StoreLoaderListenerCallback
+{
+ private static final String TAG = "StoreLoader";
+ private final SplitInstallManager m_splitInstallManager;
+ private HashMap<String, StoreLoaderListener> m_listeners =
+ new HashMap();
+ private Context m_context;
+ private String m_moduleName;
+
+ public static native void stateChangedNative(String callId, int state);
+ public static native void errorOccurredNative(String callId, int errorCode,
+ String errorMessage);
+ public static native void userConfirmationRequestedNative(String callId, int errorCode,
+ String errorMessage);
+ public static native void downloadProgressChangedNative(String callId, long bytes, long total);
+ public static native void finishedNative(String callId);
+
+ public StoreLoader(Context context) {
+ m_context = context;
+
+ SplitCompat.install(context);
+
+ m_splitInstallManager = SplitInstallManagerFactory.create(context);
+ if (m_splitInstallManager == null)
+ Log.e(TAG,"Constructor: Did not get splitInstallManager");
+ }
+
+ /**
+ * Loads Feature Delivery module from a play store.
+ */
+ public void installModuleFromStore(String moduleName, String callId) {
+ Log.d(TAG, "installModuleFromStore: " + moduleName + " " + callId);
+ m_moduleName = moduleName;
+
+ registerListener(callId);
+
+ SplitInstallRequest request =
+ SplitInstallRequest.newBuilder().addModule(m_moduleName).build();
+ if (request == null)
+ Log.e(TAG, "Null request");
+
+ if (m_splitInstallManager == null)
+ Log.e(TAG, "Null m_splitInstallManager");
+
+ m_splitInstallManager.startInstall(request)
+ .addOnSuccessListener(sessionId -> {
+ Log.d(TAG, "Install start call succesfull");
+ StoreLoaderListener listener = m_listeners.get(callId);
+ if (listener != null) {
+ if (!listener.isCancelled())
+ listener.setSessionId(sessionId);
+ else
+ m_splitInstallManager.cancelInstall(sessionId);
+ } else {
+ Log.d(TAG, "Listener for callId '" + callId + "' is not found");
+ }
+ });
+ }
+
+ public void onStateChanged(String callId, int state) {
+ stateChangedNative(callId, state);
+ if (state == StoreLoaderListenerCallback.LOADED ||
+ state == StoreLoaderListenerCallback.CANCELED) {
+ finished(callId);
+ }
+ }
+
+ public void onErrorOccurred(String callId, int errorCode, String errorMessage) {
+ onStateChanged(callId, ERROR);
+
+ Log.e(TAG, "Error occurred " + errorCode + " " + errorMessage);
+ errorOccurredNative(callId, errorCode, errorMessage);
+ finished(callId);
+ }
+
+ public void onUserConfirmationRequested(String callId, int errorCode, String errorMessage) {
+ Log.d(TAG, "Requires user confirmation " + errorCode);
+ userConfirmationRequestedNative(callId, errorCode, errorMessage);
+ }
+
+ public void onDownloadProgressChanged(String callId, long bytes, long total) {
+ Log.d(TAG, "Downloading " + bytes + "/" + total);
+ downloadProgressChangedNative(callId, bytes, total);
+ }
+
+ public void onLoadLibrary(String callId) {
+ Log.d(TAG, "Load library for the module " + m_moduleName);
+
+ stateChangedNative(callId, StoreLoaderListenerCallback.LOADING);
+ // update context.
+ try {
+ m_context = m_context.createPackageContext(m_context.getPackageName(), 0);
+ } catch (NameNotFoundException ignored) {
+ Log.e(TAG, "Could not get package name");
+ }
+ // install splitcompat for new context.
+ SplitCompat.install(m_context);
+ // try to load new library
+ boolean isLoaded = false;
+ for (String abi : Build.SUPPORTED_ABIS) {
+ String fullLibraryName = m_moduleName + "_" + abi;
+ try {
+ System.loadLibrary(fullLibraryName);
+ isLoaded = true;
+ break;
+ } catch (Exception e) {
+ Log.d(TAG, "Exception occurred when loading the library " + fullLibraryName + ":" +
+ e.getClass().getCanonicalName());
+ }
+ }
+
+ if (isLoaded)
+ onStateChanged(callId, StoreLoaderListenerCallback.LOADED);
+ else
+ onErrorOccurred(callId, -1, "Error loading library. Check logcat for details.");
+
+ }
+
+ public void cancelInstall(String callId) {
+ StoreLoaderListener listener = m_listeners.get(callId);
+ if (listener == null) {
+ Log.e(TAG, "The listener for callId " + callId + " is not found");
+ return;
+ }
+
+ if (listener.getSessionId() < 0)
+ listener.postponeCancel();
+ else
+ m_splitInstallManager.cancelInstall(listener.getSessionId());
+ }
+
+ private void finished(String callId) {
+ unregisterListener(callId);
+ finishedNative(callId);
+ }
+
+ private void registerListener(String callId) {
+ Log.d(TAG, "registerListener");
+ StoreLoaderListener listener = new StoreLoaderListener(this, callId);
+ if (listener != null) {
+ m_listeners.put(callId, listener);
+ m_splitInstallManager.registerListener(listener);
+ }
+ }
+
+ private void unregisterListener(String callId) {
+ Log.d(TAG, "unregisterListener");
+ StoreLoaderListener listener = m_listeners.remove(callId);
+ if (listener != null)
+ m_splitInstallManager.unregisterListener(listener);
+ }
+}
diff --git a/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java
new file mode 100644
index 00000000000..3e83a0fa7f9
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListener.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+package org.qtproject.example.android_dynamic_feature;
+
+import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener;
+import com.google.android.play.core.splitinstall.SplitInstallSessionState;
+import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
+
+import android.util.Log;
+
+public class StoreLoaderListener implements SplitInstallStateUpdatedListener {
+
+ private final StoreLoaderListenerCallback m_callback;
+ private final String m_callId;
+ private int m_installSessionId = -1;
+ private static final String TAG = "StoreLoaderListener";
+ private int m_currentStatus = SplitInstallSessionStatus.UNKNOWN;
+ private boolean m_postponeCancel = false;
+
+ public StoreLoaderListener(StoreLoaderListenerCallback callback, String callId) {
+ m_callback = callback;
+ m_callId = callId;
+ }
+
+ public void setSessionId(int sessionId) { m_installSessionId = sessionId; }
+ public int getSessionId() { return m_installSessionId; }
+
+ public void postponeCancel() { m_postponeCancel = true; }
+ public boolean isCancelled() { return m_postponeCancel; }
+
+ @Override
+ public void onStateUpdate(SplitInstallSessionState state) {
+ Log.d(TAG,
+ "onStateUpdate, status: " + state.status() + " session id: " + state.sessionId());
+ if (state.sessionId() != m_installSessionId) {
+ // Not mine
+ return;
+ }
+
+ switch (state.status()) {
+ case SplitInstallSessionStatus.DOWNLOADING:
+ Log.d(TAG,
+ "SplitInstallSessionState: DOWNLOADING " + state.bytesDownloaded() + "/"
+ + state.totalBytesToDownload());
+ if (m_currentStatus != SplitInstallSessionStatus.DOWNLOADING)
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.DOWNLOADING);
+
+ m_callback.onDownloadProgressChanged(m_callId, state.bytesDownloaded(),
+ state.totalBytesToDownload());
+ break;
+ case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION:
+ Log.d(TAG, "SplitInstallSessionState: REQUIRES_USER_CONFIRMATION");
+ m_callback.onStateChanged(m_callId,
+ StoreLoaderListenerCallback.REQUIRES_USER_CONFIRMATION);
+
+ m_callback.onUserConfirmationRequested(m_callId, state.errorCode(), "" /*noop for now*/);
+ break;
+ case SplitInstallSessionStatus.INSTALLED:
+ Log.d(TAG, "SplitInstallSessionState: INSTALLED");
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.INSTALLED);
+ m_callback.onLoadLibrary(m_callId);
+ break;
+ case SplitInstallSessionStatus.INSTALLING:
+ Log.d(TAG, "SplitInstallSessionState: INSTALLING");
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.INSTALLING);
+ break;
+ case SplitInstallSessionStatus.FAILED:
+ Log.d(TAG,
+ "SplitInstallSessionState: FAILED with error code: " + state.errorCode());
+ m_callback.onErrorOccurred(m_callId, state.errorCode(), "" /*noop for now*/);
+ break;
+ case SplitInstallSessionStatus.PENDING:
+ Log.d(TAG, "SplitInstallSessionState: PENDING");
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.PENDING);
+ break;
+ case SplitInstallSessionStatus.DOWNLOADED:
+ Log.d(TAG, "SplitInstallSessionState: DOWNLOADED");
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.DOWNLOADED);
+ break;
+ case SplitInstallSessionStatus.CANCELED:
+ Log.d(TAG, "SplitInstallSessionState: CANCELED");
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.CANCELED);
+ break;
+ case SplitInstallSessionStatus.CANCELING:
+ Log.d(TAG, "SplitInstallSessionState: CANCELING");
+ m_callback.onStateChanged(m_callId, StoreLoaderListenerCallback.CANCELING);
+ break;
+ }
+ m_currentStatus = state.status();
+ }
+}
diff --git a/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java
new file mode 100644
index 00000000000..99c7327e328
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/storeloader/java/src/main/java/org/qtproject/example/android_dynamic_feature/StoreLoaderListenerCallback.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+package org.qtproject.example.android_dynamic_feature;
+
+public interface StoreLoaderListenerCallback {
+ public static final int UNKNOWN = 0; // Unused but native part has and uses one.
+ public static final int INITIALIZED = 1;
+ public static final int PENDING = 2;
+ public static final int DOWNLOADING = 3;
+ public static final int DOWNLOADED = 4;
+ public static final int REQUIRES_USER_CONFIRMATION = 5;
+ public static final int CANCELING = 6;
+ public static final int CANCELED = 7;
+ public static final int INSTALLING = 8;
+ public static final int INSTALLED = 9;
+ public static final int LOADING = 10;
+ public static final int LOADED = 11;
+ public static final int ERROR = 12;
+
+ public void onStateChanged(String callId, int state);
+ public void onErrorOccurred(String callId, int errorCode, String errorMessage);
+ public void onUserConfirmationRequested(String callId, int errorCode, String errorMessage);
+ public void onDownloadProgressChanged(String callId, long bytes, long total);
+ public void onLoadLibrary(String callId);
+}
diff --git a/tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp
new file mode 100644
index 00000000000..7abce59b79e
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.cpp
@@ -0,0 +1,227 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "storeloader.h"
+
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qlogging.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/quuid.h>
+
+#include <jni.h>
+
+Q_DECLARE_JNI_CLASS(StoreLoader,
+ "org/qtproject/example/android_dynamic_feature/StoreLoader");
+
+class StoreLoaderHandlerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(StoreLoaderHandler)
+public:
+ void setState(StoreLoader::State state)
+ {
+ if (m_state == state)
+ return;
+ Q_Q(StoreLoaderHandler);
+ m_state = state;
+ q->stateChanged(m_state);
+ }
+
+ const QString &callId() const & noexcept { return m_callId; }
+
+private:
+ StoreLoader::State m_state = StoreLoader::State::Unknown;
+ QString m_callId = QUuid::createUuid().toString();
+};
+
+namespace QtPrivate {
+QString asString(const jstring &s)
+{
+ return QJniObject(s).toString();
+}
+} // namespace QtPrivate
+
+namespace {
+
+class StoreLoaderImpl
+{
+public:
+ StoreLoaderImpl();
+ bool registerNatives() const;
+
+ void addHandler(StoreLoaderHandler *handler);
+ StoreLoaderHandler *findHandler(const jstring &callId);
+ void removeHandler(const jstring &callId);
+
+ QtJniTypes::StoreLoader loader = nullptr;
+
+private:
+ QHash<QString, QPointer<StoreLoaderHandler>> m_handlers;
+ QMutex m_lock;
+};
+
+Q_GLOBAL_STATIC(StoreLoaderImpl, loaderInstance)
+
+void stateChangedNative(JNIEnv *, jobject, jstring callId, int state)
+{
+ qDebug("State changed %s.", qPrintable(callId));
+
+ if (auto *handler = loaderInstance->findHandler(callId))
+ emit handler->stateChanged(static_cast<StoreLoader::State>(state));
+}
+
+void errorOccurredNative(JNIEnv *, jobject, jstring callId, int errorCode, jstring errorMessage)
+{
+ qDebug("Error occurred %s %d %s.", qPrintable(callId), errorCode, qPrintable(errorMessage));
+ auto *handler = loaderInstance->findHandler(callId);
+ if (!handler)
+ return;
+
+ emit handler->errorOccured(errorCode, QJniObject(errorMessage).toString());
+}
+
+void userConfirmationRequestedNative(JNIEnv *, jobject, jstring callId, int errorCode,
+ jstring errorMessage)
+{
+ qDebug("User confirmation requested %s %d %s.", qPrintable(callId), errorCode,
+ qPrintable(errorMessage));
+ auto *handler = loaderInstance->findHandler(callId);
+ if (!handler)
+ return;
+
+ emit handler->confirmationRequest(errorCode, QJniObject(errorMessage).toString());
+}
+
+void downloadProgressChangedNative(JNIEnv *, jobject, jstring callId, long bytes, long total)
+{
+ qDebug("Download progress changed %ld/%ld.", bytes, total);
+ auto *handler = loaderInstance->findHandler(callId);
+ if (!handler)
+ return;
+
+ emit handler->downloadProgress(bytes, total);
+}
+
+void finishedNative(JNIEnv *, jobject, jstring callId)
+{
+ auto *handler = loaderInstance->findHandler(callId);
+ if (!handler)
+ return;
+
+ emit handler->finished();
+}
+
+} // namespace
+
+Q_DECLARE_JNI_NATIVE_METHOD(stateChangedNative)
+Q_DECLARE_JNI_NATIVE_METHOD(errorOccurredNative)
+Q_DECLARE_JNI_NATIVE_METHOD(userConfirmationRequestedNative)
+Q_DECLARE_JNI_NATIVE_METHOD(downloadProgressChangedNative)
+Q_DECLARE_JNI_NATIVE_METHOD(finishedNative)
+
+StoreLoaderImpl::StoreLoaderImpl()
+{
+ loader = QJniObject::construct<QtJniTypes::StoreLoader, QtJniTypes::Context>(
+ QNativeInterface::QAndroidApplication::context());
+}
+
+bool StoreLoaderImpl::registerNatives() const
+{
+ static bool result = [] {
+ return QtJniTypes::StoreLoader::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(stateChangedNative),
+ Q_JNI_NATIVE_METHOD(errorOccurredNative),
+ Q_JNI_NATIVE_METHOD(userConfirmationRequestedNative),
+ Q_JNI_NATIVE_METHOD(downloadProgressChangedNative),
+ Q_JNI_NATIVE_METHOD(finishedNative),
+ });
+ }();
+
+ if (!result)
+ qCritical("Unable to register native methods.");
+
+ return result;
+}
+
+void StoreLoaderImpl::addHandler(StoreLoaderHandler *handler)
+{
+ Q_ASSERT(handler);
+
+ QMutexLocker lock(&m_lock);
+ const auto &callId = handler->callId();
+ Q_ASSERT_X(m_handlers.constFind(callId) != m_handlers.constEnd(), "StoreLoaderImpl::addHandler",
+ qPrintable(QString("Handler with callId %1 already exists.").arg(callId)));
+
+ m_handlers[callId] = QPointer(handler);
+}
+
+
+StoreLoaderHandler *StoreLoaderImpl::findHandler(const jstring &callId)
+{
+ QMutexLocker lock(&m_lock);
+ const auto it = m_handlers.constFind(QJniObject(callId).toString());
+ if (it == m_handlers.constEnd()) {
+ qCritical("The handler for the call %s was not found.", qPrintable(callId));
+ return nullptr;
+ }
+
+ if (it.value().isNull()) {
+ qCritical("The handler for the call %s expired.", qPrintable(callId));
+ m_handlers.erase(it);
+ }
+
+ return it.value().get();
+}
+
+void StoreLoaderImpl::removeHandler(const jstring &callId)
+{
+ QMutexLocker lock(&m_lock);
+ m_handlers.remove(QJniObject(callId).toString());
+}
+
+std::unique_ptr<StoreLoaderHandler>
+StoreLoader::loadModule(const QString &moduleName)
+{
+ if (moduleName.isEmpty())
+ return {};
+
+ if (!loaderInstance->registerNatives())
+ return {};
+
+ if (!loaderInstance->loader.isValid()) {
+ qCritical("StoreLoader not constructed");
+ return {};
+ }
+
+ auto handlerPtr = std::make_unique<StoreLoaderHandler>(
+ nullptr, StoreLoaderHandler::PrivateConstructor{});
+ loaderInstance->addHandler(handlerPtr.get());
+
+ const auto &callId = handlerPtr->callId();
+ qDebug("Loading module %s, callId: %s.", qPrintable(moduleName), qPrintable(callId));
+
+ loaderInstance->loader.callMethod<void>("installModuleFromStore", moduleName, callId);
+ return handlerPtr;
+}
+
+StoreLoaderHandler::StoreLoaderHandler(QObject *parent, PrivateConstructor)
+ : QObject(*new StoreLoaderHandlerPrivate(), parent)
+{
+}
+
+StoreLoaderHandler::~StoreLoaderHandler() = default;
+
+const QString &StoreLoaderHandler::callId() const & noexcept
+{
+ Q_D(const StoreLoaderHandler);
+ return d->callId();
+}
+
+void StoreLoaderHandler::cancel()
+{
+ Q_D(StoreLoaderHandler);
+ loaderInstance->loader.callMethod<void>("cancelInstall", d->callId());
+}
+
+#include "moc_storeloader.cpp"
diff --git a/tests/auto/other/android/dynamic_feature/storeloader/storeloader.h b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.h
new file mode 100644
index 00000000000..be4e103207e
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/storeloader/storeloader.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef STORELOADER_H
+#define STORELOADER_H
+
+#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qobject.h>
+
+#include <memory>
+
+class StoreLoaderHandler;
+
+namespace StoreLoader {
+ Q_NAMESPACE
+enum class State {
+ Unknown,
+ Initialized,
+ Pending,
+ Downloading,
+ Downloaded,
+ RequiresUserConfirmation,
+ Canceling,
+ Canceled,
+ Installing,
+ Installed,
+ Loading,
+ Loaded,
+ Error,
+};
+Q_ENUM_NS(State)
+
+std::unique_ptr<StoreLoaderHandler> loadModule(const QString &moduleName);
+}; // namespace StoreLoader
+
+class StoreLoaderHandlerPrivate;
+
+class StoreLoaderHandler : public QObject
+{
+ Q_OBJECT
+ QT_DEFINE_TAG_STRUCT(PrivateConstructor);
+
+public:
+ explicit StoreLoaderHandler(QObject *parent, PrivateConstructor);
+ ~StoreLoaderHandler() override;
+
+ const QString &callId() const & noexcept;
+
+ void cancel();
+signals:
+ void stateChanged(StoreLoader::State state);
+ void downloadProgress(qsizetype bytes, qsizetype total);
+ void errorOccured(int errorCode, const QString &errorString);
+ void confirmationRequest(int errorCode, const QString &errorString);
+ void finished();
+
+private:
+ Q_DISABLE_COPY_MOVE(StoreLoaderHandler)
+ Q_DECLARE_PRIVATE(StoreLoaderHandler)
+
+ friend std::unique_ptr<StoreLoaderHandler>
+ StoreLoader::loadModule(const QString &);
+};
+
+#endif // STORELOADER_H
diff --git a/tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp b/tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp
new file mode 100644
index 00000000000..739a645c5d5
--- /dev/null
+++ b/tests/auto/other/android/dynamic_feature/tst_android_dynamic_feature.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <storeloader.h>
+
+#include <QtTest/QTest>
+#include <QtTest/QSignalSpy>
+
+#include <QtCore/QVariant>
+
+using namespace Qt::Literals::StringLiterals;
+
+class QtDynamicFeatureTest : public QObject
+{
+ Q_OBJECT
+public:
+ QtDynamicFeatureTest(){}
+
+private Q_SLOTS:
+ void loadResourcesFeature();
+};
+
+void QtDynamicFeatureTest::loadResourcesFeature()
+{
+ QVERIFY(!QFile::exists(":/dynamic_resources/qtlogo.png"));
+
+ auto handler = StoreLoader::loadModule("tst_android_dynamic_feature_resources"_L1);
+
+ QSignalSpy spy(handler.get(), &StoreLoaderHandler::finished);
+
+ QVERIFY(spy.wait(20000));
+ QVERIFY(QFile::exists(":/dynamic_resources/qtlogo.png"));
+}
+
+QTEST_MAIN(QtDynamicFeatureTest)
+
+#include "tst_android_dynamic_feature.moc"
diff --git a/tests/auto/other/android/package_source_dir/CMakeLists.txt b/tests/auto/other/android/package_source_dir/CMakeLists.txt
new file mode 100644
index 00000000000..360aa50d87d
--- /dev/null
+++ b/tests/auto/other/android/package_source_dir/CMakeLists.txt
@@ -0,0 +1,54 @@
+# 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_android_package_source_dir LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Package name is generated and written to AndroidManifest.xml
+qt_internal_add_test(tst_package_source_dir_not_defined
+ SOURCES
+ tst_android_package_source_dir.cpp
+ DEFINES
+ EXPECTED_APP_NAME="tst_package_source_dir_not_defined"
+)
+
+# Package name from user-provided AndroidManifest.xml
+qt_internal_add_test(tst_package_source_dir_custom_android_manifest
+ SOURCES
+ tst_android_package_source_dir.cpp
+ DEFINES
+ EXPECTED_APP_NAME="tst_package_source_dir_custom_android_manifest_my"
+)
+
+if(QT_USE_ANDROID_MODERN_BUNDLE)
+ set_target_properties(tst_package_source_dir_custom_android_manifest
+ PROPERTIES
+ QT_ANDROID_PACKAGE_SOURCE_DIR
+ "${CMAKE_CURRENT_SOURCE_DIR}/custom_android_manifest_bundle"
+ )
+else()
+ set_target_properties(tst_package_source_dir_custom_android_manifest
+ PROPERTIES
+ QT_ANDROID_PACKAGE_SOURCE_DIR
+ "${CMAKE_CURRENT_SOURCE_DIR}/custom_android_manifest"
+ )
+endif()
+
+if(QT_USE_ANDROID_MODERN_BUNDLE)
+ # Partial gradle template is only supported by QT_USE_ANDROID_MODERN_BUNDLE
+ #
+ # Package name from user-provided gradle.build.in
+ qt_internal_add_test(tst_package_source_dir_partial_template
+ SOURCES
+ tst_android_package_source_dir.cpp
+ DEFINES
+ EXPECTED_APP_NAME="tst_package_source_dir_partial_template_my"
+ )
+ set_target_properties(tst_package_source_dir_partial_template
+ PROPERTIES
+ QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/partial_template"
+ )
+endif()
diff --git a/tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml b/tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml
new file mode 100644
index 00000000000..4f53295e979
--- /dev/null
+++ b/tests/auto/other/android/package_source_dir/custom_android_manifest/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.example.tst_package_source_dir_custom_android_manifest"
+ android:installLocation="auto"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <supports-screens
+ android:anyDensity="true"
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true" />
+ <application
+ android:name="org.qtproject.qt.android.bindings.QtApplication"
+ android:hardwareAccelerated="true"
+ android:label="tst_package_source_dir_custom_android_manifest_my"
+ android:requestLegacyExternalStorage="true"
+ android:allowBackup="true"
+ android:fullBackupOnly="false">
+ <activity
+ android:name="org.qtproject.qt.android.bindings.QtActivity"
+ android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
+ android:launchMode="singleTop"
+ android:screenOrientation="unspecified"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.app.lib_name"
+ android:value="tst_package_source_dir_custom_android_manifest" />
+
+ <meta-data
+ android:name="android.app.arguments"
+ android:value="" />
+ </activity>
+
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="${applicationId}.qtprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/qtprovider_paths"/>
+ </provider>
+ </application>
+</manifest>
diff --git a/tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml b/tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml
new file mode 100644
index 00000000000..4f53295e979
--- /dev/null
+++ b/tests/auto/other/android/package_source_dir/custom_android_manifest_bundle/app/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.example.tst_package_source_dir_custom_android_manifest"
+ android:installLocation="auto"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <supports-screens
+ android:anyDensity="true"
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true" />
+ <application
+ android:name="org.qtproject.qt.android.bindings.QtApplication"
+ android:hardwareAccelerated="true"
+ android:label="tst_package_source_dir_custom_android_manifest_my"
+ android:requestLegacyExternalStorage="true"
+ android:allowBackup="true"
+ android:fullBackupOnly="false">
+ <activity
+ android:name="org.qtproject.qt.android.bindings.QtActivity"
+ android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
+ android:launchMode="singleTop"
+ android:screenOrientation="unspecified"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.app.lib_name"
+ android:value="tst_package_source_dir_custom_android_manifest" />
+
+ <meta-data
+ android:name="android.app.arguments"
+ android:value="" />
+ </activity>
+
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="${applicationId}.qtprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/qtprovider_paths"/>
+ </provider>
+ </application>
+</manifest>
diff --git a/tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml b/tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml
new file mode 100644
index 00000000000..838d239e5f4
--- /dev/null
+++ b/tests/auto/other/android/package_source_dir/partial_template/app/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.example.tst_package_source_dir_partial_template"
+ android:installLocation="auto"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <supports-screens
+ android:anyDensity="true"
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true" />
+ <application
+ android:name="org.qtproject.qt.android.bindings.QtApplication"
+ android:hardwareAccelerated="true"
+ android:label="${appName}"
+ android:requestLegacyExternalStorage="true"
+ android:allowBackup="true"
+ android:fullBackupOnly="false">
+ <activity
+ android:name="org.qtproject.qt.android.bindings.QtActivity"
+ android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
+ android:launchMode="singleTop"
+ android:screenOrientation="unspecified"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.app.lib_name"
+ android:value="tst_package_source_dir_partial_template" />
+
+ <meta-data
+ android:name="android.app.arguments"
+ android:value="" />
+ </activity>
+
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="${applicationId}.qtprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/qtprovider_paths"/>
+ </provider>
+ </application>
+</manifest>
diff --git a/tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in b/tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in
new file mode 100644
index 00000000000..538f1ca6217
--- /dev/null
+++ b/tests/auto/other/android/package_source_dir/partial_template/app/build.gradle.in
@@ -0,0 +1,54 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.6.0'
+ }
+}
+
+apply plugin: '@GRADLE_PLUGIN_TYPE@'
+
+dependencies {
+ @GRADLE_DEPENDENCIES@
+}
+
+android {
+ namespace '@PACKAGE_NAME@'
+ compileSdkVersion '@ANDROID_COMPILE_SDK_VERSION@'
+ buildToolsVersion '@ANDROID_BUILD_TOOLS_VERSION@'
+ ndkVersion '@ANDROID_NDK_REVISION@'
+
+ defaultConfig {
+ @DEFAULT_CONFIG_VALUES@
+ manifestPlaceholders = [ appName:"tst_package_source_dir_partial_template_my" ]
+ }
+
+ sourceSets {
+ main {
+@SOURCE_SETS@
+ }
+ }
+
+ tasks.withType(JavaCompile) {
+ options.incremental = true
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+ aaptOptions {
+ // Do not compress Qt binary resources file
+ noCompress 'rcc'
+ }
+
+ @ANDROID_DEPLOYMENT_EXTRAS@
+}
diff --git a/tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp b/tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp
new file mode 100644
index 00000000000..d74d388371f
--- /dev/null
+++ b/tests/auto/other/android/package_source_dir/tst_android_package_source_dir.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QCoreApplication>
+#include <QJniObject>
+#include <QTest>
+#include <QDebug>
+
+using namespace QNativeInterface;
+
+Q_DECLARE_JNI_CLASS(ApplicationInfo, "android/content/pm/ApplicationInfo")
+Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager")
+Q_DECLARE_JNI_CLASS(CharSequence, "java/lang/CharSequence")
+
+class tst_android_package_source_dir : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void applicationName();
+
+private:
+};
+
+void tst_android_package_source_dir::applicationName()
+{
+ QJniObject appCtx = QAndroidApplication::context();
+ QVERIFY(appCtx.isValid());
+
+ const auto appInfo = appCtx.callMethod<QtJniTypes::ApplicationInfo>("getApplicationInfo");
+ QVERIFY(appInfo.isValid());
+
+ const auto packageManager = appCtx.callMethod<QtJniTypes::PackageManager>("getPackageManager");
+ QVERIFY(packageManager.isValid());
+
+ const auto appNameLabel =
+ appInfo.callMethod<QtJniTypes::CharSequence>("loadLabel", packageManager);
+ QVERIFY(appNameLabel.isValid());
+
+ const auto appName = appNameLabel.callMethod<jstring>("toString").toString();
+
+ QCOMPARE_EQ(appName, QString::fromLatin1(EXPECTED_APP_NAME));
+}
+
+QTEST_MAIN(tst_android_package_source_dir);
+
+#include "tst_android_package_source_dir.moc"
diff --git a/tests/auto/other/android/permissions/CMakeLists.txt b/tests/auto/other/android/permissions/CMakeLists.txt
new file mode 100644
index 00000000000..0424c674e1b
--- /dev/null
+++ b/tests/auto/other/android/permissions/CMakeLists.txt
@@ -0,0 +1,23 @@
+# 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_android_permissions LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_android_permissions
+ SOURCES
+ tst_android_permissions.cpp
+)
+
+qt_add_android_permission(tst_android_permissions
+ NAME android.permission.ACCESS_COARSE_LOCATION
+)
+
+qt_add_android_permission(tst_android_permissions
+ NAME android.permission.ACCESS_FINE_LOCATION
+ ATTRIBUTES
+ maxSdkVersion 31
+)
diff --git a/tests/auto/other/android/permissions/tst_android_permissions.cpp b/tests/auto/other/android/permissions/tst_android_permissions.cpp
new file mode 100644
index 00000000000..5d7a255d3ac
--- /dev/null
+++ b/tests/auto/other/android/permissions/tst_android_permissions.cpp
@@ -0,0 +1,80 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QCoreApplication>
+#include <QJniObject>
+#include <QSet>
+#include <QString>
+#include <QTest>
+
+constexpr int GET_PERMISSIONS(0x00001000);
+
+using namespace QNativeInterface;
+using namespace Qt::StringLiterals;
+
+Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager")
+Q_DECLARE_JNI_CLASS(PackageInfo, "android/content/pm/PackageInfo")
+
+class tst_android_permissions : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void checkExpectedDefaults();
+ void checkNonExisting();
+ void checkNonDefaultPermissions();
+
+private:
+ QJniArray<QString> m_requestedPermissions;
+};
+
+void tst_android_permissions::initTestCase()
+{
+ QJniObject appCtx = QAndroidApplication::context();
+ QVERIFY(appCtx.isValid());
+
+ const auto packageName = appCtx.callMethod<QString>("getPackageName");
+ const auto packageManager = appCtx.callMethod<QtJniTypes::PackageManager>("getPackageManager");
+ QVERIFY(packageManager.isValid());
+
+ const auto packageInfo = QJniObject(packageManager.callMethod<QtJniTypes::PackageInfo>(
+ "getPackageInfo", packageName, jint(GET_PERMISSIONS)));
+ QVERIFY(packageInfo.isValid());
+
+ m_requestedPermissions = packageInfo.getField<QJniArray<QString>>("requestedPermissions");
+ QVERIFY(m_requestedPermissions.isValid());
+}
+
+void tst_android_permissions::checkExpectedDefaults()
+{
+ QSet<QString> expectedDefaults{ { "android.permission.INTERNET"_L1 },
+ { "android.permission.WRITE_EXTERNAL_STORAGE"_L1 },
+ { "android.permission.READ_EXTERNAL_STORAGE"_L1 } };
+
+ for (const auto &permission : m_requestedPermissions)
+ expectedDefaults.remove(permission);
+
+ QVERIFY(expectedDefaults.empty());
+}
+
+void tst_android_permissions::checkNonExisting()
+{
+ for (const auto &permission : m_requestedPermissions)
+ QCOMPARE_NE(permission, "android.permission.BLUETOOTH_SCAN");
+}
+
+void tst_android_permissions::checkNonDefaultPermissions()
+{
+ bool hasNonDefaultPermissions = false;
+ for (const auto &permission : m_requestedPermissions) {
+ if (permission == "android.permission.ACCESS_COARSE_LOCATION")
+ hasNonDefaultPermissions = true;
+ }
+
+ QVERIFY(hasNonDefaultPermissions);
+}
+
+QTEST_MAIN(tst_android_permissions);
+
+#include "tst_android_permissions.moc"
diff --git a/tests/auto/testlib/selftests/mouse/tst_mouse.cpp b/tests/auto/testlib/selftests/mouse/tst_mouse.cpp
index 4af55adde91..ae8fa54dcea 100644
--- a/tests/auto/testlib/selftests/mouse/tst_mouse.cpp
+++ b/tests/auto/testlib/selftests/mouse/tst_mouse.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
+#include <QtTest/private/qtesthelpers_p.h>
#include <QtGui/QWindow>
#include <QtGui/QCursor>
#include <QtGui/private/qguiapplication_p.h>
@@ -256,8 +257,9 @@ void tst_Mouse::doubleClick()
{
MouseWindow w;
w.show();
- w.setGeometry(100, 100, 200, 200);
+ w.resize(200, 200);
QVERIFY(QTest::qWaitForWindowActive(&w));
+ QVERIFY(QTestPrivate::ensurePositionTopLeft(&w));
// click
QPoint point(10, 10);
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
index ab88dd78111..217122100a0 100644
--- a/tests/auto/tools/moc/tst_moc.cpp
+++ b/tests/auto/tools/moc/tst_moc.cpp
@@ -7,7 +7,7 @@
https://developercommunity.visualstudio.com/t/Regression:-c-compilation-failure-in-c/10926790
*/
#include <QtCore/qcompilerdetection.h>
-#if defined(Q_CC_MSVC_ONLY) && (_MSC_FULL_VER >= 194435209) && (_MSC_FULL_VER < 194500000)
+#if defined(Q_CC_MSVC_ONLY) && (_MSC_FULL_VER >= 194435208) && (_MSC_FULL_VER < 194500000)
# define MSVC_ENUM_BUG
#endif
diff --git a/tests/auto/tools/qmake/testdata/windows_resources/REUSE.toml b/tests/auto/tools/qmake/testdata/windows_resources/REUSE.toml
index 8d05566c678..65bfc74258c 100644
--- a/tests/auto/tools/qmake/testdata/windows_resources/REUSE.toml
+++ b/tests/auto/tools/qmake/testdata/windows_resources/REUSE.toml
@@ -3,5 +3,5 @@ version = 1
[[annotations]]
path = ["version.inc"]
precedence = "override"
-SPDX-FileCopyrightText = "Copyright (C) 2019 The Qt Company Ltd."
+SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GPL-3.0-only"
diff --git a/tests/auto/tools/uic/baseline/REUSE.toml b/tests/auto/tools/uic/baseline/REUSE.toml
index 30f460c00b0..c147a71dca5 100644
--- a/tests/auto/tools/uic/baseline/REUSE.toml
+++ b/tests/auto/tools/uic/baseline/REUSE.toml
@@ -3,5 +3,5 @@ version = 1
[[annotations]]
path = ["Widget.ui"]
precedence = "override"
-SPDX-FileCopyrightText = "Copyright (C) 2016 The Qt Company Ltd."
+SPDX-FileCopyrightText = "Copyright (C) The Qt Company Ltd."
SPDX-License-Identifier = "LicenseRef-Qt-Commercial OR GPL-3.0-only"
diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp
index 3e5ff5ce640..df19ea1568e 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp
+++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp
@@ -657,9 +657,6 @@ void tst_QGraphicsView::viewport()
#if QT_CONFIG(opengl)
void tst_QGraphicsView::openGLViewport()
{
-#if !QT_CONFIG(run_opengl_tests)
- QSKIP("Skip test as run-opengl-tests feature is off.");
-#endif
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
QSKIP("QOpenGL is not supported on this platform.");
if (isPlatformEGLFS())
diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp
index 03be7aa0fe9..b651668cabd 100644
--- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp
+++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp
@@ -213,6 +213,7 @@ private slots:
void storeRestoreLowMemoryMode();
void setSectionResizeModeWithSectionWillTakeMemory();
void setModelWithAutoSizeWillSwitchToMemoryMode();
+ void tableViewResizeSectionsWillSwitchToMemoryMode();
void setDefaultSectionSizeRespectsColumnWidth();
@@ -3559,12 +3560,17 @@ struct TableViewWithBasicModel : public QTableView
emptyState = verticalHeader()->saveState();
setModel(&m);
header = verticalHeader();
+ horizontal_header = horizontalHeader();
}
bool hasLowMemoryUsage() const {
return emptyState.size() == header->saveState().size();
}
+ bool horizontalLowMememorUsage() const {
+ return emptyState.size() == horizontal_header->saveState().size();
+ }
+
bool hasHigherMemoryUsage() const {
const int delta = 1000;
return header->saveState().size() > delta + emptyState.size();
@@ -3572,6 +3578,7 @@ struct TableViewWithBasicModel : public QTableView
BasicModel m;
QHeaderView *header;
+ QHeaderView *horizontal_header;
QByteArray emptyState;
};
@@ -3770,6 +3777,15 @@ void tst_QHeaderView::setModelWithAutoSizeWillSwitchToMemoryMode()
QCOMPARE_GT(nonEmptyState.size(), emptyState.size() + delta);
}
+void tst_QHeaderView::tableViewResizeSectionsWillSwitchToMemoryMode()
+{
+ TableViewWithBasicModel tv;
+ QVERIFY(tv.horizontalLowMememorUsage());
+ tv.horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+ tv.resizeColumnsToContents();
+ QVERIFY(!tv.horizontalLowMememorUsage());
+}
+
void tst_QHeaderView::setDefaultSectionSizeRespectsColumnWidth()
{
QTreeWidget tree;
diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
index c460eb1db8c..f418d5ead14 100644
--- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
+++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
@@ -655,121 +655,122 @@ void tst_QWidgetRepaintManager::evaluateRhi()
#if QT_CONFIG(opengl)
-#if QT_CONFIG(run_opengl_tests)
- {
- // Non-native child RHI widget enables RHI for top level regular widget
- QWidget topLevel;
- RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
- topLevel.show();
- QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
- QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
- // Only the native widget that actually flushes will report usesRhiFlush
- QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
- // But it should have an RHI it can use
- QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
- }
-
- {
- // Native child RHI widget does not enable RHI for top level
- QWidget topLevel;
- RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
- nativeRhiWidget.setAttribute(Qt::WA_NativeWindow);
- topLevel.show();
- QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
- QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi());
- QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType);
- QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush);
-
- if (!usesRhiBackingStore)
- QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi());
- }
-
- {
- // Non-native RHI child of native child enables RHI for native child,
- // but not top level.
- QWidget topLevel;
- QWidget nativeChild(&topLevel);
- nativeChild.setAttribute(Qt::WA_NativeWindow);
- RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild);
- topLevel.show();
- QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
-
- QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi());
- QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
- QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType);
- QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush);
- if (!usesRhiBackingStore)
- QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi());
- }
-
- {
- // Native child RHI widget does not prevent RHI for top level
- // if non-native RHI child widget is also present.
- QWidget topLevel;
- RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
- RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
- nativeRhiWidget.setAttribute(Qt::WA_NativeWindow);
- topLevel.show();
- QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) {
- QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi());
- QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
- QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
- }
+ {
+ // Non-native child RHI widget enables RHI for top level regular widget
+ QWidget topLevel;
+ RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+ QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
+ // Only the native widget that actually flushes will report usesRhiFlush
+ QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
+ // But it should have an RHI it can use
+ QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
+ }
- {
- // Reparenting into a window that already matches the required
- // surface type should still mark the parent as flushing with RHI.
- QWidget topLevel;
+ {
+ // Native child RHI widget does not enable RHI for top level
+ QWidget topLevel;
+ RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
+ nativeRhiWidget.setAttribute(Qt::WA_NativeWindow);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+ QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi());
+ QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType);
+ QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush);
+
+ if (!usesRhiBackingStore)
+ QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi());
+ }
- RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::Null);
- rhiWidget.show();
- QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget));
- QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
+ {
+ // Non-native RHI child of native child enables RHI for native child,
+ // but not top level.
+ QWidget topLevel;
+ QWidget nativeChild(&topLevel);
+ nativeChild.setAttribute(Qt::WA_NativeWindow);
+ RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+
+ QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi());
+ QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
+ QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType);
+ QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush);
+ if (!usesRhiBackingStore)
+ QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi());
+ }
- topLevel.show();
- QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
- rhiWidget.setParent(&topLevel);
- QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
- }
+ {
+ // Native child RHI widget does not prevent RHI for top level
+ // if non-native RHI child widget is also present.
+ QWidget topLevel;
+ RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
+ RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
+ nativeRhiWidget.setAttribute(Qt::WA_NativeWindow);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+
+ QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi());
+ QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
+ QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
+ }
- {
- // Non-native RHI child of native child enables RHI for native child,
- // but does not prevent top level from flushing with RHI.
- QWidget topLevel;
- QWidget nativeChild(&topLevel);
- nativeChild.setAttribute(Qt::WA_NativeWindow);
- RhiWidget rhiGranchild(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild);
- RhiWidget rhiChild(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
- topLevel.show();
- QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+ {
+ // Reparenting into a window that already matches the required
+ // surface type should still mark the parent as flushing with RHI.
+ QWidget topLevel;
+
+ RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::Null);
+ rhiWidget.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget));
+ QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi());
+
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+ rhiWidget.setParent(&topLevel);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
+ }
- QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi());
- QVERIFY(!QWidgetPrivate::get(&rhiGranchild)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&rhiGranchild)->rhi());
- QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
- QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
- QVERIFY(!QWidgetPrivate::get(&rhiChild)->usesRhiFlush);
- QVERIFY(QWidgetPrivate::get(&rhiChild)->rhi());
+ {
+ // Non-native RHI child of native child enables RHI for native child,
+ // but does not prevent top level from flushing with RHI.
+ QWidget topLevel;
+ QWidget nativeChild(&topLevel);
+ nativeChild.setAttribute(Qt::WA_NativeWindow);
+ RhiWidget rhiGranchild(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild);
+ RhiWidget rhiChild(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+
+ QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi());
+ QVERIFY(!QWidgetPrivate::get(&rhiGranchild)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&rhiGranchild)->rhi());
+ QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&topLevel)->rhi());
+ QVERIFY(!QWidgetPrivate::get(&rhiChild)->usesRhiFlush);
+ QVERIFY(QWidgetPrivate::get(&rhiChild)->rhi());
+ }
}
-#endif // QT_CONFIG(run_opengl_tests)
#if QT_CONFIG(metal)
QRhiMetalInitParams metalParams;
@@ -805,10 +806,6 @@ void tst_QWidgetRepaintManager::evaluateRhi()
void tst_QWidgetRepaintManager::rhiRecreateMaintainsWindowProperties()
{
-#if !QT_CONFIG(run_opengl_tests)
- QSKIP("Skip test as run-opengl-tests feature is off.");
-#endif
-
const auto *integration = QGuiApplicationPrivate::platformIntegration();
if (!integration->hasCapability(QPlatformIntegration::RhiBasedRendering))
QSKIP("Platform does not support RHI based rendering");
@@ -817,6 +814,9 @@ void tst_QWidgetRepaintManager::rhiRecreateMaintainsWindowProperties()
QSKIP("Platform does not support OpenGL RHI based rendering");
#endif
+ if (!integration->hasCapability(QPlatformIntegration::OpenGL))
+ QSKIP("OpenGL is not supported on this platform.");
+
// Reparenting Rhi widget into a window causes the window to be
// recreated, but after recreation the window properties such as
// window position must remain the same
diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
index 1b3afa0a4dc..cbe554f997f 100644
--- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
+++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
@@ -8,10 +8,12 @@
#include <QPushButton>
#include <QMainWindow>
#include <QMenuBar>
+#include <QPlainTextEdit>
#include <QToolBar>
#include <QToolButton>
#include <QStatusBar>
#include <QListWidget>
+#include <QVBoxLayout>
#include <QWidgetAction>
#include <QScreen>
#include <QSpinBox>
@@ -96,6 +98,8 @@ private slots:
void QTBUG_89082_actionTipsHide();
void QTBUG8122_widgetActionCrashOnClose();
void widgetActionTriggerClosesMenu();
+ void widgetActionContextMenu();
+
void transientParent();
void QTBUG_10735_crashWithDialog();
@@ -1590,6 +1594,36 @@ void tst_QMenu::widgetActionTriggerClosesMenu()
QCOMPARE(actionTriggered, &widgetAction);
}
+void tst_QMenu::widgetActionContextMenu() // QTBUG-134757
+{
+ QPushButton openButton("open");
+ QMenu *menu = new QMenu(&openButton);
+ QVBoxLayout *layout = new QVBoxLayout;
+ QWidgetAction widgetAction(menu);
+ QWidget menuWidget(menu);
+ QPlainTextEdit edit;
+ openButton.setMenu(menu);
+ menuWidget.setLayout(layout);
+ widgetAction.setDefaultWidget(&menuWidget);
+ menu->addAction(&widgetAction);
+ layout->addWidget(&edit);
+
+ openButton.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&openButton));
+
+ // Click the QPushButton to open its menu
+ QTest::mouseClick(&openButton, Qt::LeftButton);
+ QVERIFY(QTest::qWaitForWindowExposed(&menuWidget));
+ QWindow *popupWindow = edit.window()->windowHandle();
+ QVERIFY(popupWindow);
+ QCOMPARE(QApplication::activePopupWidget(), menu);
+
+ // Right-click the QPlainTextEdit to open its context menu
+ QTest::mouseClick(popupWindow, Qt::RightButton);
+ QVERIFY(qobject_cast<QMenu *>(QApplication::activePopupWidget()));
+ QCOMPARE_NE(QApplication::activePopupWidget(), menu);
+}
+
void tst_QMenu::transientParent()
{
QMainWindow window;
diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp
index 609a0847862..f4baf6b1714 100644
--- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp
+++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp
@@ -1233,12 +1233,41 @@ void tst_QMenuBar::taskQTBUG4965_escapeEaten()
}
#endif
+#ifdef Q_OS_LINUX
+class ResizeCounter : public QObject
+{
+public:
+ explicit ResizeCounter(QMenuBar *bar)
+ {
+ Q_ASSERT(bar);
+ bar->installEventFilter(this);
+ }
+
+ int resizeCount() const { return m_resizeCount; }
+
+protected:
+ bool eventFilter(QObject *o, QEvent *event) override
+ {
+ Q_UNUSED(o);
+ if (event->type() == QEvent::Resize)
+ ++m_resizeCount;
+ return false;
+ }
+
+private:
+ int m_resizeCount = 0;
+};
+#endif
+
void tst_QMenuBar::taskQTBUG11823_crashwithInvisibleActions()
{
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
QSKIP("Wayland: This fails. Figure out why.");
QMenuBar menubar;
+#ifdef Q_OS_LINUX
+ ResizeCounter counter(&menubar);
+#endif
menubar.setNativeMenuBar(false); //we can't check the geometry of native menubars
QAction * m = menubar.addAction( "&m" );
@@ -1247,6 +1276,12 @@ void tst_QMenuBar::taskQTBUG11823_crashwithInvisibleActions()
centerOnScreen(&menubar);
menubar.show();
QVERIFY(QTest::qWaitForWindowActive(&menubar));
+
+#ifdef Q_OS_LINUX
+ if (QSysInfo::productType().contains("opensuse"))
+ QVERIFY(QTest::qWaitFor([&]{ return counter.resizeCount() == 2;}));
+#endif
+
menubar.setActiveAction(m);
QCOMPARE(menubar.activeAction(), m);
QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Right);
diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
index b2cdd6b72c2..c5df30dd7d1 100644
--- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
+++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
@@ -63,12 +63,12 @@ private slots:
void tst_QOpenGLWidget::initTestCase()
{
-#if !QT_CONFIG(run_opengl_tests)
- QSKIP("Skip test as run-opengl-tests feature is off.");
-#endif
// See QOpenGLWidget constructor
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
QSKIP("QOpenGLWidget is not supported on this platform.");
+
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
+ QSKIP("OpenGL is not supported on this platform.");
}
void tst_QOpenGLWidget::create()
diff --git a/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp b/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp
index da4fee72f65..457df2159b1 100644
--- a/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp
+++ b/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp
@@ -64,7 +64,7 @@ void tst_QRhiWidget::testData()
QTest::newRow("Null") << QRhiWidget::Api::Null;
#endif
-#if QT_CONFIG(opengl) && QT_CONFIG(run_opengl_tests)
+#if QT_CONFIG(opengl)
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
QTest::newRow("OpenGL") << QRhiWidget::Api::OpenGL;
#endif