summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/CMakeLists.txt6
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/RunCMakeTest.cmake63
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/check.cmake63
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGen.cmake67
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGenIntro.cmake12
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/full.cmake17
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/minimal.cmake11
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/subprojects/CMakeLists.txt5
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/CMakeLists.txt51
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/subproj1_helper.cpp4
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/CMakeLists.txt46
-rw-r--r--tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/subproj2_helper.cpp4
-rw-r--r--tests/auto/corelib/global/qcheckedint/tst_qcheckedint.cpp2
-rw-r--r--tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp22
-rw-r--r--tests/auto/corelib/global/qglobal/tst_qglobal.cpp2
-rw-r--r--tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp2
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp24
-rw-r--r--tests/auto/corelib/io/qdebug/tst_qdebug.cpp2
-rw-r--r--tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp6
-rw-r--r--tests/auto/corelib/io/qsettings/tst_qsettings.cpp113
-rw-r--r--tests/auto/corelib/itemmodels/qrangemodel/data.h26
-rw-r--r--tests/auto/corelib/itemmodels/qrangemodel/tst_qrangemodel.cpp230
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp26
-rw-r--r--tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp7
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp25
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h7
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp10
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp3
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp4
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp70
-rw-r--r--tests/auto/corelib/platform/android/tst_android.cpp9
-rw-r--r--tests/auto/corelib/plugin/CMakeLists.txt4
-rw-r--r--tests/auto/corelib/plugin/qlibrary/tst/CMakeLists.txt2
-rw-r--r--tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp8
-rw-r--r--tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp46
-rw-r--r--tests/auto/corelib/time/CMakeLists.txt5
-rw-r--r--tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp3
-rw-r--r--tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp796
-rw-r--r--tests/auto/corelib/time/qtimezonebackend/CMakeLists.txt32
-rw-r--r--tests/auto/corelib/time/qtimezonebackend/tst_qtimezonebackend.cpp784
-rw-r--r--tests/auto/network/access/http2/tst_http2.cpp6
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp2
-rw-r--r--tests/auto/other/languagechange/tst_languagechange.cpp4
-rw-r--r--tests/auto/other/qaccessibility/tst_qaccessibility.cpp16
-rw-r--r--tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp8
-rw-r--r--tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp12
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp3
-rw-r--r--tests/auto/widgets/kernel/qapplication/CMakeLists.txt7
-rw-r--r--tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp7
-rw-r--r--tests/manual/CMakeLists.txt1
-rw-r--r--tests/manual/assets/CMakeLists.txt4
-rw-r--r--tests/manual/assets/assets.pro3
-rw-r--r--tests/manual/assets/downloader/CMakeLists.txt15
-rw-r--r--tests/manual/assets/downloader/downloader.pro5
-rw-r--r--tests/manual/assets/downloader/main.cpp48
-rw-r--r--tests/manual/corelib/itemmodels/qrangemodel/main.cpp81
-rw-r--r--tests/manual/manual.pro1
-rw-r--r--tests/manual/wasm/eventloop/suspendresumecontrol_auto/main.cpp45
-rw-r--r--tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp2
60 files changed, 1892 insertions, 999 deletions
diff --git a/tests/auto/cmake/RunCMake/Sbom/CMakeLists.txt b/tests/auto/cmake/RunCMake/Sbom/CMakeLists.txt
index d0ef37d817f..e9578d7246a 100644
--- a/tests/auto/cmake/RunCMake/Sbom/CMakeLists.txt
+++ b/tests/auto/cmake/RunCMake/Sbom/CMakeLists.txt
@@ -1,3 +1,7 @@
cmake_minimum_required(VERSION 3.16)
project(${RunCMake_TEST} LANGUAGES CXX)
-include(${RunCMake_TEST}.cmake)
+
+# To allow including the gen files in other subdirectory projects.
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+include(${SBOM_INCLUDE_FILE}.cmake)
diff --git a/tests/auto/cmake/RunCMake/Sbom/RunCMakeTest.cmake b/tests/auto/cmake/RunCMake/Sbom/RunCMakeTest.cmake
index 3103e651093..81e83ccead3 100644
--- a/tests/auto/cmake/RunCMake/Sbom/RunCMakeTest.cmake
+++ b/tests/auto/cmake/RunCMake/Sbom/RunCMakeTest.cmake
@@ -1,27 +1,68 @@
include(QtRunCMake)
-function(run_cmake_and_build case)
+function(run_cmake_and_build case format_case)
+ set(include_file "${case}")
+ set(case "${format_case}-${case}")
+
# Set common build directory for configure and build
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
set(options
"-DQt6_DIR=${Qt6_DIR}"
"-DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_BINARY_DIR}/installed"
+ "-DSBOM_INCLUDE_FILE=${include_file}"
+ "-DFORMAT_CASE=${format_case}"
)
+ if(format_case STREQUAL "spdx23")
+ list(APPEND options
+ -DQT_GENERATE_SBOM=ON
+ -DQT_SBOM_GENERATE_SPDX_V2=ON
+ -DQT_SBOM_GENERATE_CYDX_V1_6=OFF
+ )
+ elseif(format_case STREQUAL "cydx16")
+ list(APPEND options
+ -DQT_GENERATE_SBOM=ON
+ -DQT_SBOM_GENERATE_SPDX_V2=OFF
+ -DQT_SBOM_GENERATE_CYDX_V1_6=ON
+ )
+ elseif(format_case STREQUAL "all")
+ list(APPEND options
+ -DQT_GENERATE_SBOM=ON
+ -DQT_SBOM_GENERATE_SPDX_V2=ON
+ -DQT_SBOM_GENERATE_CYDX_V1_6=ON
+ )
+ elseif(format_case STREQUAL "none")
+ list(APPEND options
+ -DQT_GENERATE_SBOM=OFF
+ )
+ endif()
+
+ # Check CI environment variables for SBOM options to ensure we only enabled checks that
+ # require additional dependencies on machines that actually have them.
+ # Also allow force enabling all checks via QT_SBOM_FORCE_ALL_CHECKS env var.
set(maybe_sbom_env_args "$ENV{SBOM_COMMON_ARGS}")
+ set(force_all_checks "$ENV{QT_SBOM_FORCE_ALL_CHECKS}")
- if(maybe_sbom_env_args MATCHES "QT_INTERNAL_SBOM_DEFAULT_CHECKS=ON")
+ if(maybe_sbom_env_args MATCHES "QT_INTERNAL_SBOM_DEFAULT_CHECKS=ON"
+ OR force_all_checks)
list(APPEND options "-DQT_INTERNAL_SBOM_DEFAULT_CHECKS=ON")
endif()
- if(maybe_sbom_env_args MATCHES "QT_INTERNAL_SBOM_AUDIT=ON")
+ if(maybe_sbom_env_args MATCHES "QT_INTERNAL_SBOM_AUDIT=ON"
+ OR force_all_checks)
list(APPEND options "-DQT_INTERNAL_SBOM_AUDIT=ON")
endif()
- if(maybe_sbom_env_args MATCHES "QT_INTERNAL_SBOM_AUDIT_NO_ERROR=ON")
+ if(maybe_sbom_env_args MATCHES "QT_INTERNAL_SBOM_AUDIT_NO_ERROR=ON"
+ OR force_all_checks)
list(APPEND options "-DQT_INTERNAL_SBOM_AUDIT_NO_ERROR=ON")
endif()
+ if(maybe_sbom_env_args MATCHES "QT_SBOM_REQUIRE_GENERATE_CYDX_V1_6=ON"
+ OR force_all_checks)
+ list(APPEND options "-DQT_SBOM_REQUIRE_GENERATE_CYDX_V1_6=ON")
+ endif()
+
# Need to pass the python interpreter paths, to avoid sbom2doc not found errors.
# This mirrors what coin/instructions/prepare_building_env.yaml does.
set(maybe_python3_path "$ENV{PYTHON3_PATH}")
@@ -42,9 +83,17 @@ function(run_cmake_and_build case)
# fine.
set(RunCMake_TEST_OUTPUT_MERGE 1)
run_cmake_command(${case}-build ${CMAKE_COMMAND} --build .)
+
+ # Check the sbom files are present after installation.
+ set(RunCMake-check-file "check.cmake")
run_cmake_command(${case}-install ${CMAKE_COMMAND} --install .)
+ unset(RunCMake-check-file)
endfunction()
-run_cmake_and_build(minimal)
-run_cmake_and_build(full)
-run_cmake_and_build(versions)
+set(format_cases spdx23 cydx16 all none)
+foreach(format_case IN LISTS format_cases)
+ run_cmake_and_build(minimal "${format_case}")
+ run_cmake_and_build(full "${format_case}")
+ run_cmake_and_build(versions "${format_case}")
+endforeach()
+
diff --git a/tests/auto/cmake/RunCMake/Sbom/check.cmake b/tests/auto/cmake/RunCMake/Sbom/check.cmake
new file mode 100644
index 00000000000..9e143e35ab2
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/check.cmake
@@ -0,0 +1,63 @@
+function(check_exists file)
+ if(NOT EXISTS "${file}")
+ get_filename_component(file_dir "${file}" DIRECTORY)
+ file(GLOB dir_contents "${file_dir}/*")
+ string(APPEND RunCMake_TEST_FAILED "${file} does not exist\n. "
+ "Contents of directory ${file_dir}:\n ${dir_contents}\n")
+ endif()
+ set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+endfunction()
+
+function(check_not_exists file)
+ if(EXISTS "${file}")
+ get_filename_component(file_dir "${file}" DIRECTORY)
+ file(GLOB dir_contents "${file_dir}/*")
+ string(APPEND RunCMake_TEST_FAILED "${file} exists\n. "
+ "Contents of directory ${file_dir}:\n ${dir_contents}\n")
+ endif()
+ set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+endfunction()
+
+# Check that the correct option values are used for the project root sbom.
+set(root_result_file "${RunCMake_TEST_BINARY_DIR}/result.cmake")
+if(EXISTS "${root_result_file}")
+ include("${root_result_file}")
+
+ if(NOT "${ORIGINAL_QT_GENERATE_SBOM}" STREQUAL "${RESULT_QT_GENERATE_SBOM}")
+ string(APPEND RunCMake_TEST_FAILED
+ "QT_GENERATE_SBOM is ${RESULT_QT_GENERATE_SBOM}, expected ${ORIGINAL_QT_GENERATE_SBOM} \n")
+ endif()
+
+ if(NOT "${ORIGINAL_QT_SBOM_GENERATE_SPDX_V2}" STREQUAL "${RESULT_QT_SBOM_GENERATE_SPDX_V2}")
+ string(APPEND RunCMake_TEST_FAILED
+ "QT_SBOM_GENERATE_SPDX_V2 is ${RESULT_QT_SBOM_GENERATE_SPDX_V2}, "
+ "expected ${ORIGINAL_QT_SBOM_GENERATE_SPDX_V2} \n")
+ endif()
+
+ if(NOT "${ORIGINAL_QT_SBOM_GENERATE_CYDX_V1_6}" STREQUAL "${RESULT_QT_SBOM_GENERATE_CYDX_V1_6}")
+ string(APPEND RunCMake_TEST_FAILED
+ "QT_SBOM_GENERATE_CYDX_V1_6 is ${RESULT_QT_SBOM_GENERATE_CYDX_V1_6}, "
+ "expected ${ORIGINAL_QT_SBOM_GENERATE_CYDX_V1_6} \n")
+ endif()
+endif()
+
+
+# Glob for all result.cmake files recursively in the root of the test binary dir, and run checks
+# for each of them.
+file(GLOB_RECURSE result_files
+ "${RunCMake_TEST_BINARY_DIR}/**/result.cmake"
+)
+
+# Confirm that the all subproject sbom files are installed, including the root one.
+foreach(result_file IN LISTS result_files)
+ include("${result_file}")
+
+ foreach(sbom_doc IN LISTS SBOM_DOCUMENTS)
+ check_exists("${sbom_doc}")
+ endforeach()
+
+ foreach(sbom_doc IN LISTS NO_SBOM_DOCUMENTS)
+ check_not_exists("${sbom_doc}")
+ endforeach()
+endforeach()
+
diff --git a/tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGen.cmake b/tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGen.cmake
new file mode 100644
index 00000000000..a892fee2082
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGen.cmake
@@ -0,0 +1,67 @@
+if(NOT SBOM_PROJECT_NAME)
+ set(SBOM_PROJECT_NAME "${PROJECT_NAME}")
+endif()
+# Convert to lower case, otherwise on case-sensitive filesystems the generated
+# filenames may not match the expected ones.
+string(TOLOWER "${SBOM_PROJECT_NAME}" SBOM_PROJECT_NAME)
+
+if(NOT SBOM_VERSION)
+ set(SBOM_VERSION "1.0.0")
+endif()
+
+if(NOT SBOM_INSTALL_DIR)
+ set(SBOM_INSTALL_DIR "sbom")
+endif()
+
+set(sbom_document_base_name "${SBOM_PROJECT_NAME}-${SBOM_VERSION}")
+set(sbom_install_dir "${CMAKE_BINARY_DIR}/installed/${SBOM_INSTALL_DIR}")
+
+set(spdx_file "${sbom_install_dir}/${sbom_document_base_name}.spdx")
+set(spdx_json_file "${sbom_install_dir}/${sbom_document_base_name}.spdx.json")
+set(cydx_file "${sbom_install_dir}/${sbom_document_base_name}.cdx.json")
+
+set(sbom_documents "")
+set(no_sbom_documents "")
+
+if(FORMAT_CASE STREQUAL "spdx23" OR FORMAT_CASE STREQUAL "all")
+ if(QT_SBOM_GENERATE_SPDX_V2)
+ list(APPEND sbom_documents "${spdx_file}")
+ else()
+ list(APPEND no_sbom_documents "${spdx_file}")
+ endif()
+
+ if(QT_SBOM_GENERATE_SPDX_V2_JSON)
+ list(APPEND sbom_documents "${spdx_json_file}")
+ else()
+ list(APPEND no_sbom_documents "${spdx_json_file}")
+ endif()
+endif()
+
+if(FORMAT_CASE STREQUAL "cydx16" OR FORMAT_CASE STREQUAL "all")
+ if(QT_SBOM_GENERATE_CYDX_V1_6)
+ list(APPEND sbom_documents "${cydx_file}")
+ else()
+ list(APPEND no_sbom_documents "${cydx_file}")
+ endif()
+endif()
+
+if(FORMAT_CASE STREQUAL "none")
+ set(no_sbom_documents ${spdx_file} ${spdx_json_file} ${cydx_file})
+ set(sbom_documents "")
+endif()
+
+# These values will be used by the check.cmake script after installation.
+file(GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/result.cmake"
+ CONTENT
+ "
+set(SBOM_DOCUMENTS \"${sbom_documents}\")
+set(NO_SBOM_DOCUMENTS \"${no_sbom_documents}\")
+set(ORIGINAL_QT_GENERATE_SBOM \"${original_QT_GENERATE_SBOM}\")
+set(ORIGINAL_QT_SBOM_GENERATE_SPDX_V2 \"${original_QT_SBOM_GENERATE_SPDX_V2}\")
+set(ORIGINAL_QT_SBOM_GENERATE_CYDX_V1_6 \"${original_QT_SBOM_GENERATE_CYDX_V1_6}\")
+set(RESULT_QT_GENERATE_SBOM \"${QT_GENERATE_SBOM}\")
+set(RESULT_QT_SBOM_GENERATE_SPDX_V2 \"${QT_SBOM_GENERATE_SPDX_V2}\")
+set(RESULT_QT_SBOM_GENERATE_CYDX_V1_6 \"${QT_SBOM_GENERATE_CYDX_V1_6}\")
+"
+)
diff --git a/tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGenIntro.cmake b/tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGenIntro.cmake
new file mode 100644
index 00000000000..11ec50d76d6
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/cmake/CommonResultGenIntro.cmake
@@ -0,0 +1,12 @@
+# Record the sbom option values before they might be modified by an sbom_setup call, due to
+# missing python dependencies.
+set(original_QT_GENERATE_SBOM "${QT_GENERATE_SBOM}")
+set(original_QT_SBOM_GENERATE_SPDX_V2 "${QT_SBOM_GENERATE_SPDX_V2}")
+set(original_QT_SBOM_GENERATE_CYDX_V1_6 "${QT_SBOM_GENERATE_CYDX_V1_6}")
+
+# Explicitly set these because none case only has QT_GENERATE_SBOM passed as OFF.
+# In this case, the defaults for the formats is to remain ON.
+if(NOT QT_GENERATE_SBOM)
+ set(original_QT_SBOM_GENERATE_SPDX_V2 ON)
+ set(original_QT_SBOM_GENERATE_CYDX_V1_6 ON)
+endif()
diff --git a/tests/auto/cmake/RunCMake/Sbom/full.cmake b/tests/auto/cmake/RunCMake/Sbom/full.cmake
index 7241b8e77a8..b5ac77c3ccc 100644
--- a/tests/auto/cmake/RunCMake/Sbom/full.cmake
+++ b/tests/auto/cmake/RunCMake/Sbom/full.cmake
@@ -1,23 +1,30 @@
# Needed to make the sbom functions available.
find_package(Qt6 REQUIRED Core)
+include(CommonResultGenIntro)
+
_qt_internal_setup_sbom(
GENERATE_SBOM_DEFAULT "TRUE"
)
set(IS_FULL_BUILD "TRUE")
+# These are used by common_result_gen.cmake.
+set(SBOM_VERSION "2.0.0")
+set(SBOM_INSTALL_DIR "sbom_full")
+set(SBOM_PROJECT_NAME "${PROJECT_NAME}ProjectFull")
+
_qt_internal_sbom_begin_project(
- SBOM_PROJECT_NAME "${PROJECT_NAME}Project"
+ SBOM_PROJECT_NAME "${SBOM_PROJECT_NAME}"
SUPPLIER "QtProjectTest"
SUPPLIER_URL "https://qt-project.org/SbomTest"
LICENSE_EXPRESSION "LGPL-3.0-only"
COPYRIGHTS "2025 The Qt Company Ltd."
INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}"
- INSTALL_SBOM_DIR "sbom"
+ INSTALL_SBOM_DIR "${SBOM_INSTALL_DIR}"
DOWNLOAD_LOCATION "https://download.qt.io/sbom"
CPE "cpe:2.3:a:qt:qtprojecttest:1.0.0:*:*:*:*:*:*:*"
- VERSION "1.0.0"
+ VERSION "${SBOM_VERSION}"
DOCUMENT_CREATOR_TOOL "Test Build System Tool"
LICENSE_DIR_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/custom_licenses"
)
@@ -26,3 +33,7 @@ include(common_targets.cmake)
_qt_internal_sbom_end_project()
+include(CommonResultGen)
+
+# Also create separate sboms for some sibling projects under this subdir.
+add_subdirectory(subprojects)
diff --git a/tests/auto/cmake/RunCMake/Sbom/minimal.cmake b/tests/auto/cmake/RunCMake/Sbom/minimal.cmake
index 4422314b2d5..27dfc0a3b12 100644
--- a/tests/auto/cmake/RunCMake/Sbom/minimal.cmake
+++ b/tests/auto/cmake/RunCMake/Sbom/minimal.cmake
@@ -1,17 +1,26 @@
# Needed to make the sbom functions available.
find_package(Qt6 REQUIRED Core)
+include(CommonResultGenIntro)
+
_qt_internal_setup_sbom(
GENERATE_SBOM_DEFAULT "TRUE"
)
+# This is used by common_result_gen.cmake.
+set(SBOM_VERSION "1.0.0")
+
_qt_internal_sbom_begin_project(
SUPPLIER "QtProjectTest"
SUPPLIER_URL "https://qt-project.org/SbomTest"
- VERSION "1.0.0"
+ VERSION "${SBOM_VERSION}"
)
include(common_targets.cmake)
_qt_internal_sbom_end_project()
+include(CommonResultGen)
+
+# Also create separate sboms for some sibling projects under this subdir.
+add_subdirectory(subprojects)
diff --git a/tests/auto/cmake/RunCMake/Sbom/subprojects/CMakeLists.txt b/tests/auto/cmake/RunCMake/Sbom/subprojects/CMakeLists.txt
new file mode 100644
index 00000000000..0d9e12534df
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/subprojects/CMakeLists.txt
@@ -0,0 +1,5 @@
+# These subprojects look up the same system library dependency separately in each subdirectory
+# scope to ensure that we simulate the same scenario as in a top-level qt5.git build, so that
+# we try to reuse the same system library entity in multiple sboms.
+add_subdirectory(subproj1)
+add_subdirectory(subproj2)
diff --git a/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/CMakeLists.txt b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/CMakeLists.txt
new file mode 100644
index 00000000000..a59908d8889
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/CMakeLists.txt
@@ -0,0 +1,51 @@
+project(subproj1)
+
+# This is used by common_result_gen.cmake.
+set(SBOM_VERSION "1.0.1")
+set(SBOM_PROJECT_NAME "${PROJECT_NAME}")
+
+_qt_internal_sbom_begin_project(
+ SBOM_PROJECT_NAME "${SBOM_PROJECT_NAME}"
+ INSTALL_SBOM_DIR "${SBOM_INSTALL_DIR}"
+ SUPPLIER "QtProjectTest"
+ SUPPLIER_URL "https://qt-project.org/SbomTest"
+ VERSION "${SBOM_VERSION}"
+)
+
+add_library(subproj1_helper STATIC)
+target_sources(subproj1_helper PRIVATE subproj1_helper.cpp)
+target_link_libraries(subproj1_helper)
+install(TARGETS subproj1_helper
+ RUNTIME DESTINATION bin
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+)
+_qt_internal_add_sbom(subproj1_helper
+ TYPE "LIBRARY"
+ RUNTIME_PATH bin
+ ARCHIVE_PATH lib
+ LIBRARY_PATH lib
+)
+
+
+# This will actually refer to the root project Threads, that gets brought in via Qt6::Core
+# dependency.
+find_package(Threads)
+if(TARGET Threads::Threads)
+ _qt_internal_add_sbom(Threads::Threads
+ TYPE SYSTEM_LIBRARY
+ )
+ target_link_libraries(subproj1_helper PRIVATE Threads::Threads)
+endif()
+
+# Create another IMPORTED target that is meant to simulate a system library, so we don't refer to
+# a target that exists in the root scope.
+add_library(FancySystemLib IMPORTED INTERFACE)
+_qt_internal_add_sbom(FancySystemLib
+ TYPE SYSTEM_LIBRARY
+)
+target_link_libraries(subproj1_helper PRIVATE FancySystemLib)
+
+_qt_internal_sbom_end_project()
+
+include(CommonResultGen)
diff --git a/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/subproj1_helper.cpp b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/subproj1_helper.cpp
new file mode 100644
index 00000000000..f6b17d4b9f8
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj1/subproj1_helper.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+int func() { return 0; };
diff --git a/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/CMakeLists.txt b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/CMakeLists.txt
new file mode 100644
index 00000000000..0a408ddf47f
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/CMakeLists.txt
@@ -0,0 +1,46 @@
+project(subproj2)
+
+# This is used by common_result_gen.cmake.
+set(SBOM_VERSION "1.0.2")
+set(SBOM_PROJECT_NAME "${PROJECT_NAME}")
+
+_qt_internal_sbom_begin_project(
+ SBOM_PROJECT_NAME "${SBOM_PROJECT_NAME}"
+ INSTALL_SBOM_DIR "${SBOM_INSTALL_DIR}"
+ SUPPLIER "QtProjectTest"
+ SUPPLIER_URL "https://qt-project.org/SbomTest"
+ VERSION "${SBOM_VERSION}"
+)
+
+add_library(subproj2_helper STATIC)
+target_sources(subproj2_helper PRIVATE subproj2_helper.cpp)
+target_link_libraries(subproj2_helper)
+install(TARGETS subproj2_helper
+ RUNTIME DESTINATION bin
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+)
+_qt_internal_add_sbom(subproj2_helper
+ TYPE "LIBRARY"
+ RUNTIME_PATH bin
+ ARCHIVE_PATH lib
+ LIBRARY_PATH lib
+)
+
+find_package(Threads)
+if(TARGET Threads::Threads)
+ _qt_internal_add_sbom(Threads::Threads
+ TYPE SYSTEM_LIBRARY
+ )
+ target_link_libraries(subproj2_helper PRIVATE Threads::Threads)
+endif()
+
+add_library(FancySystemLib IMPORTED INTERFACE)
+_qt_internal_add_sbom(FancySystemLib
+ TYPE SYSTEM_LIBRARY
+)
+target_link_libraries(subproj2_helper PRIVATE FancySystemLib)
+
+_qt_internal_sbom_end_project()
+
+include(CommonResultGen)
diff --git a/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/subproj2_helper.cpp b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/subproj2_helper.cpp
new file mode 100644
index 00000000000..f6b17d4b9f8
--- /dev/null
+++ b/tests/auto/cmake/RunCMake/Sbom/subprojects/subproj2/subproj2_helper.cpp
@@ -0,0 +1,4 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+int func() { return 0; };
diff --git a/tests/auto/corelib/global/qcheckedint/tst_qcheckedint.cpp b/tests/auto/corelib/global/qcheckedint/tst_qcheckedint.cpp
index fe509e558c6..72175f8e04c 100644
--- a/tests/auto/corelib/global/qcheckedint/tst_qcheckedint.cpp
+++ b/tests/auto/corelib/global/qcheckedint/tst_qcheckedint.cpp
@@ -347,7 +347,7 @@ void tst_QCheckedInt::division()
// This causes an internal compiler error on MSVC, so skipping it there
// Integrity's compiler says this code isn't constexpr.
-#if (!defined(Q_CC_MSVC) || Q_CC_MSVC > 1944) && !defined(Q_CC_GHS)
+#if (!defined(Q_CC_MSVC) || Q_CC_MSVC > 1950) && !defined(Q_CC_GHS)
template <typename T>
constexpr bool checkedIntTypeProperties()
diff --git a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
index bbe355061eb..0474ad974cb 100644
--- a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
+++ b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
@@ -17,15 +17,26 @@ class tst_QGetPutEnv : public QObject
{
Q_OBJECT
private slots:
+ void init();
+
void getSetCheck();
void encoding();
void intValue_data();
void intValue();
+
+private:
+ QByteArray uniqueEnvVarName;
};
+void tst_QGetPutEnv::init()
+{
+ QUuid uuid = QUuid::createUuid();
+ uniqueEnvVarName = "QT_TEST_ENV_VAR_" + uuid.toByteArray(QUuid::Id128);
+}
+
void tst_QGetPutEnv::getSetCheck()
{
- const char varName[] = "should_not_exist";
+ const char *varName = uniqueEnvVarName.constData();
bool ok;
@@ -117,13 +128,14 @@ void tst_QGetPutEnv::encoding()
// The LATIN SMALL LETTER A WITH ACUTE is NFC for NFD:
// U+0061 U+0301 LATIN SMALL LETTER A + COMBINING ACUTE ACCENT
- const char varName[] = "should_not_exist";
+ const char *varName = uniqueEnvVarName.constData();
+
static const wchar_t rawvalue[] = { 'a', 0x00E1, 0x03B1, 0x0430, 0 };
QString value = QString::fromWCharArray(rawvalue);
#if defined(Q_OS_WIN)
- const wchar_t wvarName[] = L"should_not_exist";
- _wputenv_s(wvarName, rawvalue);
+ std::wstring wvarName = QString::fromUtf8(varName).toStdWString();
+ _wputenv_s(wvarName.data(), rawvalue);
#else
// confirm the locale is UTF-8
if (value.toLocal8Bit() != "a\xc3\xa1\xce\xb1\xd0\xb0")
@@ -203,7 +215,7 @@ void tst_QGetPutEnv::intValue_data()
void tst_QGetPutEnv::intValue()
{
const int maxlen = (sizeof(qint64) * CHAR_BIT + 2) / 3;
- const char varName[] = "should_not_exist";
+ const char *varName = uniqueEnvVarName.constData();
QFETCH(QByteArray, value);
QFETCH(qint64, expected);
diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
index 0b65673c393..bbcc8c5e5e1 100644
--- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
+++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
@@ -464,7 +464,7 @@ Q_COREAPP_STARTUP_FUNCTION(myStartupFunc)
void tst_QGlobal::qCoreAppStartupFunction()
{
- QCOMPARE(qStartupFunctionValue, 0);
+ qStartupFunctionValue = 0;
int argc = 1;
char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
QCoreApplication app(argc, argv);
diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
index d2fee5124db..34c0fd11bed 100644
--- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
+++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
@@ -1011,7 +1011,7 @@ SUB_OVERFLOW_UNSIGNED_TYPE_TEST(ulong, ULONG_MAX)
#if defined(QT_HAS_128_BIT_MULTIPLICATION)
// Compiling this causes an ICE in MSVC, so skipping it
-#if !defined(Q_CC_MSVC) || Q_CC_MSVC > 1944
+#if !defined(Q_CC_MSVC) || Q_CC_MSVC > 1950
SIGNED_TYPE_TEST(qlonglong, LLONG_MIN, LLONG_MAX)
UNSIGNED_TYPE_TEST(qulonglong, ULLONG_MAX)
#endif
diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
index a32045bbbb1..f9a77e05de9 100644
--- a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
+++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
@@ -304,11 +304,9 @@ void tst_QRandomGenerator::generate32_data()
QTest::newRow("fixed") << (RandomValue32 & RandomDataMask);
QTest::newRow("global") << 0U;
#ifdef QT_BUILD_INTERNAL
- if (qHasHwrng())
- QTest::newRow("hwrng") << uint(UseSystemRNG);
- QTest::newRow("system") << uint(UseSystemRNG | SkipHWRNG);
+ QTest::newRow("system") << uint(UseSystemRNG);
# ifdef HAVE_FALLBACK_ENGINE
- QTest::newRow("system-fallback") << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG);
+ QTest::newRow("system-fallback") << uint(UseSystemRNG | SkipSystemRNG);
# endif
#endif
}
@@ -553,7 +551,7 @@ void tst_QRandomGenerator::bounded()
QCOMPARE(ivalue, int(expected));
// confirm only the bound now
- setRNGControl(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
+ setRNGControl(control & (SkipSystemRNG|UseSystemRNG));
value = rng.bounded(sup);
QVERIFY(value < sup);
@@ -685,7 +683,7 @@ void tst_QRandomGenerator::bounded64()
QCOMPARE(ivalue, int(expected));
// confirm only the bound now
- setRNGControl(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
+ setRNGControl(control & (SkipSystemRNG|UseSystemRNG));
value = rng.bounded(sup);
QVERIFY(value < sup);
@@ -810,11 +808,9 @@ void tst_QRandomGenerator::stdUniformIntDistribution_data()
auto newRow = [&](quint32 max) {
#ifdef QT_BUILD_INTERNAL
- if (qHasHwrng())
- QTest::addRow("hwrng:%u", max) << uint(UseSystemRNG) << max;
- QTest::addRow("system:%u", max) << uint(UseSystemRNG | SkipHWRNG) << max;
+ QTest::addRow("system:%u", max) << uint(UseSystemRNG) << max;
# ifdef HAVE_FALLBACK_ENGINE
- QTest::addRow("system-fallback:%u", max) << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG) << max;
+ QTest::addRow("system-fallback:%u", max) << uint(UseSystemRNG | SkipSystemRNG) << max;
# endif
#endif
QTest::addRow("global:%u", max) << 0U << max;
@@ -925,11 +921,9 @@ void tst_QRandomGenerator::stdUniformRealDistribution_data()
auto newRow = [&](double min, double sup) {
#ifdef QT_BUILD_INTERNAL
- if (qHasHwrng())
- QTest::addRow("hwrng:%g-%g", min, sup) << uint(UseSystemRNG) << min << sup;
- QTest::addRow("system:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG) << min << sup;
+ QTest::addRow("system:%g-%g", min, sup) << uint(UseSystemRNG) << min << sup;
# ifdef HAVE_FALLBACK_ENGINE
- QTest::addRow("system-fallback:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG) << min << sup;
+ QTest::addRow("system-fallback:%g-%g", min, sup) << uint(UseSystemRNG | SkipSystemRNG) << min << sup;
# endif
#endif
QTest::addRow("global:%g-%g", min, sup) << 0U << min << sup;
@@ -948,7 +942,7 @@ void tst_QRandomGenerator::stdUniformRealDistribution()
QFETCH(uint, control);
QFETCH(double, min);
QFETCH(double, sup);
- RandomGenerator rng(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
+ RandomGenerator rng(control & (SkipSystemRNG|UseSystemRNG));
{
QRandomGenerator rd;
diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp
index e0f677e1511..7603d84623b 100644
--- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp
+++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp
@@ -1623,6 +1623,8 @@ void tst_QDebug::threadSafety() const
#ifdef Q_OS_WASM
QSKIP("threadSafety does not run on wasm");
#else
+ s_messages = {};
+
MessageHandlerSetter mhs(threadSafeMessageHandler);
const int numThreads = 10;
QThreadPool::globalInstance()->setMaxThreadCount(numThreads);
diff --git a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp
index 0fd62d40f62..9aa99a1b653 100644
--- a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp
+++ b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp
@@ -186,6 +186,12 @@ private slots:
Q_ASSERT(!qApp); // Rules should not require an app to resolve
+ static bool calledOnce = false;
+ if (calledOnce)
+ QSKIP("QLoggingRegistry_environment can only run once");
+
+ calledOnce = true;
+
qputenv("QT_LOGGING_RULES", "qt.foo.bar=true");
QLoggingCategory qtEnabledByLoggingRule("qt.foo.bar");
QCOMPARE(qtEnabledByLoggingRule.isDebugEnabled(), true);
diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
index 5453237cadb..0d06951fb1e 100644
--- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
+++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
@@ -183,7 +183,6 @@ private slots:
void testIniParsing_data();
void testIniParsing();
- void testEscapes();
void testEscapedKeys_data();
void testEscapedKeys();
void testUnescapedKeys_data();
@@ -192,6 +191,11 @@ private slots:
void testEscapedStringList();
void testUnescapedStringList_data();
void testUnescapedStringList();
+ void testEscapedVariant_data();
+ void testEscapedVariant();
+ void testBadEscape();
+ void testBadEscape_data();
+
void testNormalizedKey_data();
void testNormalizedKey();
void testVariantTypes_data() { populateWithFormats(); }
@@ -2836,48 +2840,6 @@ QString escapeWeirdChars(const QString &s)
}
#ifdef QT_BUILD_INTERNAL
-void tst_QSettings::testEscapes()
-{
- QSettings settings(QSettings::UserScope, "software.org", "KillerAPP");
-
-#define testVariant(val, escStr, func) \
- { \
- QVariant v(val); \
- QString s = QSettingsPrivate::variantToString(v); \
- QCOMPARE(s, escStr); \
- QCOMPARE(QVariant(QSettingsPrivate::stringToVariant(escStr)), v); \
- QVERIFY((val) == v.func()); \
- }
-
-#define testBadEscape(escStr, vStr) \
- { \
- QVariant v = QSettingsPrivate::stringToVariant(QString(escStr)); \
- QCOMPARE(v.toString(), QString(vStr)); \
- }
-
- // streaming qvariant into a string
- testVariant(QString("Hello World!"), QString("Hello World!"), toString);
- testVariant(QString("Hello, World!"), QString("Hello, World!"), toString);
- testVariant(QString("@Hello World!"), QString("@@Hello World!"), toString);
- testVariant(QString("@@Hello World!"), QString("@@@Hello World!"), toString);
- testVariant(QVariant(100), QString("100"), toString);
- testVariant(QStringList() << "ene" << "due" << "rike", QString::fromLatin1("@Variant(\x0\x0\x0\xb\x0\x0\x0\x3\x0\x0\x0\x6\x0\x65\x0n\x0\x65\x0\x0\x0\x6\x0\x64\x0u\x0\x65\x0\x0\x0\x8\x0r\x0i\x0k\x0\x65)", 50), toStringList);
- testVariant(QRect(1, 2, 3, 4), QString("@Rect(1 2 3 4)"), toRect);
- testVariant(QSize(5, 6), QString("@Size(5 6)"), toSize);
- testVariant(QPoint(7, 8), QString("@Point(7 8)"), toPoint);
-
- testBadEscape("", "");
- testBadEscape("@", "@");
- testBadEscape("@@", "@");
- testBadEscape("@@@", "@@");
- testBadEscape(" ", " ");
- testBadEscape("@Rect", "@Rect");
- testBadEscape("@Rect(", "@Rect(");
- testBadEscape("@Rect()", "@Rect()");
- testBadEscape("@Rect)", "@Rect)");
- testBadEscape("@Rect(1 2 3)", "@Rect(1 2 3)");
- testBadEscape("@@Rect(1 2 3)", "@Rect(1 2 3)");
-}
void tst_QSettings::testEscapedKeys_data()
{
@@ -3062,6 +3024,71 @@ void tst_QSettings::testUnescapedStringList()
QCOMPARE(iniUnescapedStringList(reescStrList), plainStrList);
}
+void tst_QSettings::testEscapedVariant_data()
+{
+ QTest::addColumn<QVariant>("val");
+ QTest::addColumn<QString>("escStr");
+
+ // streaming qvariant into a string
+
+ QTest::newRow("Hello World!") << QVariant{u"Hello World!"_s} << u"Hello World!"_s;
+ QTest::newRow("Hello, World!") << QVariant{u"Hello, World!"_s} << u"Hello, World!"_s;
+ QTest::newRow("@Hello World!") << QVariant{u"@Hello World!"_s} << u"@@Hello World!"_s;
+ QTest::newRow("@@Hello World!") << QVariant{u"@@Hello World!"_s} << u"@@@Hello World!"_s;
+ QTest::newRow("int-100") << QVariant{100} << u"100"_s;
+ QTest::newRow("qrect") << QVariant{QRect(1, 2, 3, 4)} << u"@Rect(1 2 3 4)"_s;
+ QTest::newRow("qsize") << QVariant{QSize(5, 6)} << u"@Size(5 6)"_s;
+ QTest::newRow("qpoint") << QVariant{QPoint(7, 8)} << u"@Point(7 8)"_s;
+
+ auto expected = QString::fromLatin1("@Variant(\x0\x0\x0\xb\x0\x0\x0\x3\x0\x0\x0\x6\x0\x65\x0n"
+ "\x0\x65\x0\x0\x0\x6\x0\x64\x0u\x0\x65\x0\x0\x0\x8\x0r\x0"
+ "i\x0k\x0\x65)", 50);
+ QTest::newRow("stringlist") << QVariant{QStringList{u"ene"_s, u"due"_s, u"rike"_s}} << expected;
+}
+
+void tst_QSettings::testEscapedVariant()
+{
+ QFETCH(QVariant, val);
+ QFETCH(QString, escStr);
+
+ QSettings settings(QSettings::UserScope, "example.org", "KillerAPP");
+
+ QString variantAsString = QSettingsPrivate::variantToString(val);
+ QCOMPARE(variantAsString, escStr);
+
+ QVariant stringAsVariant = QSettingsPrivate::stringToVariant(escStr);
+ QCOMPARE(stringAsVariant, val);
+}
+
+void tst_QSettings::testBadEscape_data()
+{
+ QTest::addColumn<QString>("escStr");
+ QTest::addColumn<QString>("variantStr");
+
+ QTest::newRow("empty") << u""_s << u""_s;
+ QTest::newRow("space") << u" "_s << u" "_s;
+ QTest::newRow("@") << u"@"_s << u"@"_s;
+ QTest::newRow("@@") << u"@@"_s << u"@"_s;
+ QTest::newRow("@@@") << u"@@@"_s << u"@@"_s;
+ QTest::newRow("@Rect") << u"@Rect"_s << u"@Rect"_s;
+ QTest::newRow("@Rect(") << u"@Rect("_s << u"@Rect("_s;
+ QTest::newRow("@Rect()") << u"@Rect()"_s << u"@Rect()"_s;
+ QTest::newRow("@Rect)") << u"@Rect)"_s << u"@Rect)"_s;
+
+ QTest::newRow("@Rect(1 2 3)") << u"@Rect(1 2 3)"_s << u"@Rect(1 2 3)"_s;
+ QTest::newRow("@@Rect(1 2 3)") << u"@@Rect(1 2 3)"_s << u"@Rect(1 2 3)"_s;
+}
+
+void tst_QSettings::testBadEscape()
+{
+ QFETCH(QString, escStr);
+ QFETCH(QString, variantStr);
+
+ QSettings settings(QSettings::UserScope, "example.org", "KillerAPP");
+
+ QVariant v = QSettingsPrivate::stringToVariant(escStr);
+ QCOMPARE(v.toString(), variantStr);
+}
#endif
void tst_QSettings::testCaseSensitivity()
diff --git a/tests/auto/corelib/itemmodels/qrangemodel/data.h b/tests/auto/corelib/itemmodels/qrangemodel/data.h
index 0df5b981fd6..c49c7da3d42 100644
--- a/tests/auto/corelib/itemmodels/qrangemodel/data.h
+++ b/tests/auto/corelib/itemmodels/qrangemodel/data.h
@@ -113,20 +113,36 @@ struct QRangeModel::ItemAccess<ItemAccessType>
class Object : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString string READ string WRITE setString)
- Q_PROPERTY(int number READ number WRITE setNumber)
+ Q_PROPERTY(QString string READ string WRITE setString NOTIFY stringChanged)
+ Q_PROPERTY(int number READ number WRITE setNumber NOTIFY numberChanged)
public:
using QObject::QObject;
QString string() const { return m_string; }
- void setString(const QString &string) { m_string = string; }
+ void setString(const QString &string)
+ {
+ if (m_string == string)
+ return;
+ m_string = string;
+ Q_EMIT stringChanged();
+ }
int number() const { return m_number; }
- void setNumber(int number) { m_number = number; }
+ void setNumber(int number)
+ {
+ if (m_number == number)
+ return;
+ m_number = number;
+ Q_EMIT numberChanged(m_number);
+ }
+
+Q_SIGNALS:
+ void stringChanged();
+ void numberChanged(int number);
private:
// note: default values need to be convertible to each other
QString m_string = "1234";
- int m_number = 42;
+ int m_number = -1;
};
// a class that can be both and requires disambiguation
diff --git a/tests/auto/corelib/itemmodels/qrangemodel/tst_qrangemodel.cpp b/tests/auto/corelib/itemmodels/qrangemodel/tst_qrangemodel.cpp
index 6c57a870b27..09522e48084 100644
--- a/tests/auto/corelib/itemmodels/qrangemodel/tst_qrangemodel.cpp
+++ b/tests/auto/corelib/itemmodels/qrangemodel/tst_qrangemodel.cpp
@@ -10,6 +10,8 @@
#include <QtCore/qstringlistmodel.h>
#include <QtTest/qsignalspy.h>
+#include <QtGui/qcolor.h>
+
#if QT_CONFIG(itemmodeltester)
#include <QtTest/qabstractitemmodeltester.h>
#endif
@@ -34,6 +36,8 @@ private slots:
void overrideRoleNames();
void setRoleNames();
void defaultRoleNames();
+ void autoConnectPolicy_data();
+ void autoConnectPolicy();
void dimensions_data() { createTestData(); }
void dimensions();
@@ -730,6 +734,232 @@ void tst_QRangeModel::defaultRoleNames()
}();
}
+class MultiRoleObject : public Object
+{
+public:
+ template <typename Signal>
+ bool isConnected(Signal &&signal) const
+ {
+ return isSignalConnected(QMetaMethod::fromSignal(signal));
+ }
+};
+
+template <>
+struct QRangeModel::RowOptions<MultiRoleObject>
+{
+ static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
+};
+
+void tst_QRangeModel::autoConnectPolicy_data()
+{
+ QTest::addColumn<QRangeModel::AutoConnectPolicy>("policy");
+
+ QTest::addRow("Full") << QRangeModel::AutoConnectPolicy::Full;
+ QTest::addRow("OnRead") << QRangeModel::AutoConnectPolicy::OnRead;
+}
+
+void tst_QRangeModel::autoConnectPolicy()
+{
+ QFETCH(const QRangeModel::AutoConnectPolicy, policy);
+
+ [policy]{
+ QList<MultiRoleObject *> objectList = {
+ new MultiRoleObject,
+ new MultiRoleObject,
+ new MultiRoleObject,
+ };
+ QRangeModel model(&objectList);
+ model.setAutoConnectPolicy(policy);
+ QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged);
+
+ int emissions = 0;
+ objectList[0]->setString("String 0");
+ if (policy == QRangeModel::AutoConnectPolicy::OnRead) {
+ QCOMPARE(dataChangedSpy.size(), emissions);
+ } else {
+ QCOMPARE(dataChangedSpy.size(), ++emissions);
+ QCOMPARE(dataChangedSpy.at(0).at(0), model.index(0, 0));
+ QCOMPARE(dataChangedSpy.at(0).at(1), model.index(0, 0));
+ QCOMPARE(dataChangedSpy.at(0).at(2), QVariant::fromValue(QList<int>{Qt::UserRole}));
+ }
+
+ if (policy == QRangeModel::AutoConnectPolicy::OnRead) {
+ QVERIFY(!objectList.at(1)->isConnected(&Object::stringChanged));
+ QVERIFY(!objectList.at(1)->isConnected(&Object::numberChanged));
+ model.data(model.index(1, 0), Qt::UserRole + 1);
+ QVERIFY(!objectList.at(1)->isConnected(&Object::stringChanged));
+ QVERIFY(objectList.at(1)->isConnected(&Object::numberChanged));
+ model.itemData(model.index(1, 0));
+ QVERIFY(objectList.at(1)->isConnected(&Object::stringChanged));
+ }
+
+ objectList[1]->setNumber(42);
+ QCOMPARE(dataChangedSpy.size(), ++emissions);
+ QCOMPARE(dataChangedSpy.at(emissions - 1).at(0), model.index(1, 0));
+ QCOMPARE(dataChangedSpy.at(emissions - 1).at(1), model.index(1, 0));
+ QCOMPARE(dataChangedSpy.at(emissions - 1).at(2), QVariant::fromValue(QList<int>{Qt::UserRole + 1}));
+
+ QVERIFY(model.insertRow(0));
+ QCOMPARE(objectList.at(1)->isConnected(&Object::numberChanged),
+ policy == QRangeModel::AutoConnectPolicy::Full);
+ QCOMPARE(objectList.at(1)->isConnected(&Object::stringChanged),
+ policy == QRangeModel::AutoConnectPolicy::Full);
+ }();
+
+ [policy]{
+ QList<QList<MultiRoleObject *>> objectTable = {
+ {new MultiRoleObject, new MultiRoleObject},
+ {new MultiRoleObject, new MultiRoleObject},
+ };
+ QRangeModel model(&objectTable);
+ connect(&model, &QRangeModel::rowsInserted,
+ &model, [&objectTable](const QModelIndex &, int first, int last){
+ while (first <= last) {
+ objectTable[first][0] = new MultiRoleObject;
+ objectTable[first][1] = new MultiRoleObject;
+ ++first;
+ }
+ });
+ connect(&model, &QRangeModel::columnsInserted,
+ &model, [&objectTable](const QModelIndex &, int first, int last){
+ for (auto &row : objectTable) {
+ for (int column = first; column <= last; ++column)
+ row[column] = new MultiRoleObject;
+ }
+ });
+ model.setAutoConnectPolicy(policy);
+ QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged);
+
+ objectTable[0][1]->setString("String 0/1");
+ QCOMPARE(dataChangedSpy.size(), policy == QRangeModel::AutoConnectPolicy::Full ? 1 : 0);
+
+ model.insertRows(1, 2);
+ for (const auto &row : std::as_const(objectTable)) {
+ for (const auto &object : row) {
+ QCOMPARE(object->isConnected(&Object::numberChanged),
+ policy == QRangeModel::AutoConnectPolicy::Full);
+ QCOMPARE(object->isConnected(&Object::stringChanged),
+ policy == QRangeModel::AutoConnectPolicy::Full);
+ }
+ }
+
+ model.insertColumn(0);
+ for (const auto &row : std::as_const(objectTable)) {
+ for (const auto &object : row) {
+ QCOMPARE(object->isConnected(&Object::numberChanged),
+ policy == QRangeModel::AutoConnectPolicy::Full);
+ QCOMPARE(object->isConnected(&Object::stringChanged),
+ policy == QRangeModel::AutoConnectPolicy::Full);
+ }
+ }
+ }();
+
+ [policy]{
+ QList<std::tuple<MultiRoleObject *, MultiRoleObject *>> objectTable = {
+ {new MultiRoleObject, new MultiRoleObject},
+ {new MultiRoleObject, new MultiRoleObject},
+ };
+ QRangeModel model(&objectTable);
+ model.setAutoConnectPolicy(policy);
+ QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged);
+
+ if (policy == QRangeModel::AutoConnectPolicy::OnRead) {
+ for (int row = 0; row < model.rowCount(); ++row) {
+ for (int column = 0; column < model.columnCount(); ++column)
+ model.itemData(model.index(row, column));
+ }
+ }
+
+ auto *topRight = get<1>(objectTable[0]);
+ auto *bottomLeft = get<0>(objectTable[1]);
+ topRight->setNumber(52);
+ QCOMPARE(dataChangedSpy.size(), 1);
+
+ QVERIFY(bottomLeft->isConnected(&Object::numberChanged));
+ QVERIFY(bottomLeft->isConnected(&Object::stringChanged));
+ QVERIFY(model.removeRows(1, 1));
+ bottomLeft->setNumber(52); // this will lazily break the connection
+ QVERIFY(!bottomLeft->isConnected(&Object::numberChanged));
+ QVERIFY(bottomLeft->isConnected(&Object::stringChanged));
+ QCOMPARE(dataChangedSpy.size(), 1);
+ bottomLeft->setNumber(53); // this should not crash
+ bottomLeft->setString("No update");
+ QVERIFY(!bottomLeft->isConnected(&Object::stringChanged));
+
+ const QModelIndex index = model.index(0, 0);
+ dataChangedSpy.clear();
+ QVERIFY(model.setData(index, "string", Qt::UserRole));
+ QCOMPARE(dataChangedSpy.count(), 1);
+ QCOMPARE(dataChangedSpy.back().at(2),
+ QVariant::fromValue(QList<int>{Qt::UserRole}));
+ // this will right now emit dataChanged three times:
+ QVERIFY(model.setItemData(index, QMap<int, QVariant>{
+ {Qt::UserRole, QVariant("string")},
+ {Qt::UserRole + 1, QVariant(42)},
+ }));
+ QCOMPARE(dataChangedSpy.count(), 2);
+ QCOMPARE(dataChangedSpy.back().at(2),
+ QVariant::fromValue(QList<int>{Qt::UserRole, Qt::UserRole + 1}));
+ QVERIFY(model.setData(index, 42, Qt::UserRole + 1));
+ QCOMPARE(dataChangedSpy.count(), 3);
+ QCOMPARE(dataChangedSpy.back().at(2),
+ QVariant::fromValue(QList<int>{Qt::UserRole + 1}));
+ }();
+
+ [policy]{
+ using Tree = QList<MultiRoleObject *>;
+ struct ObjectTreeProtocol
+ {
+ const MultiRoleObject *parentRow(const MultiRoleObject &row) const
+ {
+ return static_cast<MultiRoleObject *>(row.parent());
+ }
+ void setParentRow(MultiRoleObject &row, MultiRoleObject *parent)
+ {
+ row.setParent(parent);
+ }
+ const Tree &childRows(const MultiRoleObject &row) const {
+ // don't do that at home...
+ return *reinterpret_cast<const Tree *>(&row.children());
+ }
+ Tree &childRows(const MultiRoleObject &) {
+ empty = {};
+ return empty;
+ }
+ Tree empty;
+ };
+ Tree tree {
+ new MultiRoleObject,
+ new MultiRoleObject,
+ new MultiRoleObject,
+ };
+ tree[0]->setObjectName("root 0");
+ tree[1]->setObjectName("root 1");
+ tree[2]->setObjectName("root 2");
+ auto *child01 = new MultiRoleObject;
+ child01->setObjectName("child 0/1");
+ child01->setParent(tree[0]);
+ (new MultiRoleObject)->setParent(tree[1]);
+ (new MultiRoleObject)->setParent(tree[2]);
+
+ QRangeModel model(std::ref(tree), ObjectTreeProtocol{});
+ QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged);
+ model.setAutoConnectPolicy(policy);
+
+ const QModelIndex root0 = model.index(0, 0);
+ const QModelIndex child01Index = model.index(0, 0, root0);
+ if (policy == QRangeModel::AutoConnectPolicy::OnRead)
+ QVERIFY(model.data(child01Index, Qt::UserRole + 1).isValid());
+
+ child01->setNumber(42);
+ QCOMPARE(dataChangedSpy.size(), 1);
+
+ QCOMPARE(dataChangedSpy.at(0).at(0).value<QModelIndex>(), child01Index);
+ QCOMPARE(dataChangedSpy.at(0).at(1).value<QModelIndex>(), child01Index);
+ QCOMPARE(dataChangedSpy.at(0).at(2), QVariant::fromValue(QList{Qt::UserRole + 1}));
+ }();
+}
+
void tst_QRangeModel::dimensions()
{
QFETCH(Factory, factory);
diff --git a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
index 96bbd60ab83..9a5e2a5f89b 100644
--- a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
+++ b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
@@ -326,7 +326,7 @@ void tst_QChronoTimer::remainingTimeDuringActivation()
if (!singleShot) {
// do it again - see QTBUG-46940
- remainingTime = std::chrono::milliseconds::min();
+ remainingTime = std::chrono::nanoseconds::min();
QVERIFY(timeoutSpy.wait());
QCOMPARE_LE(remainingTime, timeout);
QCOMPARE_GT(remainingTime, 0ns);
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
index ff54a5a6fc4..696bcdc07d7 100644
--- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -2640,29 +2640,27 @@ void tst_QMetaObject::keysToValue()
QCOMPARE(QLatin1String(mf.valueToKeys(3)), QLatin1String("MyFlag1|MyFlag2"));
// Test flags with extra '|'
- QTest::ignoreMessage(QtWarningMsg,
- QRegularExpression(u"QMetaEnum::keysToValue: malformed keys string, ends with '|'.+"_s));
+ static const QRegularExpression endsWithBar(
+ R"(QMetaEnum::keysToValue: malformed keys string, ends with '\|'.+)"_L1);
+ QTest::ignoreMessage(QtWarningMsg, endsWithBar);
QCOMPARE(mf.keysToValue64("MyFlag1|MyFlag2|"), std::nullopt);
- QTest::ignoreMessage(QtWarningMsg,
- QRegularExpression(u"QMetaEnum::keysToValue: malformed keys string, ends with '|'.+"_s));
+ QTest::ignoreMessage(QtWarningMsg, endsWithBar);
QCOMPARE(mf.keysToValue("MyFlag1|MyFlag2|", &ok), -1);
QCOMPARE(ok, false);
- QTest::ignoreMessage(QtWarningMsg,
- QRegularExpression(u"QMetaEnum::keysToValue: malformed keys string, starts with '|'.+"_s));
+ static const QRegularExpression startsWithBar(
+ R"(QMetaEnum::keysToValue: malformed keys string, starts with '\|'.+)"_L1);
+ QTest::ignoreMessage(QtWarningMsg, startsWithBar);
QCOMPARE(mf.keysToValue64("|MyFlag1|MyFlag2|"), std::nullopt);
- QTest::ignoreMessage(QtWarningMsg,
- QRegularExpression(u"QMetaEnum::keysToValue: malformed keys string, starts with '|'.+"_s));
+ QTest::ignoreMessage(QtWarningMsg, startsWithBar);
QCOMPARE(mf.keysToValue("|MyFlag1|MyFlag2|", &ok), -1);
QCOMPARE(ok, false);
- QTest::ignoreMessage(QtWarningMsg,
- QRegularExpression(
- u"QMetaEnum::keysToValue: malformed keys string, has two consecutive '|'.+"_s));
+ static const QRegularExpression doubleBar(
+ R"(QMetaEnum::keysToValue: malformed keys string, has two consecutive '\|'.+)"_L1);
+ QTest::ignoreMessage(QtWarningMsg, doubleBar);
QCOMPARE(mf.keysToValue64("MyFlag1||MyFlag2"), std::nullopt);
- QTest::ignoreMessage(QtWarningMsg,
- QRegularExpression(
- u"QMetaEnum::keysToValue: malformed keys string, has two consecutive '|'.+"_s));
+ QTest::ignoreMessage(QtWarningMsg, doubleBar);
QCOMPARE(mf.keysToValue("MyFlag1||MyFlag2", &ok), -1);
QCOMPARE(ok, false);
diff --git a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
index bde70e32233..10769165ec4 100644
--- a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
+++ b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
@@ -307,6 +307,13 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(EnumFlagsTester::TestFlags)
void tst_QMetaProperty::readAndWriteWithLazyRegistration()
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("lazy registration only runs once per type per process");
+ return;
+ }
+ executedOnce = true;
+
QVERIFY(!QMetaType::fromName("CustomReadObject*").isValid());
QVERIFY(!QMetaType::fromName("CustomWriteObject*").isValid());
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
index 683025fd409..3cf16367777 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
@@ -528,11 +528,25 @@ void tst_QMetaType::qMetaTypeId()
QCOMPARE(::qMetaTypeId<qint8>(), QMetaType::fromType<qint8>().id());
}
+class QPropObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QList<QVariant> prop READ prop WRITE setProp)
+
+public:
+ QPropObject() { propList << 42 << "Hello"; }
+
+ QList<QVariant> prop() const { return propList; }
+ void setProp(const QList<QVariant> &list) { propList = list; }
+ QList<QVariant> propList;
+};
+
void tst_QMetaType::properties()
{
qRegisterMetaType<QList<QVariant> >("QList<QVariant>");
+ QPropObject sut;
- QVariant v = property("prop");
+ QVariant v = sut.property("prop");
QCOMPARE(v.typeName(), "QVariantList");
@@ -542,8 +556,8 @@ void tst_QMetaType::properties()
values << 43 << "world";
- QVERIFY(setProperty("prop", values));
- v = property("prop");
+ QVERIFY(sut.setProperty("prop", values));
+ v = sut.property("prop");
QCOMPARE(v.toList().size(), 4);
}
@@ -1450,6 +1464,11 @@ void tst_QMetaType::defaultConstructTrivial_QTBUG_109594()
void tst_QMetaType::typedConstruct()
{
+ static bool calledOnce = false;
+ if (calledOnce)
+ QSKIP("tst_QMetaType::typedConstruct can only run once");
+ calledOnce = true;
+
auto testMetaObjectWriteOnGadget = [](QVariant &gadget, const QList<GadgetPropertyType> &properties)
{
auto metaObject = QMetaType(gadget.userType()).metaObject();
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
index 6f218bb0651..e9a177356e6 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
@@ -25,7 +25,6 @@ struct MessageHandlerCustom : public MessageHandler
class tst_QMetaType: public QObject
{
Q_OBJECT
- Q_PROPERTY(QList<QVariant> prop READ prop WRITE setProp)
public:
struct GadgetPropertyType {
@@ -34,14 +33,8 @@ public:
QVariant testData;
};
- tst_QMetaType() { propList << 42 << "Hello"; }
-
- QList<QVariant> prop() const { return propList; }
- void setProp(const QList<QVariant> &list) { propList = list; }
-
private:
void registerGadget(const char * name, const QList<GadgetPropertyType> &gadgetProperties);
- QList<QVariant> propList;
private slots:
#if QT_CONFIG(thread)
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
index 661c1f6072b..93f23259745 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
@@ -148,6 +148,11 @@ void registerCustomTypeConversions()
void tst_QMetaType::convertCustomType_data()
{
+ static bool calledOnce = false;
+ if (calledOnce)
+ QSKIP("convertCustomType can only run once");
+ calledOnce = true;
+
customTypeNotYetConvertible();
registerCustomTypeConversions();
@@ -363,6 +368,11 @@ void tst_QMetaType::compareCustomEqualOnlyType()
void tst_QMetaType::customDebugStream()
{
+ static bool calledOnce = false;
+ if (calledOnce)
+ QSKIP("customDebugStream can only run once");
+ calledOnce = true;
+
MessageHandlerCustom handler(::qMetaTypeId<CustomDebugStreamableType>());
QVariant v1 = QVariant::fromValue(CustomDebugStreamableType());
handler.expectedMessage = "QVariant(CustomDebugStreamableType, string-content)";
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index afb0bf0169a..6d9f7d12dcb 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -8736,6 +8736,9 @@ QT_END_NAMESPACE
void tst_QObject::emitToDestroyedClass()
{
using namespace EmitToDestroyedClass;
+ assertionCallCount = 0;
+ wouldHaveAssertedCount = 0;
+
std::unique_ptr ptr = std::make_unique<Derived>();
QObject::connect(ptr.get(), &Base::theSignal, ptr.get(), &Derived::doNothing);
QCOMPARE(assertionCallCount, 0);
diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
index 81316b061d0..98c184872f9 100644
--- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
+++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
@@ -1202,10 +1202,10 @@ void tst_QTimer::singleShotDestructionBeforeEventDispatcher()
// would be deleted by the QObject destructor, which is too late to
// unregister the timer.
- auto thr = QThread::create([] {
+ std::unique_ptr<QThread> thr{QThread::create([] {
QObject o;
QTimer::singleShot(300s, &o, [] {});
- });
+ })};
thr->start();
thr->wait();
}
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index e713901101c..721d3d3af98 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -432,6 +432,9 @@ private slots:
void get_QTransform() { get_impl(QTransform{1, 2, 3, 4, 5, 6, 7, 8, 9}); } // too large
void get_NonDefaultConstructible();
+ void reference();
+ void pointer();
+
private:
using StdVariant = std::variant<std::monostate,
// list here all the types with which we instantiate getIf_impl:
@@ -6213,6 +6216,73 @@ void tst_QVariant::get_NonDefaultConstructible()
get_impl(NonDefaultConstructible{42});
}
+struct QVariantWrapper
+{
+public:
+ static constexpr bool canNoexceptConvertToQVariant
+ = std::is_nothrow_copy_constructible_v<QVariant>;
+ static constexpr bool canNoexceptAssignQVariant
+ = std::is_nothrow_copy_assignable_v<QVariant>;
+
+ QVariantWrapper(QVariant *content = nullptr) noexcept : m_content(content) {}
+
+ QVariant content() const noexcept(canNoexceptConvertToQVariant) { return *m_content; }
+ void setContent(const QVariant &content) noexcept(canNoexceptAssignQVariant)
+ {
+ *m_content = content;
+ }
+
+private:
+ QVariant *m_content = nullptr;
+};
+
+QT_BEGIN_NAMESPACE
+template<>
+QVariant::ConstReference<QVariantWrapper>::operator QVariant() const
+ noexcept(QVariantWrapper::canNoexceptConvertToQVariant)
+{
+ return m_referred.content();
+}
+
+template<>
+QVariant::Reference<QVariantWrapper> &QVariant::Reference<QVariantWrapper>::operator=(
+ const QVariant &content) noexcept(QVariantWrapper::canNoexceptAssignQVariant)
+{
+ m_referred.setContent(content);
+ return *this;
+}
+QT_END_NAMESPACE
+
+void tst_QVariant::reference()
+{
+ QVariant content(5);
+
+ QVariant::ConstReference<QVariantWrapper> constRef(&content);
+ QCOMPARE(QVariant(constRef), QVariant(5));
+
+ QVariant::Reference<QVariantWrapper> ref(&content);
+ QCOMPARE(QVariant(ref), QVariant(5));
+
+ ref = QVariant(12);
+ QCOMPARE(QVariant(ref), QVariant(12));
+ QCOMPARE(content, QVariant(12));
+}
+
+void tst_QVariant::pointer()
+{
+ QVariant content(5);
+
+ QVariant::ConstPointer<QVariantWrapper> constPtr(&content);
+ QCOMPARE(*constPtr, QVariant(5));
+
+ QVariant::Pointer<QVariantWrapper> ptr(&content);
+ QCOMPARE(*ptr, QVariant(5));
+
+ *ptr = QVariant(12);
+ QCOMPARE(*ptr, QVariant(12));
+ QCOMPARE(content, QVariant(12));
+}
+
template <typename T>
T mutate(const T &t) { return t + t; }
template <>
diff --git a/tests/auto/corelib/platform/android/tst_android.cpp b/tests/auto/corelib/platform/android/tst_android.cpp
index 3db9629d4a3..3665f100a61 100644
--- a/tests/auto/corelib/platform/android/tst_android.cpp
+++ b/tests/auto/corelib/platform/android/tst_android.cpp
@@ -348,7 +348,8 @@ void tst_Android::testFullScreenDimensions()
QVERIFY(display.isValid());
QSize appSize;
- if (QNativeInterface::QAndroidApplication::sdkVersion() >= __ANDROID_API_R__) {
+ const int sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion();
+ if (sdkVersion >= __ANDROID_API_R__) {
using namespace QtJniTypes;
auto windowMetrics = windowManager.callMethod<WindowMetrics>("getCurrentWindowMetrics");
auto bounds = windowMetrics.callMethod<Rect>("getBounds");
@@ -383,7 +384,6 @@ void tst_Android::testFullScreenDimensions()
const auto appContext = activity.callMethod<QtJniTypes::Context>("getApplicationContext");
const auto appInfo = appContext.callMethod<QtJniTypes::ApplicationInfo>("getApplicationInfo");
const int targetSdkVersion = appInfo.getField<jint>("targetSdkVersion");
- const int sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion();
if (sdkVersion >= __ANDROID_API_V__ && targetSdkVersion >= __ANDROID_API_V__) {
expectedWidth = appSize.width();
@@ -440,6 +440,11 @@ void tst_Android::testFullScreenDimensions()
QTRY_COMPARE(screen->availableGeometry().height(), realSize.getField<jint>("y"));
QTRY_COMPARE(screen->geometry().width(), realSize.getField<jint>("x"));
+ // TODO needs fix to work in local and CI on same fashion
+ const bool runsOnCI = qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci");
+ if ((sdkVersion > __ANDROID_API_V__) && runsOnCI)
+ QEXPECT_FAIL("", "Fails on Android 16 (QTBUG-141712).", Continue);
+
QTRY_COMPARE(screen->geometry().height(), realSize.getField<jint>("y"));
widget.showNormal();
}
diff --git a/tests/auto/corelib/plugin/CMakeLists.txt b/tests/auto/corelib/plugin/CMakeLists.txt
index 5518231ace2..82fa9d18749 100644
--- a/tests/auto/corelib/plugin/CMakeLists.txt
+++ b/tests/auto/corelib/plugin/CMakeLists.txt
@@ -7,7 +7,9 @@ endif()
add_subdirectory(quuid)
if(QT_FEATURE_library)
add_subdirectory(qpluginloader)
- add_subdirectory(qlibrary)
+ if(QT_FEATURE_private_tests)
+ add_subdirectory(qlibrary)
+ endif()
endif()
if(QT_BUILD_SHARED_LIBS AND QT_FEATURE_library)
add_subdirectory(qplugin)
diff --git a/tests/auto/corelib/plugin/qlibrary/tst/CMakeLists.txt b/tests/auto/corelib/plugin/qlibrary/tst/CMakeLists.txt
index fc452f37f53..62dd4a06c17 100644
--- a/tests/auto/corelib/plugin/qlibrary/tst/CMakeLists.txt
+++ b/tests/auto/corelib/plugin/qlibrary/tst/CMakeLists.txt
@@ -13,7 +13,7 @@ qt_internal_add_test(tst_qlibrary
SOURCES
../tst_qlibrary.cpp
TESTDATA ${test_data}
- LIBRARIES mylib mylib2
+ LIBRARIES mylib mylib2 Qt::CorePrivate
)
add_dependencies(tst_qlibrary mylib mylib2)
diff --git a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp
index 5ae49ff1707..d708f6def1c 100644
--- a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp
+++ b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp
@@ -6,6 +6,7 @@
#include <qdir.h>
#include <qlibrary.h>
#include <QtCore/QRegularExpression>
+#include <QtCore/private/qlibrary_p.h>
// Helper macros to let us know if some suffixes and prefixes are valid
@@ -162,7 +163,12 @@ void tst_QLibrary::cleanup()
};
for (const auto &entry : libs) {
- do {} while (QLibrary(entry.name, entry.version).unload());
+ bool unloaded = false;
+ do {
+ QLibrary lib(entry.name, entry.version);
+ auto libPrivate = QLibraryPrivate::get(&lib);
+ unloaded = libPrivate->unload();
+ } while (unloaded);
}
}
diff --git a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
index 37e1f744f34..763ec71e277 100644
--- a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
+++ b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
@@ -80,6 +80,14 @@ QSingleton<SingletonObject> IncrementThread::singleton;
void tst_QThreadOnce::sameThread_data()
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("tst_QThreadOnce can only run once");
+ return;
+ }
+ executedOnce = true;
+
+
SingletonObject::runCount = 0;
QTest::addColumn<int>("expectedValue");
@@ -105,6 +113,14 @@ void tst_QThreadOnce::sameThread()
void tst_QThreadOnce::multipleThreads()
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("tst_QThreadOnce can only run once");
+ return;
+ }
+ executedOnce = true;
+
+
#if defined(Q_OS_VXWORKS)
const int NumberOfThreads = 20;
#else
@@ -136,6 +152,13 @@ void tst_QThreadOnce::multipleThreads()
void tst_QThreadOnce::nesting()
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("tst_QThreadOnce can only run once");
+ return;
+ }
+ executedOnce = true;
+
int variable = 0;
Q_ONCE {
Q_ONCE {
@@ -148,6 +171,14 @@ void tst_QThreadOnce::nesting()
static void reentrant(int control, int &counter)
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("tst_QThreadOnce can only run once");
+ return;
+ }
+ executedOnce = true;
+
+
Q_ONCE {
if (counter)
reentrant(--control, counter);
@@ -159,6 +190,13 @@ static void reentrant(int control, int &counter)
void tst_QThreadOnce::reentering()
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("tst_QThreadOnce can only run once");
+ return;
+ }
+ executedOnce = true;
+
const int WantedRecursions = 5;
int count = 0;
SingletonObject::runCount = 0;
@@ -181,6 +219,14 @@ static void exception_helper(int &val)
#ifndef QT_NO_EXCEPTIONS
void tst_QThreadOnce::exception()
{
+ static bool executedOnce = false;
+ if (executedOnce) {
+ QSKIP("tst_QThreadOnce can only run once");
+ return;
+ }
+ executedOnce = true;
+
+
int count = 0;
try {
diff --git a/tests/auto/corelib/time/CMakeLists.txt b/tests/auto/corelib/time/CMakeLists.txt
index 1fb95e9245c..29e882429f4 100644
--- a/tests/auto/corelib/time/CMakeLists.txt
+++ b/tests/auto/corelib/time/CMakeLists.txt
@@ -8,6 +8,7 @@ if(QT_FEATURE_datetimeparser)
add_subdirectory(qdatetimeparser)
endif()
add_subdirectory(qtime)
-if(QT_FEATURE_timezone)
- add_subdirectory(qtimezone)
+add_subdirectory(qtimezone)
+if(QT_FEATURE_timezone AND QT_FEATURE_developer_build)
+ add_subdirectory(qtimezonebackend)
endif()
diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
index fde06d2edd9..1e9aeeef799 100644
--- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
+++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
@@ -1262,8 +1262,7 @@ void tst_QDateTime::toString_strformat()
QCOMPARE(testDateTime.toString(u"yyyy-MM-dd hh:mm:ss tt"), u"2013-01-01 01:02:03 +0000"_s);
QCOMPARE(testDateTime.toString(u"yyyy-MM-dd hh:mm:ss ttt"), u"2013-01-01 01:02:03 +00:00"_s);
-#if QT_CONFIG(icu) && !defined(Q_STL_DINKUMWARE)
- // The Dinkum (VxWorks) exception may just be because it has an old ICU.
+#if QT_CONFIG(icu)
// Hopefully other timezone backends shall eventually agree with this:
const QString longForm = u"2013-01-01 01:02:03 Coordinated Universal Time"_s;
#else
diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp
index 66fbac97977..d1d86a4802f 100644
--- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp
+++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp
@@ -3,7 +3,9 @@
#include <QTest>
#include <qtimezone.h>
-#include <private/qtimezoneprivate_p.h>
+#if QT_CONFIG(timezone)
+# include <private/qtimezoneprivate_p.h>
+#endif
#include <private/qcomparisontesthelper_p.h>
#include <qlocale.h>
@@ -51,33 +53,21 @@ private Q_SLOTS:
void checkOffset();
void stressTest();
void windowsId();
- void isValidId_data();
- void isValidId();
void serialize();
void malformed();
- // Backend tests
+ // Backend tests (see also ../qtimezonebackend/)
void utcTest();
- void icuTest();
- void tzTest();
- void macTest();
void darwinTypes();
- void winTest();
void localeSpecificDisplayName_data();
void localeSpecificDisplayName();
- void roundtripDisplayNames_data();
- void roundtripDisplayNames();
+ // Compatibility with std::chrono::tzdb:
void stdCompatibility_data();
void stdCompatibility();
-#endif // timezone backends
+#endif // timezone backends exist
private:
- void printTimeZone(const QTimeZone &tz);
#if QT_CONFIG(timezone)
-# if defined(QT_BUILD_INTERNAL)
- // Generic tests of privates, called by implementation-specific private tests:
- void testCetPrivate(const QTimeZonePrivate &tzp);
- void testEpochTranPrivate(const QTimeZonePrivate &tzp);
-# endif // QT_BUILD_INTERNAL
+ void printTimeZone(const QTimeZone &tz);
// Where tzdb contains a link between zones in different territories, CLDR
// doesn't treat those as aliases for one another. For details see "Links in
// the tz database" at:
@@ -102,6 +92,7 @@ private:
static constexpr bool debug = false;
};
+#if QT_CONFIG(timezone)
void tst_QTimeZone::printTimeZone(const QTimeZone &tz)
{
QDateTime now = QDateTime::currentDateTime();
@@ -167,9 +158,11 @@ void tst_QTimeZone::printTimeZone(const QTimeZone &tz)
qDebug() << "Transition before 1 Jun = " << tz.previousTransition(jun).atUtc;
qDebug() << "";
}
+#endif // feature timezone
void tst_QTimeZone::createTest()
{
+#if QT_CONFIG(timezone)
const QTimeZone tz("Pacific/Auckland");
if constexpr (debug)
@@ -265,13 +258,16 @@ void tst_QTimeZone::createTest()
QCOMPARE(result.at(i).daylightTimeOffset, expected.at(i).daylightTimeOffset);
}
}
+#else
+ QSKIP("Test depends on backends, enabled by feature timezone");
+#endif // feature timezone
}
void tst_QTimeZone::nullTest()
{
QTimeZone nullTz1;
QTimeZone nullTz2;
- QTimeZone utc("UTC");
+ QTimeZone utc(QTimeZone::UTC);
// Validity tests
QCOMPARE(nullTz1.isValid(), false);
@@ -290,6 +286,7 @@ void tst_QTimeZone::nullTest()
utc = nullTz1;
QCOMPARE(utc.isValid(), false);
+#if QT_CONFIG(timezone)
QCOMPARE(nullTz1.id(), QByteArray());
QCOMPARE(nullTz1.territory(), QLocale::AnyTerritory);
QCOMPARE(nullTz1.comment(), QString());
@@ -335,6 +332,7 @@ void tst_QTimeZone::nullTest()
QCOMPARE(data.offsetFromUtc, invalidOffset);
QCOMPARE(data.standardTimeOffset, invalidOffset);
QCOMPARE(data.daylightTimeOffset, invalidOffset);
+#endif // feature timezone
}
void tst_QTimeZone::assign()
@@ -487,7 +485,7 @@ void tst_QTimeZone::offset()
void tst_QTimeZone::dataStreamTest()
{
-#ifndef QT_NO_DATASTREAM
+#if QT_CONFIG(timezone) && !defined(QT_NO_DATASTREAM)
// Test the OffsetFromUtc backend serialization. First with a custom timezone:
QTimeZone tz1("QST"_ba, 23456,
u"Qt Standard Time"_s, u"QST"_s, QLocale::Norway, u"Qt Testing"_s);
@@ -546,7 +544,7 @@ void tst_QTimeZone::dataStreamTest()
QCOMPARE(ds.status(), QDataStream::Ok);
}
QCOMPARE(tz2.id(), tz1.id());
-#endif
+#endif // feature timezone and enabled datastream
}
#if QT_CONFIG(timezone)
@@ -791,7 +789,7 @@ void tst_QTimeZone::hasAlternativeName()
void tst_QTimeZone::specificTransition_data()
{
-#if QT_CONFIG(timezone) && QT_CONFIG(timezone_tzdb) && defined(__GLIBCXX__)
+#if QT_CONFIG(timezone_tzdb) && defined(__GLIBCXX__)
QSKIP("libstdc++'s C++20 misreads the IANA DB for Moscow's transitions (among others).");
#endif
#if defined Q_OS_ANDROID && !QT_CONFIG(timezone_tzdb)
@@ -1159,120 +1157,6 @@ void tst_QTimeZone::windowsId()
}
}
-void tst_QTimeZone::isValidId_data()
-{
-#ifdef QT_BUILD_INTERNAL
- QTest::addColumn<QByteArray>("input");
- QTest::addColumn<bool>("valid");
-
- // a-z, A-Z, 0-9, '.', '-', '_' are valid chars
- // Can't start with '-'
- // Parts separated by '/', each part min 1 and max of 14 chars
- // (Android has parts with lengths up to 17, so tolerates this as a special case.)
-#define TESTSET(name, section, valid) \
- QTest::newRow(name " front") << QByteArray(section "/xyz/xyz") << valid; \
- QTest::newRow(name " middle") << QByteArray("xyz/" section "/xyz") << valid; \
- QTest::newRow(name " back") << QByteArray("xyz/xyz/" section) << valid
-
- // a-z, A-Z, 0-9, '.', '-', '_' are valid chars
- // Can't start with '-'
- // Parts separated by '/', each part min 1 and max of 14 chars
- TESTSET("empty", "", false);
- TESTSET("minimal", "m", true);
-#if (defined(Q_OS_ANDROID) || QT_CONFIG(icu)) && !QT_CONFIG(timezone_tzdb)
- TESTSET("maximal", "East-Saskatchewan", true); // Android actually uses this
- TESTSET("too long", "North-Saskatchewan", false); // ... but thankfully not this.
-#else
- TESTSET("maximal", "12345678901234", true);
- TESTSET("maximal twice", "12345678901234/12345678901234", true);
- TESTSET("too long", "123456789012345", false);
- TESTSET("too-long/maximal", "123456789012345/12345678901234", false);
- TESTSET("maximal/too-long", "12345678901234/123456789012345", false);
-#endif
-
- TESTSET("bad hyphen", "-hyphen", false);
- TESTSET("good hyphen", "hy-phen", true);
-
- TESTSET("valid char _", "_", true);
- TESTSET("valid char .", ".", true);
- TESTSET("valid char :", ":", true);
- TESTSET("valid char +", "+", true);
- TESTSET("valid char A", "A", true);
- TESTSET("valid char Z", "Z", true);
- TESTSET("valid char a", "a", true);
- TESTSET("valid char z", "z", true);
- TESTSET("valid char 0", "0", true);
- TESTSET("valid char 9", "9", true);
-
- TESTSET("valid pair az", "az", true);
- TESTSET("valid pair AZ", "AZ", true);
- TESTSET("valid pair 09", "09", true);
- TESTSET("valid pair .z", ".z", true);
- TESTSET("valid pair _z", "_z", true);
- TESTSET("invalid pair -z", "-z", false);
-
- TESTSET("valid triple a/z", "a/z", true);
- TESTSET("valid triple a.z", "a.z", true);
- TESTSET("valid triple a-z", "a-z", true);
- TESTSET("valid triple a_z", "a_z", true);
- TESTSET("invalid triple a z", "a z", false);
- TESTSET("invalid triple a\\z", "a\\z", false);
- TESTSET("invalid triple a,z", "a,z", false);
-
- TESTSET("invalid space", " ", false);
- TESTSET("invalid char ^", "^", false);
- TESTSET("invalid char \"", "\"", false);
- TESTSET("invalid char $", "$", false);
- TESTSET("invalid char %", "%", false);
- TESTSET("invalid char &", "&", false);
- TESTSET("invalid char (", "(", false);
- TESTSET("invalid char )", ")", false);
- TESTSET("invalid char =", "=", false);
- TESTSET("invalid char -", "-", false);
- TESTSET("invalid char ?", "?", false);
- TESTSET("invalid char ß", "ß", false);
- TESTSET("invalid char \\x01", "\x01", false);
- TESTSET("invalid char ' '", " ", false);
-
-#undef TESTSET
-
- QTest::newRow("az alone") << QByteArray("az") << true;
- QTest::newRow("AZ alone") << QByteArray("AZ") << true;
- QTest::newRow("09 alone") << QByteArray("09") << true;
- QTest::newRow("a/z alone") << QByteArray("a/z") << true;
- QTest::newRow("a.z alone") << QByteArray("a.z") << true;
- QTest::newRow("a-z alone") << QByteArray("a-z") << true;
- QTest::newRow("a_z alone") << QByteArray("a_z") << true;
- QTest::newRow(".z alone") << QByteArray(".z") << true;
- QTest::newRow("_z alone") << QByteArray("_z") << true;
- QTest::newRow("a z alone") << QByteArray("a z") << false;
- QTest::newRow("a\\z alone") << QByteArray("a\\z") << false;
- QTest::newRow("a,z alone") << QByteArray("a,z") << false;
- QTest::newRow("/z alone") << QByteArray("/z") << false;
- QTest::newRow("-z alone") << QByteArray("-z") << false;
-#if (defined(Q_OS_ANDROID) || QT_CONFIG(icu)) && !QT_CONFIG(timezone_tzdb)
- QTest::newRow("long alone") << QByteArray("12345678901234567") << true;
- QTest::newRow("over-long alone") << QByteArray("123456789012345678") << false;
-#else
- QTest::newRow("long alone") << QByteArray("12345678901234") << true;
- QTest::newRow("over-long alone") << QByteArray("123456789012345") << false;
-#endif
-
-#else
- QSKIP("This test requires a Qt -developer-build.");
-#endif // QT_BUILD_INTERNAL
-}
-
-void tst_QTimeZone::isValidId()
-{
-#ifdef QT_BUILD_INTERNAL
- QFETCH(QByteArray, input);
- QFETCH(bool, valid);
-
- QCOMPARE(QTimeZonePrivate::isValidId(input), valid);
-#endif
-}
-
void tst_QTimeZone::serialize()
{
int parts = 0;
@@ -1365,7 +1249,7 @@ void tst_QTimeZone::utcTest()
tz = QTimeZone(15 * 3600); // no IANA ID, so uses minimal id, skipping :00 minutes
QVERIFY(tz.isValid());
- QCOMPARE(tz.id(), "UTC+15");
+ QCOMPARE(tz.id(), "UTC+15:00");
QCOMPARE(tz.offsetFromUtc(now), 15 * 3600);
QCOMPARE(tz.standardTimeOffset(now), 15 * 3600);
QCOMPARE(tz.daylightTimeOffset(now), 0);
@@ -1402,314 +1286,6 @@ void tst_QTimeZone::utcTest()
QCOMPARE(tz.daylightTimeOffset(now), 0);
}
-// Relies on local variable names: zone tzp and locale enUS.
-#define ZONE_DNAME_CHECK(type, name, val) \
- QCOMPARE(tzp.displayName(QTimeZone::type, QTimeZone::name, enUS), val);
-
-void tst_QTimeZone::icuTest()
-{
-#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(icu) && !QT_CONFIG(timezone_tzdb) && !defined(Q_OS_UNIX)
- // Known datetimes
- qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
- qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
-
- // Test default constructor
- QIcuTimeZonePrivate tzpd;
- QVERIFY(tzpd.isValid());
-
- // Test invalid is not available:
- QVERIFY(!tzpd.isTimeZoneIdAvailable("Gondwana/Erewhon"));
- // and construction gives an invalid result:
- QIcuTimeZonePrivate tzpi("Gondwana/Erewhon");
- QCOMPARE(tzpi.isValid(), false);
-
- // Test named constructor
- QIcuTimeZonePrivate tzp("Europe/Berlin");
- QVERIFY(tzp.isValid());
-
- // Only test names in debug mode, names used can vary by ICU version installed
- if constexpr (debug) {
- // Test display names by type
- QLocale enUS("en_US");
- ZONE_DNAME_CHECK(StandardTime, LongName, u"Central European Standard Time");
- ZONE_DNAME_CHECK(StandardTime, ShortName, u"GMT+01:00");
- ZONE_DNAME_CHECK(StandardTime, OffsetName, u"UTC+01:00");
- ZONE_DNAME_CHECK(DaylightTime, LongName, u"Central European Summer Time");
- ZONE_DNAME_CHECK(DaylightTime, ShortName, u"GMT+02:00");
- ZONE_DNAME_CHECK(DaylightTime, OffsetName, u"UTC+02:00");
- // ICU C api does not support Generic Time yet, C++ api does
- ZONE_DNAME_CHECK(GenericTime, LongName, u"Central European Standard Time");
- ZONE_DNAME_CHECK(GenericTime, ShortName, u"GMT+01:00");
- ZONE_DNAME_CHECK(GenericTime, OffsetName, u"UTC+01:00");
-
- // Test Abbreviations
- QCOMPARE(tzp.abbreviation(std), u"CET");
- QCOMPARE(tzp.abbreviation(dst), u"CEST");
- }
-
- testCetPrivate(tzp);
- if (QTest::currentTestFailed())
- return;
- testEpochTranPrivate(QIcuTimeZonePrivate("America/Toronto"));
-#endif // ICU not on Unix, without tzdb
-}
-
-void tst_QTimeZone::tzTest()
-{
-#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX \
- && !QT_CONFIG(timezone_tzdb) && !defined Q_OS_DARWIN && !defined Q_OS_ANDROID
- const auto UTC = QTimeZone::UTC;
- // Known datetimes
- qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
- qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
-
- // Test default constructor
- QTzTimeZonePrivate tzpd;
- QVERIFY(tzpd.isValid());
-
- // Test invalid constructor
- QTzTimeZonePrivate tzpi("Gondwana/Erewhon");
- QVERIFY(!tzpi.isValid());
-
- // Test named constructor
- QTzTimeZonePrivate tzp("Europe/Berlin");
- QVERIFY(tzp.isValid());
-
- // Test POSIX-format value for $TZ:
- QTimeZone tzposix("MET-1METDST-2,M3.5.0/02:00:00,M10.5.0/03:00:00");
- QVERIFY(tzposix.isValid());
- QVERIFY(tzposix.hasDaylightTime());
-
- // Cope with stray space at start of value (QTBUG-135109):
- QTimeZone syd(" AEST-10AEDT,M10.1.0,M4.1.0/3");
- QVERIFY(syd.isValid());
- QVERIFY(syd.hasDaylightTime());
-
- // RHEL has been seen with this as Africa/Casablanca's POSIX rule:
- QTzTimeZonePrivate permaDst("<+00>0<+01>,0/0,J365/25");
- const QTimeZone utcP1("UTC+01:00"); // Should always have same offset as permaDst
- QVERIFY(permaDst.isValid());
- QVERIFY(permaDst.hasDaylightTime());
- QVERIFY(permaDst.isDaylightTime(QDate(2020, 1, 1).startOfDay(utcP1).toMSecsSinceEpoch()));
- QVERIFY(permaDst.isDaylightTime(QDate(2020, 12, 31).endOfDay(utcP1).toMSecsSinceEpoch()));
- // Note that the final /25 could be misunderstood as putting a fall-back at
- // 1am on the next year's Jan 1st; check we don't do that:
- QVERIFY(permaDst.isDaylightTime(
- QDateTime(QDate(2020, 1, 1), QTime(1, 30), utcP1).toMSecsSinceEpoch()));
- // It shouldn't have any transitions. QTimeZone::hasTransitions() only says
- // whether the backend supports them, so ask for transitions in a wide
- // enough interval that one would show up, if there are any:
- QVERIFY(permaDst.transitions(QDate(2015, 1, 1).startOfDay(UTC).toMSecsSinceEpoch(),
- QDate(2020, 1, 1).startOfDay(UTC).toMSecsSinceEpoch()
- ).isEmpty());
-
- QTimeZone tzBrazil("BRT+3"); // parts of Northern Brazil, as a POSIX rule
- QVERIFY(tzBrazil.isValid());
- QCOMPARE(tzBrazil.offsetFromUtc(QDateTime(QDate(1111, 11, 11).startOfDay())), -10800);
-
- // Test display names by type, either ICU or abbreviation only
- QLocale enUS(u"en_US");
- // Only test names in debug mode, names used can vary by ICU version installed
- if constexpr (debug) {
-#if QT_CONFIG(icu)
- ZONE_DNAME_CHECK(StandardTime, LongName, "Central European Standard Time");
- ZONE_DNAME_CHECK(StandardTime, ShortName, "GMT+01:00");
- ZONE_DNAME_CHECK(StandardTime, OffsetName, "UTC+01:00");
- ZONE_DNAME_CHECK(DaylightTime, LongName, "Central European Summer Time");
- ZONE_DNAME_CHECK(DaylightTime, ShortName, "GMT+02:00");
- ZONE_DNAME_CHECK(DaylightTime, OffsetName, "UTC+02:00");
- // ICU C api does not support Generic Time yet, C++ api does
- ZONE_DNAME_CHECK(GenericTime, LongName, "Central European Standard Time");
- ZONE_DNAME_CHECK(GenericTime, ShortName, "GMT+01:00");
- ZONE_DNAME_CHECK(GenericTime, OffsetName, "UTC+01:00");
-#else
- ZONE_DNAME_CHECK(StandardTime, LongName, "CET");
- ZONE_DNAME_CHECK(StandardTime, ShortName, "CET");
- ZONE_DNAME_CHECK(StandardTime, OffsetName, "CET");
- ZONE_DNAME_CHECK(DaylightTime, LongName, "CEST");
- ZONE_DNAME_CHECK(DaylightTime, ShortName, "CEST");
- ZONE_DNAME_CHECK(DaylightTime, OffsetName, "CEST");
- ZONE_DNAME_CHECK(GenericTime, LongName, "CET");
- ZONE_DNAME_CHECK(GenericTime, ShortName, "CET");
- ZONE_DNAME_CHECK(GenericTime, OffsetName, "CET");
-#endif // icu
-
- // Test Abbreviations
- QCOMPARE(tzp.abbreviation(std), u"CET");
- QCOMPARE(tzp.abbreviation(dst), u"CEST");
- }
-
- testCetPrivate(tzp);
- if (QTest::currentTestFailed())
- return;
- testEpochTranPrivate(QTzTimeZonePrivate("America/Toronto"));
- if (QTest::currentTestFailed())
- return;
-
- // Test first and last transition rule
- // Warning: This could vary depending on age of TZ file!
-
- // Test low date uses first rule found
- constexpr qint64 ancient = -Q_INT64_C(9999999999999);
- // Note: Depending on the OS in question, the database may be carrying the
- // Local Mean Time. which for Berlin is 0:53:28
- QTimeZonePrivate::Data dat = tzp.data(ancient);
- QCOMPARE(dat.atMSecsSinceEpoch, ancient);
- QCOMPARE(dat.daylightTimeOffset, 0);
- if (dat.abbreviation == u"LMT") {
- QCOMPARE(dat.standardTimeOffset, 3208);
- } else {
- QCOMPARE(dat.standardTimeOffset, 3600);
-
- constexpr qint64 invalidTime = std::numeric_limits<qint64>::min();
- constexpr int invalidOffset = std::numeric_limits<int>::min();
- // Test previous to low value is invalid
- dat = tzp.previousTransition(ancient);
- QCOMPARE(dat.atMSecsSinceEpoch, invalidTime);
- QCOMPARE(dat.standardTimeOffset, invalidOffset);
- QCOMPARE(dat.daylightTimeOffset, invalidOffset);
- }
-
- dat = tzp.nextTransition(ancient);
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch,
- QTimeZone::fromSecondsAheadOfUtc(3600)),
- QDateTime(QDate(1893, 4, 1), QTime(0, 6, 32),
- QTimeZone::fromSecondsAheadOfUtc(3600)));
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 0);
-
- // Date-times late enough to exercise POSIX rules:
- qint64 stdHi = QDate(2100, 1, 1).startOfDay(UTC).toMSecsSinceEpoch();
- qint64 dstHi = QDate(2100, 6, 1).startOfDay(UTC).toMSecsSinceEpoch();
- // Relevant last Sundays in October and March:
- QCOMPARE(Qt::DayOfWeek(QDate(2099, 10, 25).dayOfWeek()), Qt::Sunday);
- QCOMPARE(Qt::DayOfWeek(QDate(2100, 3, 28).dayOfWeek()), Qt::Sunday);
- QCOMPARE(Qt::DayOfWeek(QDate(2100, 10, 31).dayOfWeek()), Qt::Sunday);
-
- dat = tzp.data(stdHi);
- QCOMPARE(dat.atMSecsSinceEpoch - stdHi, qint64(0));
- QCOMPARE(dat.offsetFromUtc, 3600);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 0);
-
- dat = tzp.data(dstHi);
- QCOMPARE(dat.atMSecsSinceEpoch - dstHi, qint64(0));
- QCOMPARE(dat.offsetFromUtc, 7200);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 3600);
-
- dat = tzp.previousTransition(stdHi);
- QCOMPARE(dat.abbreviation, u"CET");
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2099, 10, 25), QTime(3, 0), QTimeZone::fromSecondsAheadOfUtc(7200)));
- QCOMPARE(dat.offsetFromUtc, 3600);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 0);
-
- dat = tzp.previousTransition(dstHi);
- QCOMPARE(dat.abbreviation, u"CEST");
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2100, 3, 28), QTime(2, 0), QTimeZone::fromSecondsAheadOfUtc(3600)));
- QCOMPARE(dat.offsetFromUtc, 7200);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 3600);
-
- dat = tzp.nextTransition(stdHi);
- QCOMPARE(dat.abbreviation, u"CEST");
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2100, 3, 28), QTime(2, 0), QTimeZone::fromSecondsAheadOfUtc(3600)));
- QCOMPARE(dat.offsetFromUtc, 7200);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 3600);
-
- dat = tzp.nextTransition(dstHi);
- QCOMPARE(dat.abbreviation, u"CET");
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch,
- QTimeZone::fromSecondsAheadOfUtc(3600)),
- QDateTime(QDate(2100, 10, 31), QTime(3, 0), QTimeZone::fromSecondsAheadOfUtc(7200)));
- QCOMPARE(dat.offsetFromUtc, 3600);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 0);
-
- // Test TZ timezone vs UTC timezone for non-whole-hour negative offset:
- QTzTimeZonePrivate tztz1("America/Caracas");
- QUtcTimeZonePrivate tzutc1("UTC-04:30");
- QVERIFY(tztz1.isValid());
- QVERIFY(tzutc1.isValid());
- QTzTimeZonePrivate::Data datatz1 = tztz1.data(std);
- QTzTimeZonePrivate::Data datautc1 = tzutc1.data(std);
- QCOMPARE(datatz1.offsetFromUtc, datautc1.offsetFromUtc);
-
- // Test TZ timezone vs UTC timezone for non-whole-hour positive offset:
- QTzTimeZonePrivate tztz2k("Asia/Kolkata"); // New name
- QTzTimeZonePrivate tztz2c("Asia/Calcutta"); // Legacy name
- // Can't assign QtzTZP, so use a reference; prefer new name.
- QTzTimeZonePrivate &tztz2 = tztz2k.isValid() ? tztz2k : tztz2c;
- QUtcTimeZonePrivate tzutc2("UTC+05:30");
- QVERIFY2(tztz2.isValid(), tztz2.id().constData());
- QVERIFY(tzutc2.isValid());
- QTzTimeZonePrivate::Data datatz2 = tztz2.data(std);
- QTzTimeZonePrivate::Data datautc2 = tzutc2.data(std);
- QCOMPARE(datatz2.offsetFromUtc, datautc2.offsetFromUtc);
-
- // Test a timezone with an abbreviation that isn't all letters:
- QTzTimeZonePrivate tzBarnaul("Asia/Barnaul");
- if (tzBarnaul.isValid()) {
- QCOMPARE(tzBarnaul.data(std).abbreviation, u"+07");
-
- // first full day of the new rule (tzdata2016b)
- QDateTime dt(QDate(2016, 3, 28), QTime(0, 0), UTC);
- QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, u"+07");
- }
-#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !timezone_tzdb && !Q_OS_DARWIN && !Q_OS_ANDROID
-}
-
-void tst_QTimeZone::macTest()
-{
-#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_DARWIN) && !QT_CONFIG(timezone_tzdb)
- // Known datetimes
- qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
- qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
-
- // Test default constructor
- QMacTimeZonePrivate tzpd;
- QVERIFY(tzpd.isValid());
-
- // Test invalid constructor
- QMacTimeZonePrivate tzpi("Gondwana/Erewhon");
- QCOMPARE(tzpi.isValid(), false);
-
- // Test named constructor
- QMacTimeZonePrivate tzp("Europe/Berlin");
- QVERIFY(tzp.isValid());
-
- // Only test names in debug mode, names used can vary by version
- if constexpr (debug) {
- // Test display names by type
- QLocale enUS(u"en_US");
- ZONE_DNAME_CHECK(StandardTime, LongName, "Central European Standard Time");
- ZONE_DNAME_CHECK(StandardTime, ShortName, "GMT+01:00");
- ZONE_DNAME_CHECK(StandardTime, OffsetName, "UTC+01:00");
- ZONE_DNAME_CHECK(DaylightTime, LongName, "Central European Summer Time");
- ZONE_DNAME_CHECK(DaylightTime, ShortName, "GMT+02:00");
- ZONE_DNAME_CHECK(DaylightTime, OffsetName, "UTC+02:00");
- // ICU C api does not support Generic Time yet, C++ api does
- ZONE_DNAME_CHECK(GenericTime, LongName, "Central European Time");
- ZONE_DNAME_CHECK(GenericTime, ShortName, "Germany Time");
- ZONE_DNAME_CHECK(GenericTime, OffsetName, "UTC+01:00");
-
- // Test Abbreviations
- QCOMPARE(tzp.abbreviation(std), u"CET");
- QCOMPARE(tzp.abbreviation(dst), u"CEST");
- }
-
- testCetPrivate(tzp);
- if (QTest::currentTestFailed())
- return;
- testEpochTranPrivate(QMacTimeZonePrivate("America/Toronto"));
-#endif // QT_BUILD_INTERNAL && Q_OS_DARWIN without tzdb
-}
-
void tst_QTimeZone::darwinTypes()
{
#ifndef Q_OS_DARWIN
@@ -1720,59 +1296,6 @@ void tst_QTimeZone::darwinTypes()
#endif
}
-void tst_QTimeZone::winTest()
-{
-#if defined(QT_BUILD_INTERNAL) && defined(USING_WIN_TZ)
- // Known datetimes
- qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
- qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
-
- // Test default constructor
- QWinTimeZonePrivate tzpd;
- if constexpr (debug)
- qDebug() << "System ID = " << tzpd.id()
- << tzpd.displayName(QTimeZone::StandardTime, QTimeZone::LongName, QLocale())
- << tzpd.displayName(QTimeZone::GenericTime, QTimeZone::LongName, QLocale());
- QVERIFY(tzpd.isValid());
-
- // Test invalid constructor
- QWinTimeZonePrivate tzpi("Gondwana/Erewhon");
- QCOMPARE(tzpi.isValid(), false);
-
- // Test named constructor
- QWinTimeZonePrivate tzp("Europe/Berlin");
- QVERIFY(tzp.isValid());
-
- // Only test names in debug mode, names used can vary by version
- if constexpr (debug) {
- // Test display names by type
- QLocale enUS(u"en_US");
- ZONE_DNAME_CHECK(StandardTime, LongName, "W. Europe Standard Time");
- ZONE_DNAME_CHECK(StandardTime, ShortName, "W. Europe Standard Time");
- ZONE_DNAME_CHECK(StandardTime, OffsetName, "UTC+01:00");
- ZONE_DNAME_CHECK(DaylightTime, LongName, "W. Europe Daylight Time");
- ZONE_DNAME_CHECK(DaylightTime, ShortName, "W. Europe Daylight Time");
- ZONE_DNAME_CHECK(DaylightTime, OffsetName, "UTC+02:00");
- ZONE_DNAME_CHECK(GenericTime, LongName,
- "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna");
- ZONE_DNAME_CHECK(GenericTime, ShortName,
- "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna");
- ZONE_DNAME_CHECK(GenericTime, OffsetName, "UTC+01:00");
-
- // Test Abbreviations
- QCOMPARE(tzp.abbreviation(std), u"CET");
- QCOMPARE(tzp.abbreviation(dst), u"CEST");
- }
-
- testCetPrivate(tzp);
- if (QTest::currentTestFailed())
- return;
- testEpochTranPrivate(QWinTimeZonePrivate("America/Toronto"));
-#endif // QT_BUILD_INTERNAL && USING_WIN_TZ
-}
-
-#undef ZONE_DNAME_CHECK
-
void tst_QTimeZone::localeSpecificDisplayName_data()
{
QTest::addColumn<QByteArray>("zoneName");
@@ -1849,285 +1372,6 @@ void tst_QTimeZone::localeSpecificDisplayName()
#endif
}
-void tst_QTimeZone::roundtripDisplayNames_data()
-{
-#ifdef QT_BUILD_INTERNAL
- QTest::addColumn<QTimeZone>("zone");
- QTest::addColumn<QLocale>("locale");
- QTest::addColumn<QTimeZone::TimeType>("type");
-
- constexpr QTimeZone::TimeType types[] = {
- QTimeZone::GenericTime, QTimeZone::StandardTime, QTimeZone::DaylightTime
- };
- const auto typeName = [](QTimeZone::TimeType type) {
- switch (type) {
- case QTimeZone::GenericTime: return "Gen";
- case QTimeZone::StandardTime: return "Std";
- case QTimeZone::DaylightTime: return "DST";
- }
- Q_UNREACHABLE_RETURN("Unrecognised");
- };
- const QList<QByteArray> allList = (QTimeZone::availableTimeZoneIds() << "Vulcan/ShiKahr"_ba);
-#ifdef EXHAUSTIVE_ZONE_DISPLAY
- const QList<QByteArray> idList = allList;
-#else
- const QList<QByteArray> idList = {
- "Africa/Casablanca"_ba, "Africa/Lagos"_ba, "Africa/Tunis"_ba,
- "America/Caracas"_ba, "America/Indiana/Tell_City"_ba, "America/Managua"_ba,
- "Asia/Bangkok"_ba, "Asia/Colombo"_ba, "Asia/Tokyo"_ba,
- "Atlantic/Bermuda"_ba, "Atlantic/Faroe"_ba, "Atlantic/Madeira"_ba,
- "Australia/Broken_Hill"_ba, "Australia/NSW"_ba, "Australia/Tasmania"_ba,
- "Brazil/Acre"_ba, "CST6CDT"_ba, "Canada/Atlantic"_ba,
- "Chile/EasterIsland"_ba, "Etc/Greenwich"_ba, "Etc/Universal"_ba,
- "Europe/Guernsey"_ba, "Europe/Kaliningrad"_ba, "Europe/Kyiv"_ba,
- "Europe/Prague"_ba, "Europe/Vatican"_ba,
- "Indian/Comoro"_ba, "Mexico/BajaSur"_ba,
- "Pacific/Bougainville"_ba, "Pacific/Midway"_ba, "Pacific/Wallis"_ba,
- "US/Aleutian"_ba,
- "UTC"_ba,
- // Those named overtly in tst_QDateTime - special cases first:
- "UTC-02:00"_ba, "UTC+02:00"_ba, "UTC+12:00"_ba,
- "Etc/GMT+3"_ba, "GMT-0"_ba, "GMT"_ba,
- // ... then ordinary names in alphabetic order:
- "America/Anchorage"_ba, "America/Metlakatla"_ba, "America/New_York"_ba,
- "America/Sao_Paulo"_ba, "America/Toronto"_ba, "America/Vancouver"_ba,
- "Asia/Kathmandu"_ba, "Asia/Manila"_ba, "Asia/Singapore"_ba,
- "Australia/Brisbane"_ba, "Australia/Eucla"_ba, "Australia/Sydney"_ba,
- "Europe/Berlin"_ba, "Europe/Helsinki"_ba, "Europe/Lisbon"_ba, "Europe/Oslo"_ba,
- "Europe/Rome"_ba,
- "Pacific/Apia"_ba, "Pacific/Auckland"_ba, "Pacific/Kiritimati"_ba,
- "Vulcan/ShiKahr"_ba // Invalid: also worth testing.
- };
- // Some valid zones in that list may be absent from the platform's
- // availableTimeZoneIds(), yet in fact work when used as it's asked to
- // instantiate them (e.g. Etc/Universal on macOS). This can give them a
- // displayName() that we fail to decode, without timezone_locale, due to
- // only trying the availableTimeZoneIds() in findLongNamePrefix(). So we
- // have to filter on membership of allList when creating rows.
-#endif // Exhaustive
- const QLocale fr(QLocale::French, QLocale::France);
- const QLocale hi(QLocale::Hindi, QLocale::India);
- for (const QByteArray &id : idList) {
- if (id == "localtime"_ba || id == "posixrules"_ba || !allList.contains(id))
- continue;
- QTimeZone zone = QTimeZone(id);
- if (!zone.isValid())
- continue;
- for (const auto type : types) {
- QTest::addRow("%s@fr_FR/%s", id.constData(), typeName(type))
- << zone << fr << type;
- QTest::addRow("%s@hi_IN/%s", id.constData(), typeName(type))
- << zone << hi << type;
- }
- }
-#else
- QSKIP("Test needs access to internal APIs");
-#endif
-}
-
-void tst_QTimeZone::roundtripDisplayNames()
-{
-#ifdef QT_BUILD_INTERNAL
- QFETCH(const QTimeZone, zone);
- QFETCH(const QLocale, locale);
- QFETCH(const QTimeZone::TimeType, type);
- static const QDateTime jan = QDateTime(QDate(2015, 1, 1), QTime(12, 0), QTimeZone::UTC);
- static const QDateTime jul = QDateTime(QDate(2015, 7, 1), QTime(12, 0), QTimeZone::UTC);
- const QDateTime dt = zone.isDaylightTime(jul) == (type == QTimeZone::DaylightTime) ? jul : jan;
-
- // Some zones exercise region format.
- const QString name = zone.displayName(type, QTimeZone::LongName, locale);
- if (!name.isEmpty()) {
- const auto tran = QTimeZonePrivate::extractPrivate(zone)->data(type);
- const qint64 when = tran.atMSecsSinceEpoch == QTimeZonePrivate::invalidMSecs()
- ? dt.toMSecsSinceEpoch() : tran.atMSecsSinceEpoch;
- const QString extended = name + "some spurious cruft"_L1;
- auto match =
- QTimeZonePrivate::findLongNamePrefix(extended, locale, when);
- if (!match)
- match = QTimeZonePrivate::findLongNamePrefix(extended, locale);
- if (!match)
- match = QTimeZonePrivate::findNarrowOffsetPrefix(extended, locale);
- if (!match)
- match = QTimeZonePrivate::findLongUtcPrefix(extended);
- auto report = qScopeGuard([=]() {
- qDebug() << "At" << QDateTime::fromMSecsSinceEpoch(when, QTimeZone::UTC)
- << "via" << name;
- });
- QCOMPARE(match.nameLength, name.size());
- report.dismiss();
-#if 0
- if (match.ianaId != zone.id()) {
- const QTimeZone found = QTimeZone(match.ianaId);
- if (QTimeZonePrivate::extractPrivate(found)->offsetFromUtc(when)
- != QTimeZonePrivate::extractPrivate(zone)->offsetFromUtc(when)) {
- // For DST, some zones haven't done it in ages, so tran may be ancient.
- // Meanwhile, match.ianaId is typically the canonical zone for a metazone.
- // That, in turn, may not have been doing DST when zone was.
- // So we can't rely on a match, but can report the mismatches.
- qDebug() << "Long name" << name << "on"
- << QTimeZonePrivate::extractPrivate(zone)->offsetFromUtc(when)
- << "at" << QDateTime::fromMSecsSinceEpoch(when, QTimeZone::UTC)
- << "got" << match.ianaId << "on"
- << QTimeZonePrivate::extractPrivate(found)->offsetFromUtc(when);
- // There are also some absurdly over-generic names, that lead to
- // ambiguities, e.g. "heure : West"
- }
- }
-#endif // Debug code
- } else if (type != QTimeZone::DaylightTime) { /* Zones with no DST have no DST-name */
- qDebug("Empty display name");
- }
-#else
- Q_ASSERT(!"Should be skipped when building data table");
-#endif
-}
-
-#ifdef QT_BUILD_INTERNAL
-// Test each private produces the same basic results for CET
-void tst_QTimeZone::testCetPrivate(const QTimeZonePrivate &tzp)
-{
- // Known datetimes
- const auto UTC = QTimeZone::UTC;
- const auto eastOneHour = QTimeZone::fromSecondsAheadOfUtc(3600);
- qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
- qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
- qint64 prev = QDateTime(QDate(2011, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
-
- QCOMPARE(tzp.offsetFromUtc(std), 3600);
- QCOMPARE(tzp.offsetFromUtc(dst), 7200);
-
- QCOMPARE(tzp.standardTimeOffset(std), 3600);
- QCOMPARE(tzp.standardTimeOffset(dst), 3600);
-
- QCOMPARE(tzp.daylightTimeOffset(std), 0);
- QCOMPARE(tzp.daylightTimeOffset(dst), 3600);
-
- QCOMPARE(tzp.hasDaylightTime(), true);
- QCOMPARE(tzp.isDaylightTime(std), false);
- QCOMPARE(tzp.isDaylightTime(dst), true);
-
- QTimeZonePrivate::Data dat = tzp.data(std);
- QCOMPARE(dat.atMSecsSinceEpoch, std);
- QCOMPARE(dat.offsetFromUtc, 3600);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 0);
- QCOMPARE(dat.abbreviation, tzp.abbreviation(std));
-
- dat = tzp.data(dst);
- QCOMPARE(dat.atMSecsSinceEpoch, dst);
- QCOMPARE(dat.offsetFromUtc, 7200);
- QCOMPARE(dat.standardTimeOffset, 3600);
- QCOMPARE(dat.daylightTimeOffset, 3600);
- QCOMPARE(dat.abbreviation, tzp.abbreviation(dst));
-
- // Only test transitions if host system supports them
- if (tzp.hasTransitions()) {
- QTimeZonePrivate::Data tran = tzp.nextTransition(std);
- // 2012-03-25 02:00 CET, +1 -> +2
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2012, 3, 25), QTime(2, 0), eastOneHour));
- QCOMPARE(tran.offsetFromUtc, 7200);
- QCOMPARE(tran.standardTimeOffset, 3600);
- QCOMPARE(tran.daylightTimeOffset, 3600);
-
- tran = tzp.nextTransition(dst);
- // 2012-10-28 03:00 CEST, +2 -> +1
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2012, 10, 28), QTime(3, 0),
- QTimeZone::fromSecondsAheadOfUtc(2 * 3600)));
- QCOMPARE(tran.offsetFromUtc, 3600);
- QCOMPARE(tran.standardTimeOffset, 3600);
- QCOMPARE(tran.daylightTimeOffset, 0);
-
- tran = tzp.previousTransition(std);
- // 2011-10-30 03:00 CEST, +2 -> +1
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2011, 10, 30), QTime(3, 0),
- QTimeZone::fromSecondsAheadOfUtc(2 * 3600)));
- QCOMPARE(tran.offsetFromUtc, 3600);
- QCOMPARE(tran.standardTimeOffset, 3600);
- QCOMPARE(tran.daylightTimeOffset, 0);
-
- tran = tzp.previousTransition(dst);
- // 2012-03-25 02:00 CET, +1 -> +2 (again)
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(2012, 3, 25), QTime(2, 0), eastOneHour));
- QCOMPARE(tran.offsetFromUtc, 7200);
- QCOMPARE(tran.standardTimeOffset, 3600);
- QCOMPARE(tran.daylightTimeOffset, 3600);
-
- QTimeZonePrivate::DataList expected;
- // 2011-03-27 02:00 CET, +1 -> +2
- tran.atMSecsSinceEpoch = QDateTime(QDate(2011, 3, 27), QTime(2, 0),
- eastOneHour).toMSecsSinceEpoch();
- tran.offsetFromUtc = 7200;
- tran.standardTimeOffset = 3600;
- tran.daylightTimeOffset = 3600;
- expected << tran;
- // 2011-10-30 03:00 CEST, +2 -> +1
- tran.atMSecsSinceEpoch = QDateTime(QDate(2011, 10, 30), QTime(3, 0),
- QTimeZone::fromSecondsAheadOfUtc(2 * 3600)
- ).toMSecsSinceEpoch();
- tran.offsetFromUtc = 3600;
- tran.standardTimeOffset = 3600;
- tran.daylightTimeOffset = 0;
- expected << tran;
- QTimeZonePrivate::DataList result = tzp.transitions(prev, std);
- QCOMPARE(result.size(), expected.size());
- for (int i = 0; i < expected.size(); ++i) {
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(result.at(i).atMSecsSinceEpoch, eastOneHour),
- QDateTime::fromMSecsSinceEpoch(expected.at(i).atMSecsSinceEpoch, eastOneHour));
- QCOMPARE(result.at(i).offsetFromUtc, expected.at(i).offsetFromUtc);
- QCOMPARE(result.at(i).standardTimeOffset, expected.at(i).standardTimeOffset);
- QCOMPARE(result.at(i).daylightTimeOffset, expected.at(i).daylightTimeOffset);
- }
- }
-}
-
-// Needs a zone with DST around the epoch; currently America/Toronto (EST5EDT)
-void tst_QTimeZone::testEpochTranPrivate(const QTimeZonePrivate &tzp)
-{
- if (!tzp.hasTransitions())
- return; // test only viable for transitions
-
- const auto UTC = QTimeZone::UTC;
- const auto hour = std::chrono::hours{1};
- QTimeZonePrivate::Data tran = tzp.nextTransition(0); // i.e. first after epoch
- // 1970-04-26 02:00 EST, -5 -> -4
- const QDateTime after = QDateTime(QDate(1970, 4, 26), QTime(2, 0),
- QTimeZone::fromDurationAheadOfUtc(-5 * hour));
- const QDateTime found = QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC);
-#ifdef USING_WIN_TZ // MS gets the date wrong: 5th April instead of 26th.
- QCOMPARE(found.toOffsetFromUtc(-5 * 3600).time(), after.time());
-#else
- QCOMPARE(found, after);
-#endif
- QCOMPARE(tran.offsetFromUtc, -4 * 3600);
- QCOMPARE(tran.standardTimeOffset, -5 * 3600);
- QCOMPARE(tran.daylightTimeOffset, 3600);
-
- // Pre-epoch time-zones might not be supported at all:
- tran = tzp.nextTransition(QDateTime(QDate(1601, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch());
- if (tran.atMSecsSinceEpoch != QTimeZonePrivate::invalidMSecs()
- // Toronto *did* have a transition before 1970 (DST since 1918):
- && tran.atMSecsSinceEpoch < 0) {
- // ... but, if they are, we should be able to search back to them:
- tran = tzp.previousTransition(0); // i.e. last before epoch
- // 1969-10-26 02:00 EDT, -4 -> -5
- QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
- QDateTime(QDate(1969, 10, 26), QTime(2, 0),
- QTimeZone::fromDurationAheadOfUtc(-4 * hour)));
- QCOMPARE(tran.offsetFromUtc, -5 * 3600);
- QCOMPARE(tran.standardTimeOffset, -5 * 3600);
- QCOMPARE(tran.daylightTimeOffset, 0);
- } else {
- // Do not use QSKIP(): that would discard the rest of this sub-test's caller.
- qDebug() << "No support for pre-epoch time-zone transitions";
- }
-}
-#endif // QT_BUILD_INTERNAL
-
#if __cpp_lib_chrono >= 201907L
Q_DECLARE_METATYPE(const std::chrono::time_zone *);
#endif
diff --git a/tests/auto/corelib/time/qtimezonebackend/CMakeLists.txt b/tests/auto/corelib/time/qtimezonebackend/CMakeLists.txt
new file mode 100644
index 00000000000..6bead8b6549
--- /dev/null
+++ b/tests/auto/corelib/time/qtimezonebackend/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qtimezonebackend Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qtimezonebackend LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qtimezonebackend
+ SOURCES
+ tst_qtimezonebackend.cpp
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ QT_NO_FOREACH
+ QT_NO_KEYWORDS
+ LIBRARIES
+ Qt::CorePrivate
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_qtimezonebackend CONDITION QT_FEATURE_icu
+ LIBRARIES
+ ICU::i18n ICU::uc ICU::data
+)
diff --git a/tests/auto/corelib/time/qtimezonebackend/tst_qtimezonebackend.cpp b/tests/auto/corelib/time/qtimezonebackend/tst_qtimezonebackend.cpp
new file mode 100644
index 00000000000..71519ccd9fc
--- /dev/null
+++ b/tests/auto/corelib/time/qtimezonebackend/tst_qtimezonebackend.cpp
@@ -0,0 +1,784 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <qtimezone.h>
+#include <private/qtimezoneprivate_p.h>
+
+#include <qlocale.h>
+#include <qscopeguard.h>
+
+#if defined(Q_OS_WIN) && !QT_CONFIG(icu) && !QT_CONFIG(timezone_tzdb)
+# define USING_WIN_TZ
+#endif
+
+// Enable to test exhaustively - this is slow and expensive.
+// It is also quite likely to trip over problems that we can't necessarily fix.
+// #define EXHAUSTIVE_ZONE_DISPLAY
+
+using namespace Qt::StringLiterals;
+
+class tst_QTimeZoneBackend : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ // Tests of QTZP functionality:
+ void isValidId_data();
+ void isValidId();
+ void roundtripDisplayNames_data();
+ void roundtripDisplayNames();
+ // Tests of specific backends (see also ../qtimezone/):
+ void icuTest();
+ void tzTest();
+ void macTest();
+ void winTest();
+
+private:
+ // Generic tests of privates, called by implementation-specific private tests:
+ void testCetPrivate(const QTimeZonePrivate &tzp);
+ void testEpochTranPrivate(const QTimeZonePrivate &tzp);
+ // Set to true to print debug output, test Display Names and run long stress tests
+ static constexpr bool debug = false;
+};
+
+void tst_QTimeZoneBackend::isValidId_data()
+{
+ QTest::addColumn<QByteArray>("input");
+ QTest::addColumn<bool>("valid");
+
+ // a-z, A-Z, 0-9, '.', '-', '_' are valid chars
+ // Can't start with '-'
+ // Parts separated by '/', each part min 1 and max of 14 chars
+ // (Android has parts with lengths up to 17, so tolerates this as a special case.)
+#define TESTSET(name, section, valid) \
+ QTest::newRow(name " front") << QByteArray(section "/xyz/xyz") << valid; \
+ QTest::newRow(name " middle") << QByteArray("xyz/" section "/xyz") << valid; \
+ QTest::newRow(name " back") << QByteArray("xyz/xyz/" section) << valid
+
+ // a-z, A-Z, 0-9, '.', '-', '_' are valid chars
+ // Can't start with '-'
+ // Parts separated by '/', each part min 1 and max of 14 chars
+ TESTSET("empty", "", false);
+ TESTSET("minimal", "m", true);
+#if (defined(Q_OS_ANDROID) || QT_CONFIG(icu)) && !QT_CONFIG(timezone_tzdb)
+ TESTSET("maximal", "East-Saskatchewan", true); // Android actually uses this
+ TESTSET("too long", "North-Saskatchewan", false); // ... but thankfully not this.
+#else
+ TESTSET("maximal", "12345678901234", true);
+ TESTSET("maximal twice", "12345678901234/12345678901234", true);
+ TESTSET("too long", "123456789012345", false);
+ TESTSET("too-long/maximal", "123456789012345/12345678901234", false);
+ TESTSET("maximal/too-long", "12345678901234/123456789012345", false);
+#endif
+
+ TESTSET("bad hyphen", "-hyphen", false);
+ TESTSET("good hyphen", "hy-phen", true);
+
+ TESTSET("valid char _", "_", true);
+ TESTSET("valid char .", ".", true);
+ TESTSET("valid char :", ":", true);
+ TESTSET("valid char +", "+", true);
+ TESTSET("valid char A", "A", true);
+ TESTSET("valid char Z", "Z", true);
+ TESTSET("valid char a", "a", true);
+ TESTSET("valid char z", "z", true);
+ TESTSET("valid char 0", "0", true);
+ TESTSET("valid char 9", "9", true);
+
+ TESTSET("valid pair az", "az", true);
+ TESTSET("valid pair AZ", "AZ", true);
+ TESTSET("valid pair 09", "09", true);
+ TESTSET("valid pair .z", ".z", true);
+ TESTSET("valid pair _z", "_z", true);
+ TESTSET("invalid pair -z", "-z", false);
+
+ TESTSET("valid triple a/z", "a/z", true);
+ TESTSET("valid triple a.z", "a.z", true);
+ TESTSET("valid triple a-z", "a-z", true);
+ TESTSET("valid triple a_z", "a_z", true);
+ TESTSET("invalid triple a z", "a z", false);
+ TESTSET("invalid triple a\\z", "a\\z", false);
+ TESTSET("invalid triple a,z", "a,z", false);
+
+ TESTSET("invalid space", " ", false);
+ TESTSET("invalid char ^", "^", false);
+ TESTSET("invalid char \"", "\"", false);
+ TESTSET("invalid char $", "$", false);
+ TESTSET("invalid char %", "%", false);
+ TESTSET("invalid char &", "&", false);
+ TESTSET("invalid char (", "(", false);
+ TESTSET("invalid char )", ")", false);
+ TESTSET("invalid char =", "=", false);
+ TESTSET("invalid char -", "-", false);
+ TESTSET("invalid char ?", "?", false);
+ TESTSET("invalid char ß", "ß", false);
+ TESTSET("invalid char \\x01", "\x01", false);
+ TESTSET("invalid char ' '", " ", false);
+
+#undef TESTSET
+
+ QTest::newRow("az alone") << QByteArray("az") << true;
+ QTest::newRow("AZ alone") << QByteArray("AZ") << true;
+ QTest::newRow("09 alone") << QByteArray("09") << true;
+ QTest::newRow("a/z alone") << QByteArray("a/z") << true;
+ QTest::newRow("a.z alone") << QByteArray("a.z") << true;
+ QTest::newRow("a-z alone") << QByteArray("a-z") << true;
+ QTest::newRow("a_z alone") << QByteArray("a_z") << true;
+ QTest::newRow(".z alone") << QByteArray(".z") << true;
+ QTest::newRow("_z alone") << QByteArray("_z") << true;
+ QTest::newRow("a z alone") << QByteArray("a z") << false;
+ QTest::newRow("a\\z alone") << QByteArray("a\\z") << false;
+ QTest::newRow("a,z alone") << QByteArray("a,z") << false;
+ QTest::newRow("/z alone") << QByteArray("/z") << false;
+ QTest::newRow("-z alone") << QByteArray("-z") << false;
+#if (defined(Q_OS_ANDROID) || QT_CONFIG(icu)) && !QT_CONFIG(timezone_tzdb)
+ QTest::newRow("long alone") << QByteArray("12345678901234567") << true;
+ QTest::newRow("over-long alone") << QByteArray("123456789012345678") << false;
+#else
+ QTest::newRow("long alone") << QByteArray("12345678901234") << true;
+ QTest::newRow("over-long alone") << QByteArray("123456789012345") << false;
+#endif
+}
+
+void tst_QTimeZoneBackend::isValidId()
+{
+ QFETCH(QByteArray, input);
+ QFETCH(bool, valid);
+
+ QCOMPARE(QTimeZonePrivate::isValidId(input), valid);
+}
+
+void tst_QTimeZoneBackend::roundtripDisplayNames_data()
+{
+ QTest::addColumn<QTimeZone>("zone");
+ QTest::addColumn<QLocale>("locale");
+ QTest::addColumn<QTimeZone::TimeType>("type");
+
+ constexpr QTimeZone::TimeType types[] = {
+ QTimeZone::GenericTime, QTimeZone::StandardTime, QTimeZone::DaylightTime
+ };
+ const auto typeName = [](QTimeZone::TimeType type) {
+ switch (type) {
+ case QTimeZone::GenericTime: return "Gen";
+ case QTimeZone::StandardTime: return "Std";
+ case QTimeZone::DaylightTime: return "DST";
+ }
+ Q_UNREACHABLE_RETURN("Unrecognised");
+ };
+ const QList<QByteArray> allList = (QTimeZone::availableTimeZoneIds() << "Vulcan/ShiKahr"_ba);
+#ifdef EXHAUSTIVE_ZONE_DISPLAY
+ const QList<QByteArray> idList = allList;
+#else
+ const QList<QByteArray> idList = {
+ "Africa/Casablanca"_ba, "Africa/Lagos"_ba, "Africa/Tunis"_ba,
+ "America/Caracas"_ba, "America/Coyhaique"_ba,
+ "America/Indiana/Tell_City"_ba, "America/Managua"_ba,
+ "Asia/Bangkok"_ba, "Asia/Colombo"_ba, "Asia/Tokyo"_ba,
+ "Atlantic/Bermuda"_ba, "Atlantic/Faroe"_ba, "Atlantic/Madeira"_ba,
+ "Australia/Broken_Hill"_ba, "Australia/NSW"_ba, "Australia/Tasmania"_ba,
+ "Brazil/Acre"_ba, "Canada/Atlantic"_ba, "Chile/EasterIsland"_ba,
+ "CST6CDT"_ba, "Etc/Greenwich"_ba, "Etc/Universal"_ba,
+ "Europe/Guernsey"_ba, "Europe/Kaliningrad"_ba, "Europe/Kyiv"_ba,
+ "Europe/Prague"_ba, "Europe/Vatican"_ba,
+ "Indian/Comoro"_ba, "Mexico/BajaSur"_ba,
+ "Pacific/Bougainville"_ba, "Pacific/Midway"_ba, "Pacific/Wallis"_ba,
+ "US/Aleutian"_ba,
+ "UTC"_ba,
+ // Those named overtly in tst_QDateTime - special cases first:
+ "UTC-02:00"_ba, "UTC+02:00"_ba, "UTC+12:00"_ba,
+ "Etc/GMT+3"_ba, "GMT-0"_ba, "GMT"_ba,
+ // ... then ordinary names in alphabetic order:
+ "America/Anchorage"_ba, "America/Metlakatla"_ba, "America/New_York"_ba,
+ "America/Sao_Paulo"_ba, "America/Toronto"_ba, "America/Vancouver"_ba,
+ "Asia/Kathmandu"_ba, "Asia/Manila"_ba, "Asia/Singapore"_ba,
+ "Australia/Brisbane"_ba, "Australia/Eucla"_ba, "Australia/Sydney"_ba,
+ "Europe/Berlin"_ba, "Europe/Helsinki"_ba, "Europe/Lisbon"_ba, "Europe/Oslo"_ba,
+ "Europe/Rome"_ba,
+ "Pacific/Apia"_ba, "Pacific/Auckland"_ba, "Pacific/Kiritimati"_ba,
+ "Vulcan/ShiKahr"_ba // Invalid: also worth testing.
+ };
+ // Some valid zones in that list may be absent from the platform's
+ // availableTimeZoneIds(), yet in fact work when used as it's asked to
+ // instantiate them (e.g. Etc/Universal on macOS). This can give them a
+ // displayName() that we fail to decode, without timezone_locale, due to
+ // only trying the availableTimeZoneIds() in findLongNamePrefix(). So we
+ // have to filter on membership of allList when creating rows.
+#endif // Exhaustive
+ const QLocale fr(QLocale::French, QLocale::France);
+ const QLocale hi(QLocale::Hindi, QLocale::India);
+ for (const QByteArray &id : idList) {
+ if (id == "localtime"_ba || id == "posixrules"_ba || !allList.contains(id))
+ continue;
+ QTimeZone zone = QTimeZone(id);
+ if (!zone.isValid())
+ continue;
+ for (const auto type : types) {
+ QTest::addRow("%s@fr_FR/%s", id.constData(), typeName(type))
+ << zone << fr << type;
+ QTest::addRow("%s@hi_IN/%s", id.constData(), typeName(type))
+ << zone << hi << type;
+ }
+ }
+}
+
+void tst_QTimeZoneBackend::roundtripDisplayNames()
+{
+ QFETCH(const QTimeZone, zone);
+ QFETCH(const QLocale, locale);
+ QFETCH(const QTimeZone::TimeType, type);
+ static const QDateTime jan = QDateTime(QDate(2015, 1, 1), QTime(12, 0), QTimeZone::UTC);
+ static const QDateTime jul = QDateTime(QDate(2015, 7, 1), QTime(12, 0), QTimeZone::UTC);
+ const QDateTime dt = zone.isDaylightTime(jul) == (type == QTimeZone::DaylightTime) ? jul : jan;
+
+ // Some zones exercise region format.
+ const QString name = zone.displayName(type, QTimeZone::LongName, locale);
+ if (!name.isEmpty()) {
+ const auto tran = QTimeZonePrivate::extractPrivate(zone)->data(type);
+ const qint64 when = tran.atMSecsSinceEpoch == QTimeZonePrivate::invalidMSecs()
+ ? dt.toMSecsSinceEpoch() : tran.atMSecsSinceEpoch;
+ const QString extended = name + "some spurious cruft"_L1;
+ auto match =
+ QTimeZonePrivate::findLongNamePrefix(extended, locale, when);
+ if (!match)
+ match = QTimeZonePrivate::findLongNamePrefix(extended, locale);
+ if (!match)
+ match = QTimeZonePrivate::findNarrowOffsetPrefix(extended, locale);
+ if (!match)
+ match = QTimeZonePrivate::findLongUtcPrefix(extended);
+ auto report = qScopeGuard([=]() {
+ qDebug() << "At" << QDateTime::fromMSecsSinceEpoch(when, QTimeZone::UTC)
+ << "via" << name;
+ });
+ QCOMPARE(match.nameLength, name.size());
+ report.dismiss();
+#if 0
+ if (match.ianaId != zone.id()) {
+ const QTimeZone found = QTimeZone(match.ianaId);
+ if (QTimeZonePrivate::extractPrivate(found)->offsetFromUtc(when)
+ != QTimeZonePrivate::extractPrivate(zone)->offsetFromUtc(when)) {
+ // For DST, some zones haven't done it in ages, so tran may be ancient.
+ // Meanwhile, match.ianaId is typically the canonical zone for a metazone.
+ // That, in turn, may not have been doing DST when zone was.
+ // So we can't rely on a match, but can report the mismatches.
+ qDebug() << "Long name" << name << "on"
+ << QTimeZonePrivate::extractPrivate(zone)->offsetFromUtc(when)
+ << "at" << QDateTime::fromMSecsSinceEpoch(when, QTimeZone::UTC)
+ << "got" << match.ianaId << "on"
+ << QTimeZonePrivate::extractPrivate(found)->offsetFromUtc(when);
+ // There are also some absurdly over-generic names, that lead to
+ // ambiguities, e.g. "heure : West"
+ }
+ }
+#endif // Debug code
+ } else if (type != QTimeZone::DaylightTime) { /* Zones with no DST have no DST-name */
+ qDebug("Empty display name");
+ }
+}
+
+// Relies on local variable names: zone tzp and locale enUS.
+#define ZONE_DNAME_CHECK(type, name, val) \
+ QCOMPARE(tzp.displayName(QTimeZone::type, QTimeZone::name, enUS), val);
+
+void tst_QTimeZoneBackend::icuTest()
+{
+#if QT_CONFIG(icu) && !QT_CONFIG(timezone_tzdb) && (defined(Q_OS_VXWORKS) || !defined(Q_OS_UNIX))
+ // Known datetimes
+ qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
+ qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
+
+ // Test default constructor
+ QIcuTimeZonePrivate tzpd;
+ QVERIFY(tzpd.isValid());
+
+ // Test invalid is not available:
+ QVERIFY(!tzpd.isTimeZoneIdAvailable("Gondwana/Erewhon"));
+ // and construction gives an invalid result:
+ QIcuTimeZonePrivate tzpi("Gondwana/Erewhon");
+ QCOMPARE(tzpi.isValid(), false);
+
+ // Test named constructor
+ QIcuTimeZonePrivate tzp("Europe/Berlin");
+ QVERIFY(tzp.isValid());
+
+ // Only test names in debug mode, names used can vary by ICU version installed
+ if constexpr (debug) {
+ // Test display names by type
+ QLocale enUS("en_US");
+ ZONE_DNAME_CHECK(StandardTime, LongName, u"Central European Standard Time");
+ ZONE_DNAME_CHECK(StandardTime, ShortName, u"GMT+01:00");
+ ZONE_DNAME_CHECK(StandardTime, OffsetName, u"UTC+01:00");
+ ZONE_DNAME_CHECK(DaylightTime, LongName, u"Central European Summer Time");
+ ZONE_DNAME_CHECK(DaylightTime, ShortName, u"GMT+02:00");
+ ZONE_DNAME_CHECK(DaylightTime, OffsetName, u"UTC+02:00");
+ // ICU C api does not support Generic Time yet, C++ api does
+ ZONE_DNAME_CHECK(GenericTime, LongName, u"Central European Standard Time");
+ ZONE_DNAME_CHECK(GenericTime, ShortName, u"GMT+01:00");
+ ZONE_DNAME_CHECK(GenericTime, OffsetName, u"UTC+01:00");
+
+ // Test Abbreviations
+ QCOMPARE(tzp.abbreviation(std), u"CET");
+ QCOMPARE(tzp.abbreviation(dst), u"CEST");
+ }
+
+ testCetPrivate(tzp);
+ if (QTest::currentTestFailed())
+ return;
+ testEpochTranPrivate(QIcuTimeZonePrivate("America/Toronto"));
+#endif // ICU without tzdb, on VxWorks or not on Unix
+}
+
+void tst_QTimeZoneBackend::tzTest()
+{
+#if defined(Q_OS_UNIX) && !(QT_CONFIG(timezone_tzdb) || defined(Q_OS_DARWIN) \
+ || defined(Q_OS_ANDROID) || defined(Q_OS_VXWORKS))
+ const auto UTC = QTimeZone::UTC;
+ // Known datetimes
+ qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
+ qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
+
+ // Test default constructor
+ QTzTimeZonePrivate tzpd;
+ QVERIFY(tzpd.isValid());
+
+ // Test invalid constructor
+ QTzTimeZonePrivate tzpi("Gondwana/Erewhon");
+ QVERIFY(!tzpi.isValid());
+
+ // Test named constructor
+ QTzTimeZonePrivate tzp("Europe/Berlin");
+ QVERIFY(tzp.isValid());
+
+ // Test POSIX-format value for $TZ:
+ QTimeZone tzposix("MET-1METDST-2,M3.5.0/02:00:00,M10.5.0/03:00:00");
+ QVERIFY(tzposix.isValid());
+ QVERIFY(tzposix.hasDaylightTime());
+
+ // Cope with stray space at start of value (QTBUG-135109):
+ QTimeZone syd(" AEST-10AEDT,M10.1.0,M4.1.0/3");
+ QVERIFY(syd.isValid());
+ QVERIFY(syd.hasDaylightTime());
+
+ // RHEL has been seen with this as Africa/Casablanca's POSIX rule:
+ QTzTimeZonePrivate permaDst("<+00>0<+01>,0/0,J365/25");
+ const QTimeZone utcP1("UTC+01:00"); // Should always have same offset as permaDst
+ QVERIFY(permaDst.isValid());
+ QVERIFY(permaDst.hasDaylightTime());
+ QVERIFY(permaDst.isDaylightTime(QDate(2020, 1, 1).startOfDay(utcP1).toMSecsSinceEpoch()));
+ QVERIFY(permaDst.isDaylightTime(QDate(2020, 12, 31).endOfDay(utcP1).toMSecsSinceEpoch()));
+ // Note that the final /25 could be misunderstood as putting a fall-back at
+ // 1am on the next year's Jan 1st; check we don't do that:
+ QVERIFY(permaDst.isDaylightTime(
+ QDateTime(QDate(2020, 1, 1), QTime(1, 30), utcP1).toMSecsSinceEpoch()));
+ // It shouldn't have any transitions. QTimeZone::hasTransitions() only says
+ // whether the backend supports them, so ask for transitions in a wide
+ // enough interval that one would show up, if there are any:
+ QVERIFY(permaDst.transitions(QDate(2015, 1, 1).startOfDay(UTC).toMSecsSinceEpoch(),
+ QDate(2020, 1, 1).startOfDay(UTC).toMSecsSinceEpoch()
+ ).isEmpty());
+
+ QTimeZone tzBrazil("BRT+3"); // parts of Northern Brazil, as a POSIX rule
+ QVERIFY(tzBrazil.isValid());
+ QCOMPARE(tzBrazil.offsetFromUtc(QDateTime(QDate(1111, 11, 11).startOfDay())), -10800);
+
+ // Test display names by type, either ICU or abbreviation only
+ QLocale enUS(u"en_US");
+ // Only test names in debug mode, names used can vary by ICU version installed
+ if constexpr (debug) {
+#if QT_CONFIG(icu)
+ ZONE_DNAME_CHECK(StandardTime, LongName, "Central European Standard Time");
+ ZONE_DNAME_CHECK(StandardTime, ShortName, "GMT+01:00");
+ ZONE_DNAME_CHECK(StandardTime, OffsetName, "UTC+01:00");
+ ZONE_DNAME_CHECK(DaylightTime, LongName, "Central European Summer Time");
+ ZONE_DNAME_CHECK(DaylightTime, ShortName, "GMT+02:00");
+ ZONE_DNAME_CHECK(DaylightTime, OffsetName, "UTC+02:00");
+ // ICU C api does not support Generic Time yet, C++ api does
+ ZONE_DNAME_CHECK(GenericTime, LongName, "Central European Standard Time");
+ ZONE_DNAME_CHECK(GenericTime, ShortName, "GMT+01:00");
+ ZONE_DNAME_CHECK(GenericTime, OffsetName, "UTC+01:00");
+#else
+ ZONE_DNAME_CHECK(StandardTime, LongName, "CET");
+ ZONE_DNAME_CHECK(StandardTime, ShortName, "CET");
+ ZONE_DNAME_CHECK(StandardTime, OffsetName, "CET");
+ ZONE_DNAME_CHECK(DaylightTime, LongName, "CEST");
+ ZONE_DNAME_CHECK(DaylightTime, ShortName, "CEST");
+ ZONE_DNAME_CHECK(DaylightTime, OffsetName, "CEST");
+ ZONE_DNAME_CHECK(GenericTime, LongName, "CET");
+ ZONE_DNAME_CHECK(GenericTime, ShortName, "CET");
+ ZONE_DNAME_CHECK(GenericTime, OffsetName, "CET");
+#endif // icu
+
+ // Test Abbreviations
+ QCOMPARE(tzp.abbreviation(std), u"CET");
+ QCOMPARE(tzp.abbreviation(dst), u"CEST");
+ }
+
+ testCetPrivate(tzp);
+ if (QTest::currentTestFailed())
+ return;
+ testEpochTranPrivate(QTzTimeZonePrivate("America/Toronto"));
+ if (QTest::currentTestFailed())
+ return;
+
+ // Test first and last transition rule
+ // Warning: This could vary depending on age of TZ file!
+
+ // Test low date uses first rule found
+ constexpr qint64 ancient = -Q_INT64_C(9999999999999);
+ // Note: Depending on the OS in question, the database may be carrying the
+ // Local Mean Time. which for Berlin is 0:53:28
+ QTimeZonePrivate::Data dat = tzp.data(ancient);
+ QCOMPARE(dat.atMSecsSinceEpoch, ancient);
+ QCOMPARE(dat.daylightTimeOffset, 0);
+ if (dat.abbreviation == u"LMT") {
+ QCOMPARE(dat.standardTimeOffset, 3208);
+ } else {
+ QCOMPARE(dat.standardTimeOffset, 3600);
+
+ constexpr qint64 invalidTime = std::numeric_limits<qint64>::min();
+ constexpr int invalidOffset = std::numeric_limits<int>::min();
+ // Test previous to low value is invalid
+ dat = tzp.previousTransition(ancient);
+ QCOMPARE(dat.atMSecsSinceEpoch, invalidTime);
+ QCOMPARE(dat.standardTimeOffset, invalidOffset);
+ QCOMPARE(dat.daylightTimeOffset, invalidOffset);
+ }
+
+ dat = tzp.nextTransition(ancient);
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch,
+ QTimeZone::fromSecondsAheadOfUtc(3600)),
+ QDateTime(QDate(1893, 4, 1), QTime(0, 6, 32),
+ QTimeZone::fromSecondsAheadOfUtc(3600)));
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 0);
+
+ // Date-times late enough to exercise POSIX rules:
+ qint64 stdHi = QDate(2100, 1, 1).startOfDay(UTC).toMSecsSinceEpoch();
+ qint64 dstHi = QDate(2100, 6, 1).startOfDay(UTC).toMSecsSinceEpoch();
+ // Relevant last Sundays in October and March:
+ QCOMPARE(Qt::DayOfWeek(QDate(2099, 10, 25).dayOfWeek()), Qt::Sunday);
+ QCOMPARE(Qt::DayOfWeek(QDate(2100, 3, 28).dayOfWeek()), Qt::Sunday);
+ QCOMPARE(Qt::DayOfWeek(QDate(2100, 10, 31).dayOfWeek()), Qt::Sunday);
+
+ dat = tzp.data(stdHi);
+ QCOMPARE(dat.atMSecsSinceEpoch - stdHi, qint64(0));
+ QCOMPARE(dat.offsetFromUtc, 3600);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 0);
+
+ dat = tzp.data(dstHi);
+ QCOMPARE(dat.atMSecsSinceEpoch - dstHi, qint64(0));
+ QCOMPARE(dat.offsetFromUtc, 7200);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 3600);
+
+ dat = tzp.previousTransition(stdHi);
+ QCOMPARE(dat.abbreviation, u"CET");
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2099, 10, 25), QTime(3, 0), QTimeZone::fromSecondsAheadOfUtc(7200)));
+ QCOMPARE(dat.offsetFromUtc, 3600);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 0);
+
+ dat = tzp.previousTransition(dstHi);
+ QCOMPARE(dat.abbreviation, u"CEST");
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2100, 3, 28), QTime(2, 0), QTimeZone::fromSecondsAheadOfUtc(3600)));
+ QCOMPARE(dat.offsetFromUtc, 7200);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 3600);
+
+ dat = tzp.nextTransition(stdHi);
+ QCOMPARE(dat.abbreviation, u"CEST");
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2100, 3, 28), QTime(2, 0), QTimeZone::fromSecondsAheadOfUtc(3600)));
+ QCOMPARE(dat.offsetFromUtc, 7200);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 3600);
+
+ dat = tzp.nextTransition(dstHi);
+ QCOMPARE(dat.abbreviation, u"CET");
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch,
+ QTimeZone::fromSecondsAheadOfUtc(3600)),
+ QDateTime(QDate(2100, 10, 31), QTime(3, 0), QTimeZone::fromSecondsAheadOfUtc(7200)));
+ QCOMPARE(dat.offsetFromUtc, 3600);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 0);
+
+ // Test TZ timezone vs UTC timezone for non-whole-hour negative offset:
+ QTzTimeZonePrivate tztz1("America/Caracas");
+ QUtcTimeZonePrivate tzutc1("UTC-04:30");
+ QVERIFY(tztz1.isValid());
+ QVERIFY(tzutc1.isValid());
+ QTzTimeZonePrivate::Data datatz1 = tztz1.data(std);
+ QTzTimeZonePrivate::Data datautc1 = tzutc1.data(std);
+ QCOMPARE(datatz1.offsetFromUtc, datautc1.offsetFromUtc);
+
+ // Test TZ timezone vs UTC timezone for non-whole-hour positive offset:
+ QTzTimeZonePrivate tztz2k("Asia/Kolkata"); // New name
+ QTzTimeZonePrivate tztz2c("Asia/Calcutta"); // Legacy name
+ // Can't assign QtzTZP, so use a reference; prefer new name.
+ QTzTimeZonePrivate &tztz2 = tztz2k.isValid() ? tztz2k : tztz2c;
+ QUtcTimeZonePrivate tzutc2("UTC+05:30");
+ QVERIFY2(tztz2.isValid(), tztz2.id().constData());
+ QVERIFY(tzutc2.isValid());
+ QTzTimeZonePrivate::Data datatz2 = tztz2.data(std);
+ QTzTimeZonePrivate::Data datautc2 = tzutc2.data(std);
+ QCOMPARE(datatz2.offsetFromUtc, datautc2.offsetFromUtc);
+
+ // Test a timezone with an abbreviation that isn't all letters:
+ QTzTimeZonePrivate tzBarnaul("Asia/Barnaul");
+ if (tzBarnaul.isValid()) {
+ QCOMPARE(tzBarnaul.data(std).abbreviation, u"+07");
+
+ // first full day of the new rule (tzdata2016b)
+ QDateTime dt(QDate(2016, 3, 28), QTime(0, 0), UTC);
+ QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, u"+07");
+ }
+#endif // Unix && !(timezone_tzdb || Darwin || Android || VxWorks)
+}
+
+void tst_QTimeZoneBackend::macTest()
+{
+#if defined(Q_OS_DARWIN) && !QT_CONFIG(timezone_tzdb)
+ // Known datetimes
+ qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
+ qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
+
+ // Test default constructor
+ QMacTimeZonePrivate tzpd;
+ QVERIFY(tzpd.isValid());
+
+ // Test invalid constructor
+ QMacTimeZonePrivate tzpi("Gondwana/Erewhon");
+ QCOMPARE(tzpi.isValid(), false);
+
+ // Test named constructor
+ QMacTimeZonePrivate tzp("Europe/Berlin");
+ QVERIFY(tzp.isValid());
+
+ // Only test names in debug mode, names used can vary by version
+ if constexpr (debug) {
+ // Test display names by type
+ QLocale enUS(u"en_US");
+ ZONE_DNAME_CHECK(StandardTime, LongName, "Central European Standard Time");
+ ZONE_DNAME_CHECK(StandardTime, ShortName, "GMT+01:00");
+ ZONE_DNAME_CHECK(StandardTime, OffsetName, "UTC+01:00");
+ ZONE_DNAME_CHECK(DaylightTime, LongName, "Central European Summer Time");
+ ZONE_DNAME_CHECK(DaylightTime, ShortName, "GMT+02:00");
+ ZONE_DNAME_CHECK(DaylightTime, OffsetName, "UTC+02:00");
+ // ICU C api does not support Generic Time yet, C++ api does
+ ZONE_DNAME_CHECK(GenericTime, LongName, "Central European Time");
+ ZONE_DNAME_CHECK(GenericTime, ShortName, "Germany Time");
+ ZONE_DNAME_CHECK(GenericTime, OffsetName, "UTC+01:00");
+
+ // Test Abbreviations
+ QCOMPARE(tzp.abbreviation(std), u"CET");
+ QCOMPARE(tzp.abbreviation(dst), u"CEST");
+ }
+
+ testCetPrivate(tzp);
+ if (QTest::currentTestFailed())
+ return;
+ testEpochTranPrivate(QMacTimeZonePrivate("America/Toronto"));
+#endif // Q_OS_DARWIN without tzdb
+}
+
+void tst_QTimeZoneBackend::winTest()
+{
+#if defined(USING_WIN_TZ)
+ // Known datetimes
+ qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
+ qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), QTimeZone::UTC).toMSecsSinceEpoch();
+
+ // Test default constructor
+ QWinTimeZonePrivate tzpd;
+ if constexpr (debug)
+ qDebug() << "System ID = " << tzpd.id()
+ << tzpd.displayName(QTimeZone::StandardTime, QTimeZone::LongName, QLocale())
+ << tzpd.displayName(QTimeZone::GenericTime, QTimeZone::LongName, QLocale());
+ QVERIFY(tzpd.isValid());
+
+ // Test invalid constructor
+ QWinTimeZonePrivate tzpi("Gondwana/Erewhon");
+ QCOMPARE(tzpi.isValid(), false);
+
+ // Test named constructor
+ QWinTimeZonePrivate tzp("Europe/Berlin");
+ QVERIFY(tzp.isValid());
+
+ // Only test names in debug mode, names used can vary by version
+ if constexpr (debug) {
+ // Test display names by type
+ QLocale enUS(u"en_US");
+ ZONE_DNAME_CHECK(StandardTime, LongName, "W. Europe Standard Time");
+ ZONE_DNAME_CHECK(StandardTime, ShortName, "W. Europe Standard Time");
+ ZONE_DNAME_CHECK(StandardTime, OffsetName, "UTC+01:00");
+ ZONE_DNAME_CHECK(DaylightTime, LongName, "W. Europe Daylight Time");
+ ZONE_DNAME_CHECK(DaylightTime, ShortName, "W. Europe Daylight Time");
+ ZONE_DNAME_CHECK(DaylightTime, OffsetName, "UTC+02:00");
+ ZONE_DNAME_CHECK(GenericTime, LongName,
+ "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna");
+ ZONE_DNAME_CHECK(GenericTime, ShortName,
+ "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna");
+ ZONE_DNAME_CHECK(GenericTime, OffsetName, "UTC+01:00");
+
+ // Test Abbreviations
+ QCOMPARE(tzp.abbreviation(std), u"CET");
+ QCOMPARE(tzp.abbreviation(dst), u"CEST");
+ }
+
+ testCetPrivate(tzp);
+ if (QTest::currentTestFailed())
+ return;
+ testEpochTranPrivate(QWinTimeZonePrivate("America/Toronto"));
+#endif // TZ backend
+}
+
+#undef ZONE_DNAME_CHECK
+
+// Test each private produces the same basic results for CET
+void tst_QTimeZoneBackend::testCetPrivate(const QTimeZonePrivate &tzp)
+{
+ // Known datetimes
+ const auto UTC = QTimeZone::UTC;
+ const auto eastOneHour = QTimeZone::fromSecondsAheadOfUtc(3600);
+ qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
+ qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
+ qint64 prev = QDateTime(QDate(2011, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch();
+
+ QCOMPARE(tzp.offsetFromUtc(std), 3600);
+ QCOMPARE(tzp.offsetFromUtc(dst), 7200);
+
+ QCOMPARE(tzp.standardTimeOffset(std), 3600);
+ QCOMPARE(tzp.standardTimeOffset(dst), 3600);
+
+ QCOMPARE(tzp.daylightTimeOffset(std), 0);
+ QCOMPARE(tzp.daylightTimeOffset(dst), 3600);
+
+ QCOMPARE(tzp.hasDaylightTime(), true);
+ QCOMPARE(tzp.isDaylightTime(std), false);
+ QCOMPARE(tzp.isDaylightTime(dst), true);
+
+ QTimeZonePrivate::Data dat = tzp.data(std);
+ QCOMPARE(dat.atMSecsSinceEpoch, std);
+ QCOMPARE(dat.offsetFromUtc, 3600);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 0);
+ QCOMPARE(dat.abbreviation, tzp.abbreviation(std));
+
+ dat = tzp.data(dst);
+ QCOMPARE(dat.atMSecsSinceEpoch, dst);
+ QCOMPARE(dat.offsetFromUtc, 7200);
+ QCOMPARE(dat.standardTimeOffset, 3600);
+ QCOMPARE(dat.daylightTimeOffset, 3600);
+ QCOMPARE(dat.abbreviation, tzp.abbreviation(dst));
+
+ // Only test transitions if host system supports them
+ if (tzp.hasTransitions()) {
+ QTimeZonePrivate::Data tran = tzp.nextTransition(std);
+ // 2012-03-25 02:00 CET, +1 -> +2
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2012, 3, 25), QTime(2, 0), eastOneHour));
+ QCOMPARE(tran.offsetFromUtc, 7200);
+ QCOMPARE(tran.standardTimeOffset, 3600);
+ QCOMPARE(tran.daylightTimeOffset, 3600);
+
+ tran = tzp.nextTransition(dst);
+ // 2012-10-28 03:00 CEST, +2 -> +1
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2012, 10, 28), QTime(3, 0),
+ QTimeZone::fromSecondsAheadOfUtc(2 * 3600)));
+ QCOMPARE(tran.offsetFromUtc, 3600);
+ QCOMPARE(tran.standardTimeOffset, 3600);
+ QCOMPARE(tran.daylightTimeOffset, 0);
+
+ tran = tzp.previousTransition(std);
+ // 2011-10-30 03:00 CEST, +2 -> +1
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2011, 10, 30), QTime(3, 0),
+ QTimeZone::fromSecondsAheadOfUtc(2 * 3600)));
+ QCOMPARE(tran.offsetFromUtc, 3600);
+ QCOMPARE(tran.standardTimeOffset, 3600);
+ QCOMPARE(tran.daylightTimeOffset, 0);
+
+ tran = tzp.previousTransition(dst);
+ // 2012-03-25 02:00 CET, +1 -> +2 (again)
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(2012, 3, 25), QTime(2, 0), eastOneHour));
+ QCOMPARE(tran.offsetFromUtc, 7200);
+ QCOMPARE(tran.standardTimeOffset, 3600);
+ QCOMPARE(tran.daylightTimeOffset, 3600);
+
+ QTimeZonePrivate::DataList expected;
+ // 2011-03-27 02:00 CET, +1 -> +2
+ tran.atMSecsSinceEpoch = QDateTime(QDate(2011, 3, 27), QTime(2, 0),
+ eastOneHour).toMSecsSinceEpoch();
+ tran.offsetFromUtc = 7200;
+ tran.standardTimeOffset = 3600;
+ tran.daylightTimeOffset = 3600;
+ expected << tran;
+ // 2011-10-30 03:00 CEST, +2 -> +1
+ tran.atMSecsSinceEpoch = QDateTime(QDate(2011, 10, 30), QTime(3, 0),
+ QTimeZone::fromSecondsAheadOfUtc(2 * 3600)
+ ).toMSecsSinceEpoch();
+ tran.offsetFromUtc = 3600;
+ tran.standardTimeOffset = 3600;
+ tran.daylightTimeOffset = 0;
+ expected << tran;
+ QTimeZonePrivate::DataList result = tzp.transitions(prev, std);
+ QCOMPARE(result.size(), expected.size());
+ for (int i = 0; i < expected.size(); ++i) {
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(result.at(i).atMSecsSinceEpoch, eastOneHour),
+ QDateTime::fromMSecsSinceEpoch(expected.at(i).atMSecsSinceEpoch, eastOneHour));
+ QCOMPARE(result.at(i).offsetFromUtc, expected.at(i).offsetFromUtc);
+ QCOMPARE(result.at(i).standardTimeOffset, expected.at(i).standardTimeOffset);
+ QCOMPARE(result.at(i).daylightTimeOffset, expected.at(i).daylightTimeOffset);
+ }
+ }
+}
+
+// Needs a zone with DST around the epoch; currently America/Toronto (EST5EDT)
+void tst_QTimeZoneBackend::testEpochTranPrivate(const QTimeZonePrivate &tzp)
+{
+ if (!tzp.hasTransitions())
+ return; // test only viable for transitions
+
+ const auto UTC = QTimeZone::UTC;
+ const auto hour = std::chrono::hours{1};
+ QTimeZonePrivate::Data tran = tzp.nextTransition(0); // i.e. first after epoch
+ // 1970-04-26 02:00 EST, -5 -> -4
+ const QDateTime after = QDateTime(QDate(1970, 4, 26), QTime(2, 0),
+ QTimeZone::fromDurationAheadOfUtc(-5 * hour));
+ const QDateTime found = QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC);
+#ifdef USING_WIN_TZ // MS gets the date wrong: 5th April instead of 26th.
+ QCOMPARE(found.toOffsetFromUtc(-5 * 3600).time(), after.time());
+#else
+ QCOMPARE(found, after);
+#endif
+ QCOMPARE(tran.offsetFromUtc, -4 * 3600);
+ QCOMPARE(tran.standardTimeOffset, -5 * 3600);
+ QCOMPARE(tran.daylightTimeOffset, 3600);
+
+ // Pre-epoch time-zones might not be supported at all:
+ tran = tzp.nextTransition(QDateTime(QDate(1601, 1, 1), QTime(0, 0), UTC).toMSecsSinceEpoch());
+ if (tran.atMSecsSinceEpoch != QTimeZonePrivate::invalidMSecs()
+ // Toronto *did* have a transition before 1970 (DST since 1918):
+ && tran.atMSecsSinceEpoch < 0) {
+ // ... but, if they are, we should be able to search back to them:
+ tran = tzp.previousTransition(0); // i.e. last before epoch
+ // 1969-10-26 02:00 EDT, -4 -> -5
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, UTC),
+ QDateTime(QDate(1969, 10, 26), QTime(2, 0),
+ QTimeZone::fromDurationAheadOfUtc(-4 * hour)));
+ QCOMPARE(tran.offsetFromUtc, -5 * 3600);
+ QCOMPARE(tran.standardTimeOffset, -5 * 3600);
+ QCOMPARE(tran.daylightTimeOffset, 0);
+ } else {
+ // Do not use QSKIP(): that would discard the rest of this sub-test's caller.
+ qDebug() << "No support for pre-epoch time-zone transitions";
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_QTimeZoneBackend)
+#include "tst_qtimezonebackend.moc"
diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp
index 6ff905cb1cb..452b7dcef88 100644
--- a/tests/auto/network/access/http2/tst_http2.cpp
+++ b/tests/auto/network/access/http2/tst_http2.cpp
@@ -729,11 +729,12 @@ void tst_Http2::earlyError()
: QHttpNetworkConnection::ConnectionTypeHTTP2;
QHttpNetworkConnection connection(1, "127.0.0.1", serverPort, true, false, nullptr,
connectionType);
+#if QT_CONFIG(ssl)
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setAllowedNextProtocols({"h2"});
connection.setSslConfiguration(config);
connection.ignoreSslErrors();
-
+#endif
// SETUP manually setup the QHttpNetworkRequest
QHttpNetworkRequest req;
req.setSsl(true);
@@ -809,11 +810,12 @@ void tst_Http2::abortReply()
: QHttpNetworkConnection::ConnectionTypeHTTP2;
QHttpNetworkConnection connection(1, "127.0.0.1", serverPort, true, false, nullptr,
connectionType);
+#if QT_CONFIG(ssl)
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setAllowedNextProtocols({"h2"});
connection.setSslConfiguration(config);
connection.ignoreSslErrors();
-
+#endif
// SETUP manually setup the QHttpNetworkRequest
QHttpNetworkRequest req;
req.setSsl(true);
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index fdc2dde7921..c2b03b70d9b 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -10290,7 +10290,7 @@ void tst_QNetworkReply::requestWithTimeout()
QSignalSpy spy(reply.data(), &QNetworkReply::errorOccurred);
QCOMPARE(waitForFinish(reply), int(Failure));
QCOMPARE(spy.size(), 1);
- QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError);
+ QCOMPARE(reply->error(), QNetworkReply::TimeoutError);
}
#endif
diff --git a/tests/auto/other/languagechange/tst_languagechange.cpp b/tests/auto/other/languagechange/tst_languagechange.cpp
index 8f99730a192..ab9244c8561 100644
--- a/tests/auto/other/languagechange/tst_languagechange.cpp
+++ b/tests/auto/other/languagechange/tst_languagechange.cpp
@@ -172,10 +172,10 @@ void tst_languageChange::retranslatability_data()
<< "QFileDialog::Back"
<< "QFileDialog::Create New Folder"
<< "QFileDialog::Detail View"
- << "QFileDialog::Files of type:"
+ << "QFileDialog::Files of &type:"
<< "QFileDialog::Forward"
<< "QFileDialog::List View"
- << "QFileDialog::Look in:"
+ << "QFileDialog::&Look in:"
<< "QFileDialog::Open"
<< "QFileDialog::Parent Directory"
<< "QFileDialog::Show "
diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
index 2e92cdffada..305f48c95ee 100644
--- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
+++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
@@ -1793,6 +1793,15 @@ void tst_QAccessibility::spinBoxTest()
QAccessibleTextInterface *textIface = interface->textInterface();
QVERIFY(textIface);
+ QVERIFY(!spinBox->isReadOnly());
+ QVERIFY(interface->state().editable);
+ QVERIFY(!interface->state().readOnly);
+
+ spinBox->setReadOnly(true);
+ QVERIFY(spinBox->isReadOnly());
+ QVERIFY(!interface->state().editable);
+ QVERIFY(interface->state().readOnly);
+
QTestAccessibility::clearEvents();
}
@@ -2985,6 +2994,9 @@ void tst_QAccessibility::listTest()
listView->setModelColumn(1);
listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
listView->resize(400,400);
+ listView->setAccessibleName(QLatin1String("list view's accessible name"));
+ listView->setToolTip(QLatin1String("This list view will be used to test accessibility"));
+ listView->setWhatsThis(QLatin1String("What's this list"));
listView->show();
QVERIFY(QTest::qWaitForWindowExposed(listView));
@@ -2992,6 +3004,10 @@ void tst_QAccessibility::listTest()
QCOMPARE(verifyHierarchy(iface), 0);
QCOMPARE((int)iface->role(), (int)QAccessible::List);
+ QCOMPARE(iface->text(QAccessible::Name), QLatin1String("list view's accessible name"));
+ QCOMPARE(iface->text(QAccessible::Description), QLatin1String("This list view will be used to test accessibility"));
+ QCOMPARE(iface->text(QAccessible::Help), QLatin1String("What's this list"));
+ QCOMPARE(iface->text(QAccessible::Value), QString());
QCOMPARE(iface->childCount(), 3);
{
diff --git a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp
index 5f54471bc0f..50971e27964 100644
--- a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp
+++ b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp
@@ -233,6 +233,10 @@ void tst_QFiledialog::directoryEnteredSignal()
sidebar->setCurrentIndex(secondItem);
QTest::keyPress(sidebar->viewport(), Qt::Key_Return);
QCOMPARE(spyDirectoryEntered.size(), 1);
+ // ensure signal isn't emitted again when clicking on the already active item
+ QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, {},
+ sidebar->visualRect(secondItem).center());
+ QCOMPARE(spyDirectoryEntered.size(), 1);
spyDirectoryEntered.clear();
// lookInCombo
@@ -815,9 +819,9 @@ void tst_QFiledialog::labelText()
QFileDialog fd;
QDialogButtonBox buttonBox;
QPushButton *cancelButton = buttonBox.addButton(QDialogButtonBox::Cancel);
- QCOMPARE(fd.labelText(QFileDialog::LookIn), QString("Look in:"));
+ QCOMPARE(fd.labelText(QFileDialog::LookIn), QString("&Look in:"));
QCOMPARE(fd.labelText(QFileDialog::FileName), QString("File &name:"));
- QCOMPARE(fd.labelText(QFileDialog::FileType), QString("Files of type:"));
+ QCOMPARE(fd.labelText(QFileDialog::FileType), QString("Files of &type:"));
QCOMPARE(fd.labelText(QFileDialog::Accept), QString("&Open")); ///### see task 241462
QCOMPARE(fd.labelText(QFileDialog::Reject), cancelButton->text());
diff --git a/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp b/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp
index cdbf2011a4a..4f37e2490fd 100644
--- a/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp
+++ b/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp
@@ -53,9 +53,9 @@ void tst_QSidebar::selectUrls()
QSidebar qsidebar;
qsidebar.setModelAndUrls(&fsmodel, urls);
- QSignalSpy spy(&qsidebar, SIGNAL(goToUrl(QUrl)));
+ QSignalSpy spy(&qsidebar, &QSidebar::goToUrl);
qsidebar.selectUrl(urls.at(0));
- QCOMPARE(spy.size(), 0);
+ QCOMPARE(spy.size(), 1);
}
void tst_QSidebar::addUrls()
@@ -160,15 +160,19 @@ void tst_QSidebar::addUrls()
void tst_QSidebar::goToUrl()
{
QList<QUrl> urls;
+ const QUrl tempUrl = QUrl::fromLocalFile(QDir::temp().absolutePath());
urls << QUrl::fromLocalFile(QDir::rootPath())
- << QUrl::fromLocalFile(QDir::temp().absolutePath());
+ << tempUrl;
QFileSystemModel fsmodel;
fsmodel.setIconProvider(&defaultIconProvider);
QSidebar qsidebar;
qsidebar.setModelAndUrls(&fsmodel, urls);
qsidebar.show();
- QSignalSpy spy(&qsidebar, SIGNAL(goToUrl(QUrl)));
+ // switch to a different entry first
+ qsidebar.selectUrl(tempUrl);
+
+ QSignalSpy spy(&qsidebar, &QSidebar::goToUrl);
QTest::mousePress(qsidebar.viewport(), Qt::LeftButton, {},
qsidebar.visualRect(qsidebar.model()->index(0, 0)).center());
QCOMPARE(spy.size(), 1);
diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp
index fd8f8bd37d8..b8eab0a94fa 100644
--- a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp
+++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp
@@ -128,9 +128,6 @@ void tst_QGraphicsPixmapItem::contains_data()
// public bool contains(QPointF const& point) const
void tst_QGraphicsPixmapItem::contains()
{
- if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
- QSKIP("Wayland: This fails. Figure out why.");
-
QFETCH(QPixmap, pixmap);
QFETCH(QPointF, point);
QFETCH(bool, contains);
diff --git a/tests/auto/widgets/kernel/qapplication/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/CMakeLists.txt
index 717daf4bcd2..a4f598f3b74 100644
--- a/tests/auto/widgets/kernel/qapplication/CMakeLists.txt
+++ b/tests/auto/widgets/kernel/qapplication/CMakeLists.txt
@@ -21,9 +21,10 @@ qt_internal_add_executable(apphelper_widgets
Qt::Widgets
)
set_target_properties(apphelper_widgets PROPERTIES OUTPUT_NAME apphelper)
-set_target_properties(tst_qapplication PROPERTIES
- QT_ANDROID_EXTRA_LIBS ${CMAKE_CURRENT_BINARY_DIR}/libmodal_helper_${ANDROID_ABI}.so
-)
+
+set_property(TARGET tst_qapplication PROPERTY QT_ANDROID_EXTRA_LIBS
+ ${CMAKE_CURRENT_BINARY_DIR}/libdesktopsettingsaware_helper_${ANDROID_ABI}.so
+ ${CMAKE_CURRENT_BINARY_DIR}/libmodal_helper_${ANDROID_ABI}.so)
if(QT_FEATURE_library)
qt_internal_add_cmake_library(apphelper_widgets_plugin
diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp
index 6859f22c044..10a67daa02a 100644
--- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp
+++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp
@@ -845,12 +845,12 @@ void tst_QComboBox::virtualAutocompletion()
QKeyEvent kp3(QEvent::KeyPress, Qt::Key_R, {}, "r");
QKeyEvent kr3(QEvent::KeyRelease, Qt::Key_R, {}, "r");
- QTest::qWait(QApplication::keyboardInputInterval());
QApplication::sendEvent(testWidget, &kp3);
+ QTest::qWait(QApplication::keyboardInputInterval());
QApplication::sendEvent(testWidget, &kr3);
QTRY_COMPARE(testWidget->currentIndex(), 3);
- QTest::qWait(QApplication::keyboardInputInterval());
+ QTest::qWait(2 * QApplication::keyboardInputInterval());
testWidget->view()->setKeyboardSearchFlags(Qt::MatchContains | Qt::MatchWrap);
QApplication::sendEvent(testWidget, &kp3);
QApplication::sendEvent(testWidget, &kr3);
@@ -2247,10 +2247,11 @@ void tst_QComboBox::ignoreWheelEvents()
QFETCH(bool, allowWheelScrolling);
+ AllowWheelScrollStyle style(allowWheelScrolling);
WheelEventTestWidget widget;
QComboBox *comboBox = new QComboBox(&widget);
comboBox->addItems({ "0", "1" });
- comboBox->setStyle(new AllowWheelScrollStyle(allowWheelScrolling));
+ comboBox->setStyle(&style);
widget.show();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
index 8ace9592141..ad9db868235 100644
--- a/tests/manual/CMakeLists.txt
+++ b/tests/manual/CMakeLists.txt
@@ -6,7 +6,6 @@ if(UIKIT)
return()
endif()
-add_subdirectory(assets)
add_subdirectory(corelib)
add_subdirectory(filetest)
# diaglib is broken in dev due to missing
diff --git a/tests/manual/assets/CMakeLists.txt b/tests/manual/assets/CMakeLists.txt
deleted file mode 100644
index 03643aa8537..00000000000
--- a/tests/manual/assets/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-# Copyright (C) 2024 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-add_subdirectory(downloader)
diff --git a/tests/manual/assets/assets.pro b/tests/manual/assets/assets.pro
deleted file mode 100644
index 43f09ba46e6..00000000000
--- a/tests/manual/assets/assets.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE=subdirs
-
-SUBDIRS = downloader
diff --git a/tests/manual/assets/downloader/CMakeLists.txt b/tests/manual/assets/downloader/CMakeLists.txt
deleted file mode 100644
index b95161ac02d..00000000000
--- a/tests/manual/assets/downloader/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (C) 2024 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
-
-#####################################################################
-## tst_manual_downloader Binary:
-#####################################################################
-
-qt_internal_add_manual_test(tst_manual_downloader
- GUI
- SOURCES
- main.cpp
- LIBRARIES
- Qt::ExamplesAssetDownloaderPrivate
- Qt::Widgets
-)
diff --git a/tests/manual/assets/downloader/downloader.pro b/tests/manual/assets/downloader/downloader.pro
deleted file mode 100644
index 53976c538f0..00000000000
--- a/tests/manual/assets/downloader/downloader.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-QT += examples_asset_downloader-private widgets
-
-TARGET = tst_manual_downloader
-
-SOURCES += main.cpp
diff --git a/tests/manual/assets/downloader/main.cpp b/tests/manual/assets/downloader/main.cpp
deleted file mode 100644
index d9d711cfce5..00000000000
--- a/tests/manual/assets/downloader/main.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-
-#include <QtExamplesAssetDownloader/assetdownloader.h>
-
-#include <QApplication>
-#include <QMessageBox>
-#include <QProgressDialog>
-
-using namespace Assets::Downloader;
-
-int main(int argc, char *argv[])
-{
- QApplication app(argc, argv);
- app.setOrganizationName("QtProject");
- app.setApplicationName("Asset Downloader");
-
- QProgressDialog progress;
- progress.setAutoClose(false);
- progress.setRange(0, 0);
- QObject::connect(&progress, &QProgressDialog::canceled, &app, &QApplication::quit);
-
- AssetDownloader downloader;
- downloader.setJsonFileName("car-configurator-assets-v1.json");
- downloader.setZipFileName("car-configurator-assets-v1.zip");
- downloader.setDownloadBase(QUrl("https://download.qt.io/learning/examples/"));
-
- QObject::connect(&downloader, &AssetDownloader::started,
- &progress, &QProgressDialog::show);
- QObject::connect(&downloader, &AssetDownloader::progressChanged, &progress, [&progress](
- int progressValue, int progressMaximum, const QString &progressText) {
- progress.setLabelText(progressText);
- progress.setMaximum(progressMaximum);
- progress.setValue(progressValue);
- });
- QObject::connect(&downloader, &AssetDownloader::finished, &progress, [&](bool success) {
- progress.reset();
- progress.hide();
- if (success)
- QMessageBox::information(nullptr, "Asset Downloader", "Download Finished Successfully.");
- else
- QMessageBox::warning(nullptr, "Asset Downloader", "Download Finished with an Error.");
- });
-
- downloader.start();
-
- return app.exec();
-}
diff --git a/tests/manual/corelib/itemmodels/qrangemodel/main.cpp b/tests/manual/corelib/itemmodels/qrangemodel/main.cpp
index 0b1ede8df02..6cb87a4182a 100644
--- a/tests/manual/corelib/itemmodels/qrangemodel/main.cpp
+++ b/tests/manual/corelib/itemmodels/qrangemodel/main.cpp
@@ -170,7 +170,7 @@ class Object : public QObject
Q_PROPERTY(QString display READ display WRITE setDisplay NOTIFY displayChanged)
public:
- Object(int x)
+ Object(int x, QObject *parent = nullptr)
: m_display(QString::number(x))
{}
QString display() const { return m_display; }
@@ -196,6 +196,13 @@ class ModelFactory : public QObject
std::vector<int> numbers = {1, 2, 3, 4, 5};
QList<QString> strings = {u"one"_s, u"two"_s, u"three"_s};
std::array<int, 1000000> largeArray = {};
+ std::array<Object *, 10000> objects = {};
+
+ void updateAllObjects()
+ {
+ for (auto *object : objects)
+ object->setDisplay(QTime::currentTime().toString());
+ }
public slots:
QRangeModel *makeNumbers()
@@ -357,7 +364,7 @@ public slots:
// std::make_unique<QString>("C"),
// };
// return new QRangeModel(std::move(data));
- return nullptr;
+ return new QRangeModel(QList{"Nothing to see here"});
}
QRangeModel *makeUniqueRows()
@@ -410,7 +417,34 @@ public slots:
return new QRangeModel(std::ref(europe));
}
+
+ QRangeModel *makeAutoConnectedObjects()
+ {
+ QRangeModel *model = new QRangeModel(std::ref(objects));
+ QTimer *updater = new QTimer(model);
+ connect(updater, &QTimer::timeout, this, &ModelFactory::updateAllObjects);
+
+ for (int i = 0; i < objects.size(); ++i)
+ objects[i] = new Object(i, model);
+ updater->start(1000);
+ model->setAutoConnectPolicy(QRangeModel::AutoConnectPolicy::OnRead);
+ return model;
+ }
+
+ QRangeModel *makeAutoConnectedConstObjects()
+ {
+ QRangeModel *model = new QRangeModel(&std::as_const(objects));
+ QTimer *updater = new QTimer(model);
+ connect(updater, &QTimer::timeout, this, &ModelFactory::updateAllObjects);
+
+ for (int i = 0; i < objects.size(); ++i)
+ objects[i] = new Object(i, model);
+ updater->start(1000);
+ model->setAutoConnectPolicy(QRangeModel::AutoConnectPolicy::Full);
+ return model;
+ }
};
+
struct QMetaMethodEnumerator
{
struct iterator
@@ -498,14 +532,12 @@ public:
QComboBox *modelPicker = new QComboBox;
connect(modelPicker, &QComboBox::currentIndexChanged, this, &MainWindow::modelChanged);
- // this will implicitly run modelChanged()
- modelPicker->setModel(new QRangeModel(QMetaMethodEnumerator::fromType<ModelFactory>(),
- modelPicker));
- modelPicker->setModelColumn(1);
QToolBar *toolBar = addToolBar(tr("Model Operations"));
toolBar->addWidget(modelPicker);
+ toolBar->addSeparator();
+
QAction *addAction = toolBar->addAction(tr("Add"), this, &MainWindow::onAdd);
addAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ListAdd));
QAction *removeAction = toolBar->addAction(tr("Remove"), this, &MainWindow::onRemove);
@@ -518,6 +550,38 @@ public:
indentAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::FormatIndentMore));
QAction *dedentAction = toolBar->addAction(tr("Move out"), this, &MainWindow::onOut);
dedentAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::FormatIndentLess));
+
+ toolBar->addSeparator();
+
+ QAction *fullAutoConnectAction = new QAction(tr("Full"));
+ fullAutoConnectAction->setCheckable(true);
+ fullAutoConnectAction->setData(QVariant::fromValue(QRangeModel::AutoConnectPolicy::Full));
+ QAction *onReadAutoConnectAction = new QAction(tr("On Read"));
+ onReadAutoConnectAction->setCheckable(true);
+ onReadAutoConnectAction->setData(QVariant::fromValue(QRangeModel::AutoConnectPolicy::OnRead));
+
+ connectionOptions = new QActionGroup(this);
+ connectionOptions->setExclusive(true);
+ connectionOptions->setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional);
+ connectionOptions->addAction(fullAutoConnectAction);
+ connectionOptions->addAction(onReadAutoConnectAction);
+
+ toolBar->addAction(fullAutoConnectAction);
+ toolBar->addAction(onReadAutoConnectAction);
+
+ connect(connectionOptions, &QActionGroup::triggered, this, [this](){
+ QAction *checkedAction = connectionOptions->checkedAction();
+ const auto policy = checkedAction ? checkedAction->data().value<QRangeModel::AutoConnectPolicy>()
+ : QRangeModel::AutoConnectPolicy::None;
+
+ qDebug() << "Switching to policy" << policy;
+ model->setAutoConnectPolicy(policy);
+ });
+
+ // this will implicitly run modelChanged() and update the UI
+ modelPicker->setModel(new QRangeModel(QMetaMethodEnumerator::fromType<ModelFactory>(),
+ modelPicker));
+ modelPicker->setModelColumn(1);
}
private:
@@ -547,6 +611,10 @@ private:
qDebug() << "root object in context" << quickWidget->engine()->contextForObject(quickWidget->rootObject())->objectForName("root");
qDebug() << "list object in context" << quickWidget->engine()->contextForObject(quickWidget->rootObject())->objectForName("list");
#endif
+ for (auto *action : connectionOptions->actions()) {
+ action->setChecked(action->data().value<QRangeModel::AutoConnectPolicy>()
+ == model->autoConnectPolicy());
+ }
}
delete oldModel;
@@ -625,6 +693,7 @@ private:
#ifdef QUICK_UI
QQuickWidget *quickWidget;
#endif
+ QActionGroup *connectionOptions;
};
int main(int argc, char *argv[])
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 47f2ae9bd0f..e28eb5316a8 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -2,7 +2,6 @@ TEMPLATE=subdirs
QT_FOR_CONFIG += network-private gui-private
SUBDIRS = \
-assets \
filetest \
embeddedintoforeignwindow \
foreignwindows \
diff --git a/tests/manual/wasm/eventloop/suspendresumecontrol_auto/main.cpp b/tests/manual/wasm/eventloop/suspendresumecontrol_auto/main.cpp
index 1fd6e0a0c4f..ff3e67e3fc2 100644
--- a/tests/manual/wasm/eventloop/suspendresumecontrol_auto/main.cpp
+++ b/tests/manual/wasm/eventloop/suspendresumecontrol_auto/main.cpp
@@ -3,6 +3,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/private/qwasmsuspendresumecontrol_p.h>
+#include <QtCore/qdebug.h>
#include <qtwasmtestlib.h>
using namespace emscripten;
@@ -20,6 +21,7 @@ private slots:
void reuseTimer();
void cancelTimer();
void deleteTimer();
+ void suspendExclusive();
};
// Verify that a single timer fires
@@ -138,6 +140,49 @@ void WasmSuspendResumeControlTest::deleteTimer()
QWASMSUCCESS();
}
+// Verify that an exclusive suspend resumes for the exclusive event only
+void WasmSuspendResumeControlTest::suspendExclusive()
+{
+ QWasmSuspendResumeControl suspendResume;
+
+ // (re) implement a native timer - this gives us a unique event handler
+ // index which we can suspend exclusively on.
+ bool exclusiveTimerFired = false;
+ auto exclusiveTimerHandler = [&exclusiveTimerFired](emscripten::val) {
+ exclusiveTimerFired = true;
+ };
+ uint32_t exlusiveTimerHandlerIndex = suspendResume.registerEventHandler(std::move(exclusiveTimerHandler));
+
+ std::chrono::milliseconds exclusiveTimerTimeout = timerTimeout * 4;
+ double timoutValue = static_cast<double>(exclusiveTimerTimeout.count());
+ val jsHandler = suspendResume.jsEventHandlerAt(exlusiveTimerHandlerIndex);
+ val::global("window").call<double>("setTimeout", jsHandler, timoutValue);
+
+ // Schedule suppressedTimer to fire before the exclusive timer. Expected
+ // behavior is that it doesn't.
+ bool suppressedTimerFired = false;
+ QWasmTimer suppressedTimer(&suspendResume, [&suppressedTimerFired](){
+ suppressedTimerFired = true;
+ });
+ suppressedTimer.setTimeout(timerTimeout);
+
+ // Suspend exclusively for the exclusive timer, and verify that
+ // the correct timers fired.
+ suspendResume.suspendExclusive(exlusiveTimerHandlerIndex);
+ suspendResume.sendPendingEvents(); // <- also clears exclusive mode
+ if (!exclusiveTimerFired)
+ QWASMFAIL("Exclusive timer did not fire");
+ if (suppressedTimerFired)
+ QWASMFAIL("Suppressed timer did fire");
+
+ // Send (all) events, this should give is the suppressed timer
+ suspendResume.sendPendingEvents();
+ if (!suppressedTimerFired)
+ QWASMFAIL("Suppressed timer did not fire");
+
+ QWASMSUCCESS();
+}
+
int main(int argc, char **argv)
{
auto testObject = std::make_shared<WasmSuspendResumeControlTest>();
diff --git a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp
index ec03c7209a4..1e49847c97f 100644
--- a/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp
+++ b/tests/manual/wasm/qtwasmtestlib/qtwasmtestlib.cpp
@@ -127,7 +127,7 @@ void passTest()
EMSCRIPTEN_BINDINGS(qtwebtestrunner) {
emscripten::function("cleanupTestCase", &cleanupTestCase);
emscripten::function("getTestFunctions", &getTestFunctions);
- emscripten::function("runTestFunction", &runTestFunction);
+ emscripten::function("runTestFunction", &runTestFunction, emscripten::async());
emscripten::function("qtWasmFail", &failTest);
emscripten::function("qtWasmPass", &passTest);
}