diff options
Diffstat (limited to 'tests/auto')
250 files changed, 7219 insertions, 671 deletions
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 3331ad260d..86db954086 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -146,6 +146,21 @@ if(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS AND NOT NO_GUI) "-DTEST_ESCAPING_VALUE_ARG=${escaping_value}" "-DTEST_SPACES_VALUE_ARG=${spaces_value}" ) + + #Run test_android_aar only if the host is Unix and zipinfo is available + find_program(ZIPINFO_EXECUTABLE zipinfo) + if(CMAKE_HOST_UNIX AND ZIPINFO_EXECUTABLE) + _qt_internal_test_expect_pass(test_android_aar + BUILD_OPTIONS + ${multi_abi_vars} + --build-target verify_aar + ) + else() + message(WARNING + "Skipping test_android_aar CMake build test because \ + the host is not Unix or zipinfo is not found") + endif() + return() endif() @@ -399,8 +414,44 @@ endif() _qt_internal_test_expect_pass(test_config_expressions) _qt_internal_test_expect_pass(test_QTP0003) + if(NOT NO_GUI) _qt_internal_test_expect_pass(test_collecting_plugins) endif() _qt_internal_test_expect_pass(test_qt_manual_moc) + +# check if the os is opensuse. If it is, we need to skip tests due to CI problems +set(is_opensuse FALSE) +if(UNIX) + if(EXISTS "/etc/os-release") + file(STRINGS "/etc/os-release" os_release_content) + foreach(line IN LISTS os_release_content) + if(line MATCHES "openSUSE" OR line MATCHES "opensuse") + set(is_opensuse TRUE) + break() + endif() + endforeach() + endif() +endif() + +if(NOT QNX AND NOT WASM AND NOT (WIN32 AND QT_BUILD_MINIMAL_STATIC_TESTS) + AND NOT is_opensuse) + # Since our CI machines are slow, ctest --build-and-test buffers the output + # of the configure step of a test, and the fact that we run all the test + # logic in the configure step, we need to divide the tests into smaller + # chunks to avoid CI stdout timeout errors. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/25790 + _qt_internal_test_expect_pass(test_qt_add_ui_common) + _qt_internal_test_expect_pass(test_qt_add_ui_1) + _qt_internal_test_expect_pass(test_qt_add_ui_2) + _qt_internal_test_expect_pass(test_qt_add_ui_3) + _qt_internal_test_expect_pass(test_qt_add_ui_4) + _qt_internal_test_expect_pass(test_qt_add_ui_5) + _qt_internal_test_expect_pass(test_qt_add_ui_6) + _qt_internal_test_expect_pass(test_qt_add_ui_7) + _qt_internal_test_expect_pass(test_qt_add_ui_8) + _qt_internal_test_expect_pass(test_qt_add_ui_9) + _qt_internal_test_expect_pass(test_qt_add_ui_10) +endif() + diff --git a/tests/auto/cmake/mockplugins/.cmake.conf b/tests/auto/cmake/mockplugins/.cmake.conf index 10bc1fd407..6d83b084f7 100644 --- a/tests/auto/cmake/mockplugins/.cmake.conf +++ b/tests/auto/cmake/mockplugins/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.8.0") +set(QT_REPO_MODULE_VERSION "6.9.0") diff --git a/tests/auto/cmake/mockplugins/mock1plugin/qmock1plugin.cpp b/tests/auto/cmake/mockplugins/mock1plugin/qmock1plugin.cpp index 2ee817d80a..a2994c10e3 100644 --- a/tests/auto/cmake/mockplugins/mock1plugin/qmock1plugin.cpp +++ b/tests/auto/cmake/mockplugins/mock1plugin/qmock1plugin.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmock1plugin.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/cmake/mockplugins/mock2plugin/qmock2plugin.cpp b/tests/auto/cmake/mockplugins/mock2plugin/qmock2plugin.cpp index 5b3280e884..8e10797997 100644 --- a/tests/auto/cmake/mockplugins/mock2plugin/qmock2plugin.cpp +++ b/tests/auto/cmake/mockplugins/mock2plugin/qmock2plugin.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmock2plugin.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/cmake/mockplugins/mock3plugin/qmock3plugin.cpp b/tests/auto/cmake/mockplugins/mock3plugin/qmock3plugin.cpp index b38f854e4b..53090472e0 100644 --- a/tests/auto/cmake/mockplugins/mock3plugin/qmock3plugin.cpp +++ b/tests/auto/cmake/mockplugins/mock3plugin/qmock3plugin.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmock3plugin.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/cmake/mockplugins/mock4plugin/qmock4plugin.cpp b/tests/auto/cmake/mockplugins/mock4plugin/qmock4plugin.cpp index 5deaf7f43f..f2a904182d 100644 --- a/tests/auto/cmake/mockplugins/mock4plugin/qmock4plugin.cpp +++ b/tests/auto/cmake/mockplugins/mock4plugin/qmock4plugin.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmock4plugin.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/cmake/mockplugins/mock5plugin/qmock5plugin.cpp b/tests/auto/cmake/mockplugins/mock5plugin/qmock5plugin.cpp index c5b4620516..92ea47690f 100644 --- a/tests/auto/cmake/mockplugins/mock5plugin/qmock5plugin.cpp +++ b/tests/auto/cmake/mockplugins/mock5plugin/qmock5plugin.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmock5plugin.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/cmake/mockplugins/mock6plugin/qmock6plugin.cpp b/tests/auto/cmake/mockplugins/mock6plugin/qmock6plugin.cpp index 4a0329c68a..9dd9e02f2a 100644 --- a/tests/auto/cmake/mockplugins/mock6plugin/qmock6plugin.cpp +++ b/tests/auto/cmake/mockplugins/mock6plugin/qmock6plugin.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmock6plugin.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/cmake/test_android_aar/CMakeLists.txt b/tests/auto/cmake/test_android_aar/CMakeLists.txt new file mode 100644 index 0000000000..88cd5abf23 --- /dev/null +++ b/tests/auto/cmake/test_android_aar/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(test_android_aar) + +find_package(Qt6 COMPONENTS Core Gui REQUIRED) + +qt6_add_executable(test_aar main.cpp) + +# To support opening the project in QtCreator +if($CACHE{QT_USE_TARGET_ANDROID_BUILD_DIR}) + set(TARGET_ANDROID_BUILD_DIR ${CMAKE_BINARY_DIR}/android-build-test_aar) +else() + set(TARGET_ANDROID_BUILD_DIR ${CMAKE_BINARY_DIR}/android-build) +endif() + +# Add common libs for available ABIs that should be present in the aar package in a list +unset(aar_content_filepaths_to_verify) +foreach(abi IN LISTS QT_ANDROID_ABIS) + list(APPEND aar_content_filepaths_to_verify "jni/${abi}/libQt6Core_${abi}.so") + list(APPEND aar_content_filepaths_to_verify "jni/${abi}/libQt6Gui_${abi}.so") + list(APPEND aar_content_filepaths_to_verify + "jni/${abi}/libplugins_platforms_qtforandroid_${abi}.so") + list(APPEND aar_content_filepaths_to_verify "jni/${abi}/libtest_aar_${abi}.so") +endforeach() + +# Add a few ABI independent file that should be present in the aar package in a list +list(APPEND aar_content_filepaths_to_verify "libs/Qt6Android.jar") +list(APPEND aar_content_filepaths_to_verify "res/xml/qtprovider_paths.xml") +list(APPEND aar_content_filepaths_to_verify "AndroidManifest.xml") +list(JOIN aar_content_filepaths_to_verify "|" grep_pattern) + +# The overall number of lines we should expect to be filtered by grep regex +list(LENGTH aar_content_filepaths_to_verify expected_file_count) + +set(zipinfo_command "zipinfo -1 ${TARGET_ANDROID_BUILD_DIR}/test_aar.aar") + +# Runs zipinfo on the aar package, greps and outputs the number of matching lines +# and finally compares the expected number of lines with the output +add_custom_target(verify_aar + bash -c "${zipinfo_command} \ + | grep -Ecx '${grep_pattern}' \ + | grep -xq ${expected_file_count} \ + || { echo 'Error: The aar package is missing at least one file.'; exit 1; };" + COMMENT "Verifying aar package content" + VERBATIM +) + +# Build aar package before verification +add_dependencies(verify_aar aar) + +target_link_libraries(test_aar PRIVATE Qt::Core Qt::Gui) diff --git a/tests/auto/cmake/test_android_aar/main.cpp b/tests/auto/cmake/test_android_aar/main.cpp new file mode 100644 index 0000000000..90df9ee937 --- /dev/null +++ b/tests/auto/cmake/test_android_aar/main.cpp @@ -0,0 +1,4 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +int main(int, char *[]) { return 0; } diff --git a/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf b/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf index 10bc1fd407..6d83b084f7 100644 --- a/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf +++ b/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.8.0") +set(QT_REPO_MODULE_VERSION "6.9.0") diff --git a/tests/auto/cmake/test_interface/widget_test/main.cpp b/tests/auto/cmake/test_interface/widget_test/main.cpp index e6a8ab5fe9..0a47f43d84 100644 --- a/tests/auto/cmake/test_interface/widget_test/main.cpp +++ b/tests/auto/cmake/test_interface/widget_test/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QString> #include <QWidget> diff --git a/tests/auto/cmake/test_moc_macro_target/check_moc_parameters.cmake b/tests/auto/cmake/test_moc_macro_target/check_moc_parameters.cmake index 4ca8fab119..5388816c07 100644 --- a/tests/auto/cmake/test_moc_macro_target/check_moc_parameters.cmake +++ b/tests/auto/cmake/test_moc_macro_target/check_moc_parameters.cmake @@ -1,4 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause function(check_parameters file_path) file(READ ${file_path} file_content) foreach(compile_option IN ITEMS "-DDEFINE_CMDLINE_SIGNAL" "-DMY_OPTION") diff --git a/tests/auto/cmake/test_qt_add_resources_rebuild/sample/CMakeLists.txt b/tests/auto/cmake/test_qt_add_resources_rebuild/sample/CMakeLists.txt index 0a40a948c6..1696eae261 100644 --- a/tests/auto/cmake/test_qt_add_resources_rebuild/sample/CMakeLists.txt +++ b/tests/auto/cmake/test_qt_add_resources_rebuild/sample/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(sample LANGUAGES CXX) diff --git a/tests/auto/cmake/test_qt_add_ui_1/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_1/CMakeLists.txt new file mode 100644 index 0000000000..882abecc0b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_1/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "sub1/sub2/sub3/sub4/../../../../src/ui_files/ui_mainwindow.h". + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of the + # sub1/sub2/sub3/sub4 folder in ${hash_folder}/include + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder( "example" "${test_ui_file}" hash_folder) + string(CONCAT test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/UicBuildLeak_incPathGen" + "${config_path}-build") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_additional_args "-DMAINWINDOW_UI_PATH=sub1/sub2/" + "sub3/sub4/../../../../src/ui_files/") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_files/ui_mainwindow.h") + string(CONCAT test_folder_to_check "${test_build_dir}/.qt/" + "${hash_folder}/${config_arg}/sub1/sub2/sub3/sub4") + incremental_build_test( + TEST_NAME UicBuildLeak_incPathGen + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "${test_additional_args}" + FILE_TO_TOUCH "${test_ui_file}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK "${test_folder_to_check}" + ) + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_10/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_10/CMakeLists.txt new file mode 100644 index 0000000000..6b223fb281 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_10/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: mainwindow.ui file is touched after the first build. The + # ui_mainwindow.h should be generated without the ${config_arg} folder. + # Expect 1: Successful build without the double build issue. + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "qt_add_ui_simple_no_config${config_path}-build") + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder( + "example" + "${test_ui_file}" + hash_folder) + incremental_build_test( + TEST_NAME qt6_add_ui_simple_no_config + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CONFIG "${config_arg}" + ADDITIONAL_ARGS "-DUI_NO_CONFIG_OPTIONS=ON" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/ui_mainwindow.h" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_2/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_2/CMakeLists.txt new file mode 100644 index 0000000000..dceb0c7a2e --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_2/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: mainwindow.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + set(test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/qt_add_ui_simple${config_path}-build") + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder( + "example" + "${test_ui_file}" + hash_folder) + incremental_build_test( + TEST_NAME qt6_add_ui_simple + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CONFIG "${config_arg}" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/ui_mainwindow.h" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_3/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_3/CMakeLists.txt new file mode 100644 index 0000000000..81a9a38431 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_3/CMakeLists.txt @@ -0,0 +1,199 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: Check whether api sets warning against AUTOUIC activation. + # Expect 1: Printing of the error message and failure of the build. + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_warning_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CLEAN_FIRST + ADDITIONAL_ARGS "-DCMAKE_AUTOUIC=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error: " + "${cmake_error}\nFAIL: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-" + "build failed to configure") + else() + message(STATUS "PASS: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-" + "build was configured successfully") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_warning_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + RESULT_VARIABLE cmake_build_result) + if(NOT cmake_build_result EQUAL 0) + message(STATUS "PASS: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}" + "-build failed to build") + else() + message(FATAL_ERROR "FAIL: \"uic_warning_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-" + "build was built successfully") + endif() + expect_string_contains("${cmake_build_output}" "has \"AUTOUIC\" enabled" + SUCCESS_MESSAGE "\"uic_warning_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-build \ +has \"has AUTOUIC enabled\"" + FAILURE_MESSAGE "\"uic_warning_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_warning_test${config_path}-build \ +does not have \"has AUTOUIC enabled\"") + + + if("${generator}" MATCHES "Ninja") + # Test case: If INCLUDE_PREFIX is changed without changing the + # corresponding include path in the source file and Ninja generator + # is used, this casues the double build issue. + # Note: Only happens in Ninja generator. + # Expect 1: Failure of the build in the first build. + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CLEAN_FIRST + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3/" + "-DDO_NOT_GENERATE_FILE=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ": ${cmake_error}\nFAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to configure") + else() + message(STATUS "PASS: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was configured successfully") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + RESULT_VARIABLE cmake_build_result) + + if(NOT cmake_build_result EQUAL 0) + message(FATAL_ERROR "cmake_build_output: ${cmake_build_output}" + "FAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to build in the first build") + else() + message(STATUS "PASS: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was built successfully in the first " + "build") + endif() + + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + # We change the INCLUDE_PREFIX here. + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub/" + "-DDO_NOT_GENERATE_FILE=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ":${cmake_error}\nFAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to configure in the second " + "build") + else() + message(STATUS "PASS: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was configured successfully in the " + "second build") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_double_build_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + ERROR_VARIABLE cmake_build_error + RESULT_VARIABLE cmake_build_result) + + if(NOT cmake_build_result EQUAL 0) + message(STATUS "PASS: \"uic_double_build_test\" test in" + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build failed to build in the first build " + "after changing INCLUDE_PREFIX") + else() + message(FATAL_ERROR "cmake_build_output: ${cmake_build_output}" + "\ncmake_build_error: ${cmake_build_error}\n" + "FAIL: \"uic_double_build_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test" + "${config_path}-build was built successfully in the first " + "build after changing INCLUDE_PREFIX") + endif() + + set(expected_fail_string "No such file or directory|file not found") + expect_string_contains(${cmake_build_output} + "${expected_fail_string}" + SUCCESS_MESSAGE "\"uic_double_build_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test${config_path}\ +-build has \"ui_mainwindow.h: No such file or directory\" in \ +the first build after changing INCLUDE_PREFIX" + FAILURE_MESSAGE "\"uic_double_build_test\" test in \ +${CMAKE_CURRENT_BINARY_DIR}/uic_double_build_test${config_path}\ +-build does not have \"ui_mainwindow.h: No such file or \ +directory\" in the first build after changing INCLUDE_PREFIX") + + endif() + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_4/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_4/CMakeLists.txt new file mode 100644 index 0000000000..51c74352d6 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_4/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: widget1.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + # Expect 2: Only touched files to be built + set(test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/UicIncrementalBuild${config_path}-build") + string(CONCAT ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncrementalBuild") + + generate_hash_folder( + "example" + "${ui_file}" + hash_folder) + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_widget1.h") + incremental_build_test( + TEST_NAME UicIncrementalBuild + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + FILE_TO_TOUCH "${ui_file}" + FILE_TO_CHECK + "${test_file_to_check}" + FOLDER_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/src" + CHECK_UNWANTED_BUILDS + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_5/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_5/CMakeLists.txt new file mode 100644 index 0000000000..7b29bcf52b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_5/CMakeLists.txt @@ -0,0 +1,164 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + + if("${generator}" MATCHES "Ninja") + # Test case: Add a new ui file to CMakeLists.txt after the first + # build. + # Expect 1: Expect ${target}_autogen/prefix_info.cmake to be + # generated as expected. + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + CLEAN_FIRST + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3/" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ":${cmake_error}\nFAIL: \"uic_prefix_info_cmake_test\" test" + " in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build failed to configure") + else() + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in" + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build was configured successfully") + endif() + + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" + hash_folder_mainwindow) + string(CONCAT expected_prefix_info_cmake_content + "include_guard()\nset(${hash_folder_mainwindow}_prefix \"" + "sub1/sub2/sub3\")") + # read the content of the prefix_info.cmake file + string(CONCAT "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + file(READ + "${test_build_dir}/example_autogen/prefix_info.cmake" + prefix_info_cmake_content) + + if("${prefix_info_cmake_content}" STREQUAL + "${expected_prefix_info_cmake_content}") + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build has the expected content in " + "prefix_info.cmake") + else() + message(FATAL_ERROR "FAIL: \"uic_prefix_info_cmake_test\" test " + "in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build does not have the expected content in" + " prefix_info.cmake") + endif() + + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + run_cmake_build( + BUILD_DIR "${test_build_dir}" + VERBOSE ON + CONFIG "${config_arg}" + OUTPUT_VARIABLE cmake_build_output + ERROR_VARIABLE cmake_build_error + RESULT_VARIABLE cmake_build_result) + + if(NOT cmake_build_result EQUAL 0) + message(FATAL_ERROR "cmake_build_output: ${cmake_build_output}" + "\ncmake_build_error: ${cmake_build_error}\n" + "FAIL: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build failed to build in the first build") + else() + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build was built successfully in the first " + "build") + endif() + + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + run_cmake_configure(SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3/" + "-DNEW_UI_PATH=sub5/sub6/sub7/ " "-DADD_NEW_UI=ON" + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(NOT cmake_result EQUAL 0) + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error" + ": ${cmake_error}\nFAIL: \"uic_prefix_info_cmake_test\" " + "test in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_" + "test ${config_path}-build failed to configure in the" + "second build") + else() + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in" + " ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build was configured successfully in the " + "second build") + endif() + + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/subdir/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" + hash_folder_subdir_mainwindow) + set(expected_prefix_info_cmake_content + "include_guard()\nset(${hash_folder_subdir_mainwindow}_prefix \ +\"sub5/sub6/sub7\")") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "uic_prefix_info_cmake_test${config_path}-build") + file(READ + "${test_build_dir}/example_autogen/prefix_info.cmake" + prefix_info_cmake_content) + + if("${prefix_info_cmake_content}" STREQUAL + "${expected_prefix_info_cmake_content}") + message(STATUS "PASS: \"uic_prefix_info_cmake_test\" test in " + "${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build has the expected content in " + "prefix_info.cmake") + else() + message(FATAL_ERROR "FAIL: \"uic_prefix_info_cmake_test\" test " + "in ${CMAKE_CURRENT_BINARY_DIR}/uic_prefix_info_cmake_test" + "${config_path}-build does not have the expected content " + "in prefix_info.cmake") + endif() + endif() + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_6/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_6/CMakeLists.txt new file mode 100644 index 0000000000..2d57c6067d --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_6/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "../../../../src/ui_files/ui_mainwindow.h". + # Test case: mainwindow1.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of the "_/_/_/_/" + # folder in ${hash_folder}/include. + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../test_qt_add_ui_" + "common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" hash_folder) + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/UicBuildLeak_" + "subFolderGen${config_path}-build") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_files/ui_mainwindow.h") + incremental_build_test( + TEST_NAME UicBuildLeak_subFolderGen + SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../test_qt_add_ui_common/uic_test" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "-DMAINWINDOW_UI_PATH=../../../../src/ui_files/" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/_/_/_/_" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_7/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_7/CMakeLists.txt new file mode 100644 index 0000000000..8278f13a72 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_7/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case 1: There are two widget1.ui files in different folders. + # Expect 1: Successful build without the double build issue. + # Expect 2: Only touched files to be built. + string(CONCAT ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/" + "sub4") + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "UicIncBuild_sameFileDiffFolder${config_path}-build") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui") + generate_hash_folder( + "example" + "${ui_file}" + hash_folder) + incremental_build_test( + TEST_NAME UicIncBuild_sameFileDiffFolder + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + FILE_TO_TOUCH "${test_file_to_touch}" + CHECK_UNWANTED_BUILDS + ) + + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_8/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_8/CMakeLists.txt new file mode 100644 index 0000000000..f8d0763d35 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_8/CMakeLists.txt @@ -0,0 +1,67 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + # A CI test fails with the below condition. So, we are running the test + # only for the Debug configuration. + if ("${generator}" MATCHES "Xcode" AND + CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(configs "Debug") + elseif(multi_config_out) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "sub1/sub2/sub3/../../../../../../../../../../../../../ui_mainwindow.h". + # Test case: mainwindow1.ui file is touched after the first build. + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of the + # "_/_/_/_/_/_/_/_/_/_/sub1/sub2/sub3" folder in ${hash_folder}/include. + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" hash_folder) + string(CONCAT test_build_dir "${CMAKE_CURRENT_BINARY_DIR}/" + "UicBuildLeak_subFolderGen_complex${config_path}-build") + string(CONCAT test_additional_args "-DMAINWINDOW_UI_PATH=sub1/sub2/sub3" + "/../../../../../../../../../../../../../") + string(CONCAT test_file_to_touch "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/ui_mainwindow.h") + string(CONCAT test_folder_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/_/_/_/_/_/_/_/_/_/_/sub1/sub2/sub3") + incremental_build_test( + TEST_NAME UicBuildLeak_subFolderGen_complex + SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../test_qt_add_ui_common/uic_test" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "${test_additional_args}" + FILE_TO_TOUCH "${test_file_to_touch}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK "${test_folder_to_check}" + ) + endforeach() +endforeach() diff --git a/tests/auto/cmake/test_qt_add_ui_9/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_9/CMakeLists.txt new file mode 100644 index 0000000000..9e28ddc927 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_9/CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +include(../test_qt_add_ui_common/RunCMake.cmake) +include(../test_qt_add_ui_common/functions.cmake) + +get_generators(generators) + +foreach(generator IN ITEMS ${generators}) + message(STATUS "Running tests for generator: ${generator}") + is_multi_config(${generator} multi_config_out) + if(multi_config_out) + # Since our CI machines are slow, ctest --build-and-test buffers the + # output of the configure step of a test, and the fact that we run all + # the test logic in the configure step, we need to exclude Release + # to avoid CI stdout timeout errors. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/25790 + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if("${config}" STREQUAL "single_config") + set(config_path "") + set(config_arg "") + else() + set(config_path "_${config}") + set(config_arg "${config}") + endif() + + # Test case: ui_mainwindow.h is included as + # "sub2/sub3/../../../src/ui_files/ui_mainwindow.h". + # Expect 1: Successful build without the double build issue. + # Expect 2: No build folder leakage and generation of + # _/sub2/sub3 in ${hash_folder}/include. + # Note: This test case is a mix of previous two test cases. + string(CONCAT test_ui_file "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test/mainwindow.ui") + generate_hash_folder("example" "${test_ui_file}" hash_folder) + set(test_build_dir + "${CMAKE_CURRENT_BINARY_DIR}/UicBuildLeak_mix${config_path}-build") + string(CONCAT test_additional_args "-DMAINWINDOW_UI_PATH=sub2/sub3/" + "../../../src/ui_files/") + string(CONCAT test_file_to_check "${test_build_dir}/.qt/${hash_folder}/" + "${config_arg}/src/ui_files/ui_mainwindow.h") + string(CONCAT test_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/../" + "test_qt_add_ui_common/uic_test") + incremental_build_test( + TEST_NAME UicBuildLeak_mix + SOURCE_DIR "${test_source_dir}" + BUILD_DIR "${test_build_dir}" + CONFIG "${config_arg}" + GENERATOR "${generator}" + ADDITIONAL_ARGS "${test_additional_args}" + FILE_TO_TOUCH "${test_ui_file}" + FILE_TO_CHECK "${test_file_to_check}" + FOLDER_TO_CHECK + "${test_build_dir}/.qt/${hash_folder}/${config_arg}/_/sub2/sub3" + ) + endforeach() +endforeach() + diff --git a/tests/auto/cmake/test_qt_add_ui_common/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/CMakeLists.txt new file mode 100644 index 0000000000..628dc9373a --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(test) + +add_executable(test main.cpp) diff --git a/tests/auto/cmake/test_qt_add_ui_common/RunCMake.cmake b/tests/auto/cmake/test_qt_add_ui_common/RunCMake.cmake new file mode 100644 index 0000000000..6b39fe3398 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/RunCMake.cmake @@ -0,0 +1,157 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(run_cmake_configure) + set(options CLEAN_FIRST) + set(oneValueArgs SOURCE_DIR BUILD_DIR RESULT_VARIABLE OUTPUT_VARIABLE + ERROR_VARIABLE GENERATOR) + set(multiValueArgs ADDITIONAL_ARGS) + + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + if(NOT arg_SOURCE_DIR) + message(FATAL_ERROR "SOURCE_DIR not specified") + endif() + + if(NOT arg_BUILD_DIR) + message(FATAL_ERROR "BUILD_DIR not specified") + endif() + + is_multi_config(arg_GENERATOR multi_config_out) + if (NOT ${multi_config_out}) + set(run_arg_config_arg -Darg_TYPE=Debug) + endif() + + set(test_project_source_dir ${arg_SOURCE_DIR}) + set(test_project_build_dir ${arg_BUILD_DIR}) + + # Make sure that file paths are 'real' paths + get_filename_component(test_project_source_dir "${test_project_source_dir}" + REALPATH) + get_filename_component(test_project_build_dir "${test_project_build_dir}" + REALPATH) + + if(arg_CLEAN_FIRST) + file(REMOVE_RECURSE "${test_project_build_dir}") + endif() + file(MAKE_DIRECTORY "${test_project_build_dir}") + + execute_process(COMMAND + "${CMAKE_COMMAND}" + -S "${test_project_source_dir}" + -B "${test_project_build_dir}" + -G "${arg_GENERATOR}" + "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" + ${run_arg_config_arg} + ${arg_ADDITIONAL_ARGS} + RESULT_VARIABLE cmake_result + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ECHO_OUTPUT_VARIABLE + ECHO_ERROR_VARIABLE + ) + + # set output variables + set(${arg_RESULT_VARIABLE} ${cmake_result} PARENT_SCOPE) + set(${arg_OUTPUT_VARIABLE} ${cmake_output} PARENT_SCOPE) + set(${arg_ERROR_VARIABLE} ${cmake_error} PARENT_SCOPE) +endfunction() + +function(run_cmake_build) + set(options VERBOSE) + set(oneValueArgs CONFIG BUILD_DIR RESULT_VARIABLE OUTPUT_VARIABLE + ERROR_VARIABLE) + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + if(NOT arg_BUILD_DIR) + message(FATAL_ERROR "BUILD_DIR not specified") + endif() + + if (arg_VERBOSE OR arg_VERBOSE STREQUAL "") + set(arg_VERBOSE_ARG --verbose) + endif() + + if(arg_CONFIG) + set(arg_BUILD_CONFIG_ARG --config ${arg_CONFIG}) + endif() + + execute_process(COMMAND ${CMAKE_COMMAND} + --build ${arg_BUILD_DIR} + ${arg_VERBOSE_ARG} + ${arg_BUILD_CONFIG_ARG} + RESULT_VARIABLE cmake_result + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ECHO_OUTPUT_VARIABLE + ECHO_ERROR_VARIABLE) + + set(${arg_RESULT_VARIABLE} ${cmake_result} PARENT_SCOPE) + set(${arg_OUTPUT_VARIABLE} ${cmake_output} PARENT_SCOPE) + set(${arg_ERROR_VARIABLE} ${cmake_error} PARENT_SCOPE) +endfunction() + +function(is_multi_config generator output) + if ("${generator}" MATCHES "Visual Studio" OR "${generator}" MATCHES "Xcode" + OR "${generator}" MATCHES "Ninja Multi-Config") + set(${output} TRUE PARENT_SCOPE) + else() + set(${output} FALSE PARENT_SCOPE) + endif() +endfunction() + +# check if string includes substring +function(_internal_string_contains output string substring) + if("${string}" MATCHES "${substring}") + set(${output} TRUE PARENT_SCOPE) + else() + set(${output} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(expect_string_contains string substring) + set(oneValueArgs SUCCESS_MESSAGE FAILURE_MESSAGE) + cmake_parse_arguments(expect_string_contains "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN}) + _internal_string_contains(result "${string}" "${substring}") + if("${result}" STREQUAL TRUE) + if (expect_string_contains_SUCCESS_MESSAGE) + message(STATUS "PASS: ${expect_string_contains_SUCCESS_MESSAGE}") + else() + message(STATUS "PASS: \"${string}\" contains \"${substring}\"") + endif() + else() + if (expect_string_contains_FAILURE_MESSAGE) + message(FATAL_ERROR + "FAIL: ${expect_string_contains_FAILURE_MESSAGE}") + else() + message(FATAL_ERROR "FAIL: \"${string}\" contains \"${substring}\"") + endif() + endif() +endfunction() + +function(expect_string_not_contains string substring) + set(oneValueArgs SUCCESS_MESSAGE FAILURE_MESSAGE) + cmake_parse_arguments(expect_string_not_contains + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + _internal_string_contains(result ${string} ${substring}) + if(${result} STREQUAL FALSE) + if (expect_string_not_contains_SUCCESS_MESSAGE) + message(STATUS "PASS: ${expect_string_not_contains_SUCCESS_MESSAGE}") + else() + message(STATUS "PASS: \"${string}\" not contains \"${substring}\"") + endif() + else() + if (expect_string_not_contains_FAILURE_MESSAGE) + message(FATAL_ERROR + "FAIL: ${expect_string_not_contains_FAILURE_MESSAGE}") + else() + message(FATAL_ERROR "FAIL: \"${string}\" contains \"${substring}\"") + endif() + endif() +endfunction() diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/main.cpp new file mode 100644 index 0000000000..27296cb9a0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/mainwindow.h b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/mainwindow.h new file mode 100644 index 0000000000..9aeaefbe08 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicBuildFolderLeakageCommon/mainwindow.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/main.cpp new file mode 100644 index 0000000000..27296cb9a0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.cpp new file mode 100644 index 0000000000..d4302325fb --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" + +#include <QVBoxLayout> + +#include "../../../../src/ui_files/ui_mainwindow.h" +#include "widget1.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + auto layout = new QVBoxLayout; + layout->addWidget(new Widget1); + + QWidget* w = new QWidget(this); + w->setLayout(layout); + + setCentralWidget(w); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.h new file mode 100644 index 0000000000..46dc7690cc --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/mainwindow.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/mainwindow.ui new file mode 100644 index 0000000000..828d7c1782 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/mainwindow.ui @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout"/> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/widget1.ui new file mode 100644 index 0000000000..db0c58d5df --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/ui_files/widget1.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget1</class> + <widget class="QWidget" name="Widget1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.cpp new file mode 100644 index 0000000000..2139ff2c84 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "widget1.h" + +#include "../../../../src/ui_files/ui_widget1.h" + +Widget1::Widget1(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget1) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget1::~Widget1() +{ + delete ui; +} + +void Widget1::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.h new file mode 100644 index 0000000000..f8a5ad57d5 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef WIDGET1_H +#define WIDGET1_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget1; +} +QT_END_NAMESPACE + +class Widget1 : public QWidget +{ + Q_OBJECT +public: + explicit Widget1(QWidget* parent = nullptr); + ~Widget1(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget1* ui; +}; + +#endif // WIDGET1_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui new file mode 100644 index 0000000000..db0c58d5df --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget1.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget1</class> + <widget class="QWidget" name="Widget1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.cpp new file mode 100644 index 0000000000..721f0c868f --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "widget2.h" + +#include "../../../../ui_widget1.h" + +Widget2::Widget2(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget2) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget2::~Widget2() +{ + delete ui; +} + +void Widget2::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.h new file mode 100644 index 0000000000..e448b12caf --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/src/widget2.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef WIDGET2_H +#define WIDGET2_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget2; +} +QT_END_NAMESPACE + +class Widget2 : public QWidget +{ + Q_OBJECT + +public: + explicit Widget2(QWidget* parent = nullptr); + ~Widget2(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget2* ui; +}; + +#endif // WIDGET2_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/sub4/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/sub4/CMakeLists.txt new file mode 100644 index 0000000000..b8443c370d --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/sub1/sub2/sub3/sub4/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(UicIncrementalBuild_sameFileDifferentFolder LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui) + +set(CMAKE_AUTOMOC ON) + +qt_add_executable(example + ../../../../src/ui_files/mainwindow.ui + ../../../../src/ui_files/widget1.ui + ../../../../widget1.ui + ../../../../src/mainwindow.h + ../../../../src/widget1.h + ../../../../src/widget2.h + ../../../../src/main.cpp + ../../../../src/mainwindow.cpp + ../../../../src/widget1.cpp + ../../../../src/widget2.cpp +) + +target_link_libraries(example PRIVATE Qt6::Widgets + Qt6::Core + Qt6::Gui) + +qt6_add_ui(example + INCLUDE_PREFIX "../../../../src/ui_files" + SOURCES "../../../../src/ui_files/mainwindow.ui" + "../../../../src/ui_files/widget1.ui" + OPTIONS "$<$<CONFIG:Debug>:-a>") + +qt6_add_ui(example + INCLUDE_PREFIX "../../../../" + SOURCES "../../../../widget1.ui" + OPTIONS "$<$<CONFIG:Debug>:-a>") + diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/widget1.ui new file mode 100644 index 0000000000..facf4678f2 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncBuild_sameFileDiffFolder/widget1.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget2</class> + <widget class="QWidget" name="Widget2"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/CMakeLists.txt new file mode 100644 index 0000000000..81023c3382 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(UicIncrementalBuild LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +set(CMAKE_AUTOMOC ON) + +qt_add_executable(example + src/mainwindow.ui + src/widget1.ui + src/widget2.ui + src/mainwindow.h + src/widget1.h + src/widget2.h + src/main.cpp + src/mainwindow.cpp + src/widget1.cpp + src/widget2.cpp +) + +target_link_libraries(example PRIVATE Qt6::Widgets + Qt6::Core + Qt6::Gui) + +qt6_add_ui(example + INCLUDE_PREFIX "src" + SOURCES "src/mainwindow.ui" "src/widget1.ui" "src/widget2.ui" + OPTIONS "$<$<CONFIG:Debug>:-a>") + diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/main.cpp new file mode 100644 index 0000000000..27296cb9a0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.cpp new file mode 100644 index 0000000000..ef582f187c --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" + +#include <QVBoxLayout> + +#include "src/ui_mainwindow.h" +#include "widget1.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + auto layout = new QVBoxLayout; + layout->addWidget(new Widget1); + + QWidget* w = new QWidget(this); + w->setLayout(layout); + + setCentralWidget(w); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.h new file mode 100644 index 0000000000..46dc7690cc --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.ui new file mode 100644 index 0000000000..828d7c1782 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/mainwindow.ui @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QGridLayout" name="gridLayout"/> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.cpp new file mode 100644 index 0000000000..4047ea4d9c --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "widget1.h" + +#include "src/ui_widget1.h" + +Widget1::Widget1(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget1) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget1::~Widget1() +{ + delete ui; +} + +void Widget1::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.h new file mode 100644 index 0000000000..f8a5ad57d5 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef WIDGET1_H +#define WIDGET1_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget1; +} +QT_END_NAMESPACE + +class Widget1 : public QWidget +{ + Q_OBJECT +public: + explicit Widget1(QWidget* parent = nullptr); + ~Widget1(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget1* ui; +}; + +#endif // WIDGET1_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui new file mode 100644 index 0000000000..db0c58d5df --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget1.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget1</class> + <widget class="QWidget" name="Widget1"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.cpp b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.cpp new file mode 100644 index 0000000000..1dc28b0c8b --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "widget2.h" + +#include "src/ui_widget2.h" + +Widget2::Widget2(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget2) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget2::~Widget2() +{ + delete ui; +} + +void Widget2::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.h b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.h new file mode 100644 index 0000000000..e448b12caf --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef WIDGET2_H +#define WIDGET2_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget2; +} +QT_END_NAMESPACE + +class Widget2 : public QWidget +{ + Q_OBJECT + +public: + explicit Widget2(QWidget* parent = nullptr); + ~Widget2(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget2* ui; +}; + +#endif // WIDGET2_H diff --git a/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.ui b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.ui new file mode 100644 index 0000000000..facf4678f2 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/UicIncrementalBuild/src/widget2.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget2</class> + <widget class="QWidget" name="Widget2"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Input:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>OnTextChanged:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="OnTextChanged"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/functions.cmake b/tests/auto/cmake/test_qt_add_ui_common/functions.cmake new file mode 100644 index 0000000000..efb8b09774 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/functions.cmake @@ -0,0 +1,223 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(generate_hash_folder target_name infile out_folder) + get_filename_component(infile_abs "${infile}" ABSOLUTE) + string(SHA1 infile_hash "${target_name}${infile_abs}") + string(SUBSTRING "${infile_hash}" 0 6 short_hash) + set(${out_folder} "${short_hash}" PARENT_SCOPE) +endfunction() + +function(get_latest_vs_generator output) + execute_process(COMMAND ${CMAKE_COMMAND} -G + ERROR_VARIABLE CMAKE_GENERATORS_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX MATCHALL "Visual Studio [0-9]+ [0-9]+" vs_generators + "${CMAKE_GENERATORS_ERROR}") + + if(NOT vs_generators) + message(FATAL_ERROR "No visual studio generators found") + endif() + + set(last_version "0") + set(last_generator "") + foreach(generator IN LISTS vs_generators) + string(REGEX MATCH "Visual Studio ([0-9]+) [0-9]+" unused "${generator}") + if("${CMAKE_MATCH_1}" VERSION_GREATER "${last_version}") + set(last_version "${CMAKE_MATCH_1}") + set(last_generator "${CMAKE_MATCH_0}") + endif() + endforeach() + set(${output} "${last_generator}" PARENT_SCOPE) +endfunction() + +function(check_unwanted_builds_after_first_build cmake_output test_name test_dir + generator) + set(unwanted_builds_success_message + "\"${test_name}\" in \"${test_dir}\" -> No unwanted builds") + set(unwanted_builds_failure_message + "\"${test_name}\" in \"${test_dir}\" -> Unwanted builds found") + if(${generator} MATCHES "Ninja") + expect_string_not_contains(${cmake_output} + "widget2.cpp.o.d|mainwindow.cpp.o.d" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + elseif(${generator} MATCHES "Make") + string(CONCAT not_expect_string + "Building CXX object UicIncrementalBuild/CMakeFiles" + "/example.dir/src/widget2.cpp.o|Building CXX object UicIncremental" + "Build/CMakeFiles/example.dir/src/mainwindow.cpp.o") + expect_string_not_contains(${cmake_output} "${not_expect_string}" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + elseif(${generator} MATCHES "Visual Studio") + expect_string_not_contains(${cmake_output} "widget2.cpp|mainwindow.cpp" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + elseif(${generator} MATCHES "Xcode") + expect_string_not_contains(${cmake_output} "widget2.cpp|mainwindow.cpp" + SUCCESS_MESSAGE ${unwanted_builds_success_message} + FAILURE_MESSAGE ${unwanted_builds_failure_message}) + endif() +endfunction() + +function(check_output_after_second_build cmake_output test_name + test_dir generator) + set(second_build_success_message + "\"${test_name}\" in \"${test_dir}\" -> Generation of UI files were not \ +triggered in the second build") + set(second_build_failure_message + "\"${test_name}\" in \"${test_dir}\" -> Generation of UI files were \ +triggered in the second build") + + if(${generator} MATCHES "Ninja") + expect_string_contains(${cmake_output} "ninja: no work to do." + SUCCESS_MESSAGE ${second_build_success_message} + FAILURE_MESSAGE ${second_build_failure_message}) + elseif(${generator} MATCHES "Visual Studio" OR ${generator} MATCHES "Xcode") + expect_string_not_contains(${cmake_output} "mainwindow" + SUCCESS_MESSAGE + ${second_build_success_message} + FAILURE_MESSAGE + ${second_build_failure_message}) + elseif(${generator} MATCHES "Makefiles") + expect_string_not_contains(${cmake_output} "mainwindow.cpp.o.d -o" + SUCCESS_MESSAGE ${second_build_success_message} + FAILURE_MESSAGE ${second_build_failure_message}) + endif() +endfunction() + +function(incremental_build_test) + set(options CHECK_UNWANTED_BUILDS) + set(oneValueArgs CONFIG TEST_NAME SOURCE_DIR BUILD_DIR FILE_TO_TOUCH + FILE_TO_CHECK FOLDER_TO_CHECK GENERATOR) + set(multiValueArgs ADDITIONAL_ARGS) + + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + string(REPLACE ";" " " arg_ADDITIONAL_ARGS "${arg_ADDITIONAL_ARGS}") + if ("${arg_SOURCE_DIR}" STREQUAL "") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" test failed because " + "SOURCE_DIR is empty") + endif() + + if ("${arg_BUILD_DIR}" STREQUAL "") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" test failed because " + "BUILD_DIR is empty") + endif() + + if ("${arg_GENERATOR}" STREQUAL "") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" test failed because " + "GENERATOR is empty") + endif() + + run_cmake_configure(SOURCE_DIR "${arg_SOURCE_DIR}" + BUILD_DIR "${arg_BUILD_DIR}" + GENERATOR "${arg_GENERATOR}" + CLEAN_FIRST + ADDITIONAL_ARGS ${arg_ADDITIONAL_ARGS} + OUTPUT_VARIABLE cmake_output + ERROR_VARIABLE cmake_error + RESULT_VARIABLE cmake_result) + + if(${cmake_result} EQUAL 0) + message(STATUS + "PASS: \"${arg_TEST_NAME}\" test in ${arg_BUILD_DIR} was configured " + "successfully") + else() + message(FATAL_ERROR "cmake_output: ${cmake_output}\ncmake_error: " + "${cmake_error}\nFAIL: \"${arg_TEST_NAME}\" test in ${arg_BUILD_DIR}" + " failed to configure") + endif() + + if(NOT "${arg_CONFIG}" STREQUAL "single_config") + set(config_arg "${arg_CONFIG}") + endif() + + run_cmake_build(BUILD_DIR ${arg_BUILD_DIR} + VERBOSE ON + CONFIG ${config_arg} + OUTPUT_VARIABLE cmake_build_output + ERROR_VARIABLE cmake_build_error + RESULT_VARIABLE cmake_build_result) + + if(${cmake_build_result} EQUAL 0) + message(STATUS + "PASS: \"${arg_TEST_NAME}\" test in ${arg_BUILD_DIR} was built " + "successfully") + else() + message(FATAL_ERROR + "cmake_build_output: ${cmake_build_output}\ncmake_build_error: " + "${cmake_build_error}\nFAIL: \"${arg_TEST_NAME}\" test in " + "${arg_BUILD_DIR} failed to build") + endif() + + if(NOT "${arg_FILE_TO_CHECK}" STREQUAL "") + if(NOT EXISTS "${arg_FILE_TO_CHECK}") + message(FATAL_ERROR "FAIL: \"${arg_TEST_NAME}\" ${arg_FILE_TO_CHECK}" + " could not be found") + else() + message(STATUS "PASS: \"${arg_TEST_NAME}\" \"${arg_FILE_TO_CHECK}\" " + "was generated successfully") + endif() + endif() + + if(NOT "${arg_FOLDER_TO_CHECK}" STREQUAL "" AND NOT WIN32) + if(NOT EXISTS "${arg_FOLDER_TO_CHECK}") + message(FATAL_ERROR + "FAIL: \"${arg_TEST_NAME}\" Folder \"${arg_FOLDER_TO_CHECK}\" " + "does not exist") + else() + message(STATUS + "PASS: \"${arg_TEST_NAME}\" Folder \"${arg_FOLDER_TO_CHECK}\" " + "exists") + endif() + endif() + + if(NOT "${arg_FILE_TO_TOUCH}" STREQUAL "") + file(TOUCH "${arg_FILE_TO_TOUCH}") + endif() + + run_cmake_build(BUILD_DIR ${arg_BUILD_DIR} + VERBOSE ON + CONFIG ${arg_CONFIG} + OUTPUT_VARIABLE cmake_build_output) + if(${arg_CHECK_UNWANTED_BUILDS}) + check_unwanted_builds_after_first_build(${cmake_build_output} + ${arg_TEST_NAME} ${arg_BUILD_DIR} ${arg_GENERATOR}) + endif() + + run_cmake_build(BUILD_DIR ${arg_BUILD_DIR} + VERBOSE ON + CONFIG ${arg_CONFIG} + OUTPUT_VARIABLE cmake_build_output) + check_output_after_second_build(${cmake_build_output} + ${arg_TEST_NAME} ${arg_BUILD_DIR} ${arg_GENERATOR}) +endfunction() + +function(get_generators output) + if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux") + set(generators "Unix Makefiles" "Ninja" "Ninja Multi-Config") + elseif(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows") + # CI fails with Clang and Visual Studio generators. + # That's why discard that combination. + if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND + NOT MINGW) + get_latest_vs_generator(latest_vs) + endif() + set(generators "Ninja" "Ninja Multi-Config" "${latest_vs}") + elseif(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") + # TODO: Add Xcode generator when + # https://gitlab.kitware.com/cmake/cmake/-/issues/25790 is fixed. + # Otherwise, adding Xcode generator might fail CI due to the timeout + # issue. + set(generators "Unix Makefiles" "Ninja" "Ninja Multi-Config") + else() + string(JOIN "" ERROR_MESSAGE + "FAIL: host OS not supported for this test." + "host : ${CMAKE_HOST_SYSTEM_NAME}") + message(FATAL_ERROR "${ERROR_MESSAGE}") + endif() + set(${output} "${generators}" PARENT_SCOPE) +endfunction() diff --git a/tests/auto/cmake/test_qt_add_ui_common/main.cpp b/tests/auto/cmake/test_qt_add_ui_common/main.cpp new file mode 100644 index 0000000000..c93c3fc6a0 --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/main.cpp @@ -0,0 +1,4 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +int main() { return 0; } diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/CMakeLists.txt b/tests/auto/cmake/test_qt_add_ui_common/uic_test/CMakeLists.txt new file mode 100644 index 0000000000..b05efd5e4d --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(UicTest LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui) + +set(CMAKE_AUTOMOC ON) + +if (NOT DO_NOT_GENERATE_FILE) + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mainwindow.cpp" + CONTENT " \ +#include \"${CMAKE_CURRENT_SOURCE_DIR}/../UicBuildFolderLeakageCommon/mainwindow.h\" \n \ +#include \"${MAINWINDOW_UI_PATH}ui_mainwindow.h\" \n \ +MainWindow::MainWindow(QWidget* parent) \n \ + : QMainWindow(parent) \n \ + , ui(new Ui::MainWindow) \n \ +{ \n \ + ui->setupUi(this); \n \ +} \n \ + \n \ +MainWindow::~MainWindow() \n \ +{ \n \ + delete ui; \n \ +} \n \ +") +endif() + +qt_add_executable(example + ../UicBuildFolderLeakageCommon/main.cpp + ../UicBuildFolderLeakageCommon/mainwindow.h + mainwindow.ui +) + +if (${DO_NOT_GENERATE_FILE}) + target_sources(example PRIVATE mainwindow.cpp) +else() + target_sources(example PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/mainwindow.cpp") +endif() + +target_link_libraries(example PRIVATE Qt6::Widgets + Qt6::Core + Qt6::Gui) + +if (NOT UI_NO_CONFIG_OPTIONS) + set(uic_options "$<$<CONFIG:Debug>:-a>") +endif() +qt6_add_ui(example + INCLUDE_PREFIX "${MAINWINDOW_UI_PATH}" + SOURCES "mainwindow.ui" + OPTIONS "${uic_options}") + +if(ADD_NEW_UI) + qt6_add_ui(example INCLUDE_PREFIX "${NEW_UI_PATH}" + SOURCES "subdir/mainwindow.ui" + OPTIONS "${uic_options}") +endif() + +# Enable AUTOUIC after qt6_add_ui() has been called +if (CMAKE_AUTOUIC) + set_property(TARGET example PROPERTY AUTOUIC ON) +endif() + diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.cpp b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.cpp new file mode 100644 index 0000000000..1c4b48b49e --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "../UicBuildFolderLeakageCommon/mainwindow.h" +#include "sub1/sub2/sub3/ui_mainwindow.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.ui new file mode 100644 index 0000000000..4e57b05eac --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/mainwindow.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>23</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_qt_add_ui_common/uic_test/subdir/mainwindow.ui b/tests/auto/cmake/test_qt_add_ui_common/uic_test/subdir/mainwindow.ui new file mode 100644 index 0000000000..4e57b05eac --- /dev/null +++ b/tests/auto/cmake/test_qt_add_ui_common/uic_test/subdir/mainwindow.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>23</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/cmake/test_static_resources/.cmake.conf b/tests/auto/cmake/test_static_resources/.cmake.conf index 10bc1fd407..6d83b084f7 100644 --- a/tests/auto/cmake/test_static_resources/.cmake.conf +++ b/tests/auto/cmake/test_static_resources/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.8.0") +set(QT_REPO_MODULE_VERSION "6.9.0") diff --git a/tests/auto/corelib/global/CMakeLists.txt b/tests/auto/corelib/global/CMakeLists.txt index 7970116672..8adf9d7430 100644 --- a/tests/auto/corelib/global/CMakeLists.txt +++ b/tests/auto/corelib/global/CMakeLists.txt @@ -16,6 +16,9 @@ if(NOT INTEGRITY) add_subdirectory(qnativeinterface) endif() add_subdirectory(qrandomgenerator) +if (QT_FEATURE_settings) + add_subdirectory(qlibraryinfo) +endif() add_subdirectory(qlogging) add_subdirectory(qtendian) add_subdirectory(qglobalstatic) diff --git a/tests/auto/corelib/global/q20/CMakeLists.txt b/tests/auto/corelib/global/q20/CMakeLists.txt index bd28f8b999..141f94d9f7 100644 --- a/tests/auto/corelib/global/q20/CMakeLists.txt +++ b/tests/auto/corelib/global/q20/CMakeLists.txt @@ -1 +1,3 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(memory) diff --git a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp index e36429e62b..556c8212b3 100644 --- a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp +++ b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp @@ -785,9 +785,10 @@ void tst_QCompare::compareThreeWay() static_assert(noexcept(qCompareThreeWay(std::declval<TestEnum>(), std::declval<TestEnum>()))); // pointers - static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapper *>(), - std::declval<StringWrapper *>()))); - static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapper *>(), nullptr))); + using StringWrapperPtr = Qt::totally_ordered_wrapper<StringWrapper *>; + static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapperPtr>(), + std::declval<StringWrapperPtr>()))); + static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapperPtr>(), nullptr))); // Test some actual comparison results @@ -832,8 +833,17 @@ void tst_QCompare::compareThreeWay() // pointers std::array<int, 2> arr{1, 0}; +#if QT_DEPRECATED_SINCE(6, 8) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED QCOMPARE_EQ(qCompareThreeWay(&arr[1], &arr[0]), Qt::strong_ordering::greater); QCOMPARE_EQ(qCompareThreeWay(arr.data(), &arr[0]), Qt::strong_ordering::equivalent); +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 8) + const auto a0 = Qt::totally_ordered_wrapper(&arr[0]); + const auto a1 = Qt::totally_ordered_wrapper(&arr[1]); + QCOMPARE_EQ(qCompareThreeWay(a1, a0), Qt::strong_ordering::greater); + QCOMPARE_EQ(qCompareThreeWay(arr.data(), a0), Qt::strong_ordering::equivalent); } QTEST_MAIN(tst_QCompare) diff --git a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp index f140c23ed0..466723a271 100644 --- a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp +++ b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp @@ -581,20 +581,46 @@ void tst_QCompareHelpers::builtinOrder() Qt::strong_ordering::equivalent); std::array<int, 2> arr{1, 0}; +#if QT_DEPRECATED_SINCE(6, 8) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED QCOMPARE_EQ(Qt::compareThreeWay(&arr[0], &arr[1]), Qt::strong_ordering::less); QCOMPARE_EQ(Qt::compareThreeWay(arr.data(), &arr[0]), Qt::strong_ordering::equivalent); +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 8) class Base {}; class Derived : public Base {}; auto b = std::make_unique<Base>(); auto d = std::make_unique<Derived>(); +#if QT_DEPRECATED_SINCE(6, 8) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED QCOMPARE_NE(Qt::compareThreeWay(b.get(), d.get()), Qt::strong_ordering::equivalent); QCOMPARE_EQ(Qt::compareThreeWay(b.get(), nullptr), Qt::strong_ordering::greater); QCOMPARE_EQ(Qt::compareThreeWay(nullptr, d.get()), Qt::strong_ordering::less); +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 8) + + // Check Qt::totally_ordered_wrapper + auto a0 = Qt::totally_ordered_wrapper(&arr[0]); + auto a1 = Qt::totally_ordered_wrapper(&arr[1]); + QCOMPARE_EQ(Qt::compareThreeWay(a0, a1), Qt::strong_ordering::less); + QCOMPARE_EQ(Qt::compareThreeWay(arr.data(), a0), Qt::strong_ordering::equivalent); + + auto bWrapper = Qt::totally_ordered_wrapper(b.get()); + auto dWrapper = Qt::totally_ordered_wrapper(d.get()); + QCOMPARE_NE(Qt::compareThreeWay(bWrapper, dWrapper), Qt::strong_ordering::equivalent); + QCOMPARE_NE(Qt::compareThreeWay(bWrapper, d.get()), Qt::strong_ordering::equivalent); + QCOMPARE_NE(Qt::compareThreeWay(b.get(), dWrapper), Qt::strong_ordering::equivalent); + QCOMPARE_EQ(Qt::compareThreeWay(bWrapper, nullptr), Qt::strong_ordering::greater); + QCOMPARE_EQ(Qt::compareThreeWay(nullptr, dWrapper), Qt::strong_ordering::less); + dWrapper.reset(nullptr); + QCOMPARE_EQ(Qt::compareThreeWay(nullptr, dWrapper), Qt::strong_ordering::equivalent); #undef TEST_BUILTIN } QTEST_MAIN(tst_QCompareHelpers) -#include "tst_qcomparehelpers.moc" +#include "moc_tst_qcomparehelpers.cpp" diff --git a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp index a3b8200a63..16b2842de2 100644 --- a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp +++ b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp @@ -4,23 +4,26 @@ #include "tst_qcomparehelpers.h" #define DECLARE_TYPE(Name, Type, RetType, Constexpr, Suffix) \ -class Deprecated ## Name \ +class Templated ## Name \ { \ public: \ - Constexpr Deprecated ## Name () {} \ + Constexpr Templated ## Name () {} \ \ private: \ + template <typename X> \ friend Constexpr bool \ - comparesEqual(const Deprecated ## Name &lhs, int rhs) noexcept; \ + comparesEqual(const Templated ## Name &lhs, X rhs) noexcept; \ + template <typename X> \ friend Constexpr RetType \ - compareThreeWay(const Deprecated ## Name &lhs, int rhs) noexcept; \ - Q_DECLARE_ ## Type ## _ORDERED ## Suffix (Deprecated ## Name, int, \ - Q_DECL_DEPRECATED_X("This op is deprecated")) \ + compareThreeWay(const Templated ## Name &lhs, X rhs) noexcept; \ + Q_DECLARE_ ## Type ## _ORDERED ## Suffix (Templated ## Name, X, template <typename X>) \ }; \ \ -Constexpr bool comparesEqual(const Deprecated ## Name &lhs, int rhs) noexcept \ +template <typename X> \ +Constexpr bool comparesEqual(const Templated ## Name &lhs, X rhs) noexcept \ { Q_UNUSED(lhs); Q_UNUSED(rhs); return true; } \ -Constexpr RetType compareThreeWay(const Deprecated ## Name &lhs, int rhs) noexcept \ +template <typename X> \ +Constexpr RetType compareThreeWay(const Templated ## Name &lhs, X rhs) noexcept \ { Q_UNUSED(lhs); Q_UNUSED(rhs); return RetType::equivalent; } DECLARE_TYPE(PartialConst, PARTIALLY, Qt::partial_ordering, constexpr, _LITERAL_TYPE) @@ -34,10 +37,6 @@ DECLARE_TYPE(Strong, STRONGLY, Qt::strong_ordering, , ) void tst_QCompareHelpers::compareWithAttributes() { - // All these comparisons would trigger deprecation warnings. -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - #define COMPARE(ClassName) \ do { \ ClassName c; \ @@ -46,14 +45,12 @@ QT_WARNING_DISABLE_DEPRECATED QCOMPARE_GE(0, c); \ } while (false) - COMPARE(DeprecatedPartialConst); - COMPARE(DeprecatedPartial); - COMPARE(DeprecatedWeakConst); - COMPARE(DeprecatedWeak); - COMPARE(DeprecatedStrongConst); - COMPARE(DeprecatedStrong); + COMPARE(TemplatedPartialConst); + COMPARE(TemplatedPartial); + COMPARE(TemplatedWeakConst); + COMPARE(TemplatedWeak); + COMPARE(TemplatedStrongConst); + COMPARE(TemplatedStrong); #undef COMPARE - -QT_WARNING_POP } diff --git a/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp b/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp index 7acf8c2cf6..5f1ff67316 100644 --- a/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp +++ b/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp @@ -155,6 +155,7 @@ void tst_qfloat16::ordering_data() row(2.0f, -inf); row(-2.0f, nan); row(-inf, -2.0f); + // testing with values outside qfloat16 range row(0.0f, 13e5f); // generateRow(inf, 13e5f); // fails qfloat16 vs qfloat16 and qfloat16 vs int (QTBUG-118193) @@ -187,11 +188,21 @@ void tst_qfloat16::ordering() #undef CHECK_FP + auto check_int = [=](auto rhs) { + // check that we're in range before converting, otherwise + // [conv.fpint]/1 says it would be UB + using RHS = decltype(rhs); + if (right > double(std::numeric_limits<RHS>::max())) + return; + if (auto min = std::numeric_limits<RHS>::min(); min != 0 && right < double(min)) + return; + rhs = RHS(right); + const auto expectedRes = Qt::compareThreeWay(left, rhs); + QTestPrivate::testAllComparisonOperators(lhs, rhs, expectedRes); + }; #define CHECK_INT(RHS) \ do { \ - const auto rhs = static_cast<RHS>(right); \ - const auto expectedRes = Qt::compareThreeWay(left, rhs); \ - QTestPrivate::testAllComparisonOperators(lhs, rhs, expectedRes); \ + check_int(static_cast<RHS>(0)); \ POSTCHECK("qfloat16 vs " #RHS " comparison failed") \ } while (false) \ /* END */ diff --git a/tests/auto/corelib/global/qlibraryinfo/CMakeLists.txt b/tests/auto/corelib/global/qlibraryinfo/CMakeLists.txt new file mode 100644 index 0000000000..d72e871967 --- /dev/null +++ b/tests/auto/corelib/global/qlibraryinfo/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlibraryinfo LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + + +qt_internal_add_test(tst_qlibraryinfo SOURCES tst_qlibraryinfo.cpp + LIBRARIES + Qt::CorePrivate + +) + +qt_add_resources(tst_qlibraryinfo "qtconffiles" + PREFIX "/" + FILES + empty.qt.conf + partial.qt.conf + list.qt.conf + merge.qt.conf +) diff --git a/tests/auto/corelib/global/qlibraryinfo/empty.qt.conf b/tests/auto/corelib/global/qlibraryinfo/empty.qt.conf new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/corelib/global/qlibraryinfo/empty.qt.conf diff --git a/tests/auto/corelib/global/qlibraryinfo/list.qt.conf b/tests/auto/corelib/global/qlibraryinfo/list.qt.conf new file mode 100644 index 0000000000..9271b414ca --- /dev/null +++ b/tests/auto/corelib/global/qlibraryinfo/list.qt.conf @@ -0,0 +1,2 @@ +[Paths] +Documentation = "/path/to/mydoc","/path/to/anotherdoc","relativePath" diff --git a/tests/auto/corelib/global/qlibraryinfo/merge.qt.conf b/tests/auto/corelib/global/qlibraryinfo/merge.qt.conf new file mode 100644 index 0000000000..a687dc4d71 --- /dev/null +++ b/tests/auto/corelib/global/qlibraryinfo/merge.qt.conf @@ -0,0 +1,5 @@ +[Paths] +QmlImports = "/path/to/myqml" + +[Config] +MergeQtConf=true diff --git a/tests/auto/corelib/global/qlibraryinfo/partial.qt.conf b/tests/auto/corelib/global/qlibraryinfo/partial.qt.conf new file mode 100644 index 0000000000..bba214c2a6 --- /dev/null +++ b/tests/auto/corelib/global/qlibraryinfo/partial.qt.conf @@ -0,0 +1,2 @@ +[Paths] +QmlImports = "/path/to/myqml" diff --git a/tests/auto/corelib/global/qlibraryinfo/tst_qlibraryinfo.cpp b/tests/auto/corelib/global/qlibraryinfo/tst_qlibraryinfo.cpp new file mode 100644 index 0000000000..b7d79c05f5 --- /dev/null +++ b/tests/auto/corelib/global/qlibraryinfo/tst_qlibraryinfo.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qscopeguard.h> +#include <QtCore/private/qlibraryinfo_p.h> + + +class tst_QLibraryInfo : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanup(); + void path_data(); + void path(); + void paths(); + void merge(); +}; + +void tst_QLibraryInfo::initTestCase() +{ +#if !QT_CONFIG(settings) + QSKIP("QSettings support is required for the test to run."); +#endif +} + +void tst_QLibraryInfo::cleanup() +{ + QLibraryInfoPrivate::setQtconfManualPath(nullptr); + QLibraryInfoPrivate::reload(); +} + +void tst_QLibraryInfo::path_data() +{ + QTest::addColumn<QString>("qtConfPath"); + QTest::addColumn<QLibraryInfo::LibraryPath>("path"); + QTest::addColumn<QString>("expected"); + + // TODO: deal with bundle on macOs? + QString baseDir = QCoreApplication::applicationDirPath(); + + // empty means we fall-back to default entries + QTest::addRow("empty_qmlimports") << ":/empty.qt.conf" << QLibraryInfo::QmlImportsPath << (baseDir + "/qml"); + QTest::addRow("empty_Data") << ":/empty.qt.conf" << QLibraryInfo::DataPath << baseDir; + + // partial override; use given entry if provided, otherwise default + QTest::addRow("partial_qmlimports") << ":/partial.qt.conf" << QLibraryInfo::QmlImportsPath << "/path/to/myqml"; + QTest::addRow("partial_Data") << ":/partial.qt.conf" << QLibraryInfo::DataPath << baseDir; +} + +void tst_QLibraryInfo::path() +{ + QFETCH(QString, qtConfPath); + QFETCH(QLibraryInfo::LibraryPath, path); + QFETCH(QString, expected); + + QLibraryInfoPrivate::setQtconfManualPath(&qtConfPath); + QLibraryInfoPrivate::reload(); + QString value = QLibraryInfo::path(path); + QCOMPARE(value, expected); + + // check consistency with paths + auto values = QLibraryInfo::paths(path); + QVERIFY(!values.isEmpty()); + QCOMPARE(values.first(), expected); +} + +void tst_QLibraryInfo::paths() +{ + QString qtConfPath(u":/list.qt.conf"); + QLibraryInfoPrivate::setQtconfManualPath(&qtConfPath); + QLibraryInfoPrivate::reload(); + + QList<QString> values = QLibraryInfo::paths(QLibraryInfo::DocumentationPath); + QCOMPARE(values.length(), 3); + QCOMPARE(values[0], "/path/to/mydoc"); + QCOMPARE(values[1], "/path/to/anotherdoc"); + QString baseDir = QCoreApplication::applicationDirPath(); + QCOMPARE(values[2], baseDir + "/relativePath"); +} + +void tst_QLibraryInfo::merge() +{ + QString qtConfPath(u":/merge.qt.conf"); + QLibraryInfoPrivate::setQtconfManualPath(&qtConfPath); + QLibraryInfoPrivate::reload(); + + QString baseDir = QCoreApplication::applicationDirPath(); + QString docPath = QLibraryInfo::path(QLibraryInfo::DocumentationPath); + // we can't know where exactly the doc path points, but it should not point to ${baseDir}/doc, + // which would be the behavior without merge_qt_conf + QCOMPARE_NE(docPath, baseDir + "/doc"); + + QList<QString> values = QLibraryInfo::paths(QLibraryInfo::QmlImportsPath); + QCOMPARE(values.size(), 2); // custom entry + Qt default entry + QCOMPARE(values[0], "/path/to/myqml"); +} + +QTEST_GUILESS_MAIN(tst_QLibraryInfo) + +#include "tst_qlibraryinfo.moc" diff --git a/tests/auto/corelib/global/qxp/CMakeLists.txt b/tests/auto/corelib/global/qxp/CMakeLists.txt index 2178f446db..8292462d17 100644 --- a/tests/auto/corelib/global/qxp/CMakeLists.txt +++ b/tests/auto/corelib/global/qxp/CMakeLists.txt @@ -1,2 +1,4 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(function_ref) add_subdirectory(is_virtual_base_of) diff --git a/tests/auto/corelib/io/CMakeLists.txt b/tests/auto/corelib/io/CMakeLists.txt index 7fdf4b52b0..1e66aa8956 100644 --- a/tests/auto/corelib/io/CMakeLists.txt +++ b/tests/auto/corelib/io/CMakeLists.txt @@ -25,7 +25,7 @@ add_subdirectory(qloggingcategory) add_subdirectory(qnodebug) add_subdirectory(qsavefile) add_subdirectory(qstandardpaths) -if(NOT QNX) +if(NOT QNX AND NOT VXWORKS) add_subdirectory(qstorageinfo) endif() add_subdirectory(qtemporarydir) @@ -68,3 +68,9 @@ endif() if(QT_FEATURE_private_tests) add_subdirectory(qzip) endif() +if(NOT (MACOS AND "$ENV{QT_BUILD_ENVIRONMENT}" STREQUAL "ci")) + # On macOS the new features require at least macOS 13.3, + # but we also run the tests on older OS versions. + # So just skip macOS on CI for now. + add_subdirectory(qfloat16format) +endif() diff --git a/tests/auto/corelib/io/largefile/tst_largefile.cpp b/tests/auto/corelib/io/largefile/tst_largefile.cpp index 6fa3569c4f..f5af3bde63 100644 --- a/tests/auto/corelib/io/largefile/tst_largefile.cpp +++ b/tests/auto/corelib/io/largefile/tst_largefile.cpp @@ -46,6 +46,10 @@ public: // This means that files are limited to 2 GB − 1 bytes. // Limit max size to 256MB maxSizeBits = 28; // 256 MiB + #elif defined(Q_OS_VXWORKS) + // VxWorks doesn't support sparse files, also, default /tmp directory is a RAM-disk which + // limits its capacity. + maxSizeBits = 28; // 256 MiB #elif defined (Q_OS_WASM) maxSizeBits = 28; // 256 MiB #elif defined(QT_LARGEFILE_SUPPORT) @@ -494,6 +498,7 @@ void tst_LargeFile::mapFile() // 32-bit: limited to 44-bit offsets (when sizeof(off_t) == 8) //Windows: memory-mapping beyond EOF is not allowed //wasm: as for linux +//VxWorks: memory-mapping beyond EOF is not allowed void tst_LargeFile::mapOffsetOverflow() { enum { @@ -506,6 +511,9 @@ void tst_LargeFile::mapOffsetOverflow() #elif (defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)) && (Q_PROCESSOR_WORDSIZE == 4) Succeeds = true, MaxOffset = sizeof(QT_OFF_T) > 4 ? 43 : 30 +#elif defined(Q_OS_VXWORKS) + Succeeds = false, + MaxOffset = 8 * sizeof(QT_OFF_T) - 1 #else Succeeds = true, MaxOffset = 8 * sizeof(QT_OFF_T) - 1 diff --git a/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp index bb4e1b30d2..e2ac1858da 100644 --- a/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp +++ b/tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp @@ -85,9 +85,12 @@ private slots: void uncPaths(); #endif #ifndef Q_OS_WIN - void hiddenDirs_hiddenFiles(); + void hiddenFiles(); + void hiddenDirs(); #endif + void withStdAlgorithms(); + private: QSharedPointer<QTemporaryDir> m_dataDir; }; @@ -173,10 +176,10 @@ void tst_QDirListing::initTestCase() createFile("hiddenDirs_hiddenFiles/normalDirectory/.hiddenFile"); createFile("hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile"); createFile("hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile"); - createDirectory("hiddenDirs_hiddenFiles/normalDirectory/normalDirectory"); - createDirectory("hiddenDirs_hiddenFiles/normalDirectory/.hiddenDirectory"); - createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/normalDirectory"); - createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenDirectory"); + createDirectory("hiddenDirs_hiddenFiles/normalDirectory/subdir"); + createDirectory("hiddenDirs_hiddenFiles/normalDirectory/.hidden-subdir"); + createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/subdir"); + createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/.hidden-subdir"); #endif } @@ -571,40 +574,77 @@ void tst_QDirListing::uncPaths() // In Unix it is easy to create hidden files, but in Windows it requires // a special call since hidden files need to be "marked" while in Unix // anything starting by a '.' is a hidden file. -// For that reason this test is not run in Windows. -void tst_QDirListing::hiddenDirs_hiddenFiles() +// For that reason these two tests aren't run on Windows. + +void tst_QDirListing::hiddenFiles() { - // Only files - { - int matches = 0; - int failures = 0; - constexpr auto filters = QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot; - for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters, - ItFlag::Recursive)) { - ++matches; - if (dirEntry.isDir()) - ++failures; // search was only supposed to find files - } - QCOMPARE(matches, 6); - QCOMPARE(failures, 0); + QStringList expected = { + "hiddenDirs_hiddenFiles/normalFile"_L1, + "hiddenDirs_hiddenFiles/.hiddenFile"_L1, + "hiddenDirs_hiddenFiles/normalDirectory/normalFile"_L1, + "hiddenDirs_hiddenFiles/normalDirectory/.hiddenFile"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile"_L1, + }; + expected.sort(); + + constexpr auto filters = QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot; + QStringList list; + list.reserve(expected.size()); + for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters, + ItFlag::Recursive)) { + QVERIFY(dirEntry.isFile()); + list.emplace_back(dirEntry.filePath()); } - // Only directories - { - int matches = 0; - int failures = 0; - constexpr auto filters = QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot; - for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters, - ItFlag::Recursive)) { - ++matches; - if (!dirEntry.isDir()) - ++failures; // search was only supposed to find files - } - QCOMPARE(matches, 6); - QCOMPARE(failures, 0); + + list.sort(); + + QCOMPARE_EQ(list, expected); +} + +void tst_QDirListing::hiddenDirs() +{ + QStringList expected = { + "hiddenDirs_hiddenFiles/normalDirectory"_L1, + "hiddenDirs_hiddenFiles/normalDirectory/subdir"_L1, + "hiddenDirs_hiddenFiles/normalDirectory/.hidden-subdir"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/subdir"_L1, + "hiddenDirs_hiddenFiles/.hiddenDirectory/.hidden-subdir"_L1, + }; + expected.sort(); + + constexpr auto filters = QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot; + QStringList list; + list.reserve(expected.size()); + for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters, + ItFlag::Recursive)) { + QVERIFY(dirEntry.isDir()); + list.emplace_back(dirEntry.filePath()); } + list.sort(); + + QCOMPARE_EQ(list, expected); } + #endif // Q_OS_WIN +void tst_QDirListing::withStdAlgorithms() +{ + QDirListing dirList(u"entrylist"_s, QDir::AllEntries | QDir::NoDotAndDotDot, ItFlag::Recursive); + + std::for_each(dirList.cbegin(), dirList.cend(), [](const auto &dirEntry) { + QVERIFY(dirEntry.absoluteFilePath().contains("entrylist")); + }); + + const auto fileName = "dummy"_L1; + auto it = std::find_if(dirList.cbegin(), dirList.cend(), [fileName](const auto &dirEntry) { + return dirEntry.fileName() == fileName; + }); + QVERIFY(it != dirList.cend()); + QCOMPARE(it->fileName(), fileName); +} + QTEST_MAIN(tst_QDirListing) #include "tst_qdirlisting.moc" diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp index f7d531f61f..563e4c2a83 100644 --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -1217,9 +1217,16 @@ void tst_QFileInfo::setFileTimes() QCOMPARE(file.write(data), data.size()); QCOMPARE(file.size(), data.size()); - const QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(-5000); + QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(-5000); + QVERIFY(file.setFileTime(before, QFile::FileModificationTime)); const QDateTime mtime = file.fileTime(QFile::FileModificationTime).toUTC(); + if (mtime.time().msec() == 0) + { + const QTime beforeTime = before.time(); + const QTime beforeTimeWithMSCutOff{beforeTime.hour(), beforeTime.minute(), beforeTime.second(), 0}; + before.setTime(beforeTimeWithMSCutOff); + } QCOMPARE(mtime, before); } diff --git a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp index a5b0087f9c..184eef3f15 100644 --- a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp +++ b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp @@ -26,14 +26,18 @@ using namespace std::chrono_literals; #if defined(Q_OS_QNX) -// Longer polling times on QNX, otherwise the tests fail on the CI +constexpr bool isQNX = true; +#else +constexpr bool isQNX = false; +#endif + +#if defined(Q_OS_QNX) || defined(Q_OS_VXWORKS) +// Longer polling times on QNX and VxWorks, otherwise the tests fail on the CI constexpr auto nativeEngineTimeout = 1s; constexpr auto pollingEngineTimeout = 1s; -constexpr bool isQNX = true; #else constexpr auto nativeEngineTimeout = 0ms; constexpr auto pollingEngineTimeout = 20ms; -constexpr bool isQNX = false; #endif /* All tests need to run in temporary directories not used diff --git a/tests/auto/corelib/io/qfloat16format/CMakeLists.txt b/tests/auto/corelib/io/qfloat16format/CMakeLists.txt new file mode 100644 index 0000000000..0980007f69 --- /dev/null +++ b/tests/auto/corelib/io/qfloat16format/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfloat16format LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +set(CMAKE_OSX_DEPLOYMENT_TARGET 13.3) + +qt_internal_add_test(tst_qfloat16format + SOURCES + tst_qfloat16format.cpp +) + +set_target_properties(tst_qfloat16format + PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED OFF +) diff --git a/tests/auto/corelib/io/qfloat16format/tst_qfloat16format.cpp b/tests/auto/corelib/io/qfloat16format/tst_qfloat16format.cpp new file mode 100644 index 0000000000..63a27d55f6 --- /dev/null +++ b/tests/auto/corelib/io/qfloat16format/tst_qfloat16format.cpp @@ -0,0 +1,168 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> + +#include <QtCore/qcompilerdetection.h> +#include <QtCore/qdebug.h> +#include <QtCore/qfloat16format.h> +#include <QtCore/qstring.h> + +using namespace Qt::StringLiterals; + +class tst_QFloat16Format : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void formatCompileTime(); + void format_data(); + void format(); + void formatMultiArg(); +}; + +void tst_QFloat16Format::initTestCase() +{ +#ifndef QT_SUPPORTS_STD_FORMAT + QSKIP("This test requires std::format support!"); +#endif +} + +void tst_QFloat16Format::formatCompileTime() +{ +#ifdef QT_SUPPORTS_STD_FORMAT + // Starting from __cpp_lib_format == 202106L, + // std::format requires the format string to be evaluated at compile-time, + // so check it here. + + const qfloat16 val{1.234f}; + std::locale loc{"C"}; + + // char + std::string buffer; + std::format_to(std::back_inserter(buffer), "{}", val); + std::format_to(std::back_inserter(buffer), "{:*>15.7f}", val); + std::format_to(std::back_inserter(buffer), "{:*^+#15.7g}", val); + std::format_to(std::back_inserter(buffer), "{:*<-#15.7A}", val); + std::format_to(std::back_inserter(buffer), "{:*^ 15.7e}", val); + std::format_to(std::back_inserter(buffer), loc, "{:*^10.3Lf}", val); + std::format_to(std::back_inserter(buffer), loc, "{:*< 10.7LE}", val); + + // wchar_t + std::wstring wbuffer; + std::format_to(std::back_inserter(wbuffer), L"{}", val); + std::format_to(std::back_inserter(wbuffer), L"{:*>15.7f}", val); + std::format_to(std::back_inserter(wbuffer), L"{:*^+#15.7g}", val); + std::format_to(std::back_inserter(wbuffer), L"{:*<-#15.7A}", val); + std::format_to(std::back_inserter(wbuffer), L"{:*^ 15.7e}", val); + std::format_to(std::back_inserter(wbuffer), loc, L"{:*^10.3Lf}", val); + std::format_to(std::back_inserter(wbuffer), loc, L"{:*< 10.7LE}", val); +#endif // QT_SUPPORTS_STD_FORMAT +} + +void tst_QFloat16Format::format_data() +{ + QTest::addColumn<QString>("format"); + QTest::addColumn<qfloat16>("value"); + QTest::addColumn<QByteArray>("locName"); + QTest::addColumn<QString>("expectedString"); + + auto row = [](const QString &format, qfloat16 val, const QString &expected, + const QByteArray &loc = QByteArray()) + { + QString str; + QDebug dbg(&str); + dbg.nospace().noquote() << format; + if (!loc.isEmpty()) + dbg.nospace().noquote() << "_" << loc; + + QTest::newRow(qPrintable(str)) << format << val << loc << expected; + }; + + QByteArray loc; +#if defined(Q_CC_MSVC_ONLY) + loc = "de-DE"_ba; +#elif !defined(Q_CC_MINGW) // minGW has only C and POSIX locales + loc = "de_DE"_ba; +#endif + + row(u"{}"_s, qfloat16(1.f), u"1"_s); + row(u"{:#}"_s, qfloat16(1.f), u"1."_s); + row(u"{:f}"_s, qfloat16(1.f), u"1.000000"_s); + row(u"{:*>10.2a}"_s, qfloat16(-1.23f), u"**-1.3bp+0"_s); + if (!loc.isEmpty()) { + row(u"{:+Lf}"_s, qfloat16(1.f), u"+1,000000"_s, loc); + row(u"{:*^10.3LF}"_s, qfloat16(-0.1234f), u"**-0,123**"_s, loc); + row(u"{:*^#10.4Lg}"_s, qfloat16(-1.f), u"**-1,000**"_s, loc); + row(u"{:*<14.3LE}"_s, qfloat16(-0.1234f), u"-1,234E-01****"_s, loc); + } +} + +void tst_QFloat16Format::format() +{ +#ifdef QT_SUPPORTS_STD_FORMAT + QFETCH(const QString, format); + QFETCH(const qfloat16, value); + QFETCH(const QByteArray, locName); + QFETCH(const QString, expectedString); + + // char + { + std::string buffer; + const auto formatStr = format.toStdString(); + if (!locName.isEmpty()) { + std::locale loc(locName.constData()); + std::vformat_to(std::back_inserter(buffer), loc, formatStr, + std::make_format_args(value)); + } else { + std::vformat_to(std::back_inserter(buffer), formatStr, + std::make_format_args(value)); + } + const QString actualString = QString::fromStdString(buffer); + QCOMPARE_EQ(actualString, expectedString); + } + + // wchar_t + { + std::wstring buffer; + const auto formatStr = format.toStdWString(); + if (!locName.isEmpty()) { + std::locale loc(locName.constData()); + std::vformat_to(std::back_inserter(buffer), loc, formatStr, + std::make_wformat_args(value)); + } else { + std::vformat_to(std::back_inserter(buffer), formatStr, + std::make_wformat_args(value)); + } + const QString actualString = QString::fromStdWString(buffer); + QCOMPARE_EQ(actualString, expectedString); + } +#endif // QT_SUPPORTS_STD_FORMAT +} + +void tst_QFloat16Format::formatMultiArg() +{ +#ifdef QT_SUPPORTS_STD_FORMAT + const qfloat16 v1{-0.1234f}; + const qfloat16 v2{5.67f}; + + const QString expectedString = u"**+5.67**_*****-1.234E-01"_s; + // char + { + std::string buffer; + std::format_to(std::back_inserter(buffer), "{1:*^+9.2f}_{0:*>15.3E}", v1, v2); + QCOMPARE_EQ(QString::fromStdString(buffer), expectedString); + } + + // wchar_t + { + std::wstring buffer; + std::format_to(std::back_inserter(buffer), L"{1:*^+9.2f}_{0:*>15.3E}", v1, v2); + QCOMPARE_EQ(QString::fromStdWString(buffer), expectedString); + } +#endif // QT_SUPPORTS_STD_FORMAT +} + +QTEST_MAIN(tst_QFloat16Format) +#include "tst_qfloat16format.moc" diff --git a/tests/auto/corelib/io/qprocess/crasher.h b/tests/auto/corelib/io/qprocess/crasher.h index 7d55bf980f..f1ca82a86b 100644 --- a/tests/auto/corelib/io/qprocess/crasher.h +++ b/tests/auto/corelib/io/qprocess/crasher.h @@ -45,6 +45,8 @@ void crash() { #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) __ud2(); +#elif defined(_MSC_VER) && defined(_M_ARM64) + __debugbreak(); #elif __has_builtin(__builtin_trap) __builtin_trap(); #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) diff --git a/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat b/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat index 900f7ae356..8567e16850 100755 --- a/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat +++ b/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat @@ -1,2 +1,4 @@ +:: Copyright (C) 2024 The Qt Company Ltd. +:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 @echo off -echo Hello
\ No newline at end of file +echo Hello diff --git a/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat b/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat index 900f7ae356..8567e16850 100755 --- a/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat +++ b/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat @@ -1,2 +1,4 @@ +:: Copyright (C) 2024 The Qt Company Ltd. +:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 @echo off -echo Hello
\ No newline at end of file +echo Hello diff --git a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp index 6a2a3daaa2..560d4196b2 100644 --- a/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp +++ b/tests/auto/corelib/io/qprocessenvironment/tst_qprocessenvironment.cpp @@ -257,6 +257,9 @@ void tst_QProcessEnvironment::caseSensitivity() void tst_QProcessEnvironment::systemEnvironment() { +#ifdef Q_OS_VXWORKS + QSKIP("VxWorks do not have PATH environment variable"); +#endif static const char envname[] = "THIS_ENVIRONMENT_VARIABLE_HOPEFULLY_DOESNT_EXIST"; QByteArray path = qgetenv("PATH"); QByteArray nonexistant = qgetenv(envname); diff --git a/tests/auto/corelib/io/qresourceengine/generateResources.sh b/tests/auto/corelib/io/qresourceengine/generateResources.sh index 18d1e0b80f..ab05f1c35a 100755 --- a/tests/auto/corelib/io/qresourceengine/generateResources.sh +++ b/tests/auto/corelib/io/qresourceengine/generateResources.sh @@ -1,6 +1,6 @@ +#!/bin/sh # Copyright (C) 2016 Intel Corporation. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#!/bin/sh count=`awk '/ZERO_FILE_LEN/ { print $3 }' tst_qresourceengine.cpp` dd if=/dev/zero of=zero.txt bs=1 count=$count rcc --binary -o uncompressed.rcc --no-compress compressed.qrc diff --git a/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp b/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp index 39a3a1e012..c7b0fe7ea1 100644 --- a/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp +++ b/tests/auto/corelib/io/qresourceengine/staticplugin/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QObject> class PluginClass : public QObject diff --git a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp index cf0e258412..00a8cc4d72 100644 --- a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp +++ b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp @@ -45,6 +45,7 @@ private slots: private: const QString m_runtimeResourceRcc; + QByteArray m_runtimeResourceData; }; @@ -73,15 +74,27 @@ void tst_QResourceEngine::initTestCase() #endif QVERIFY(!m_runtimeResourceRcc.isEmpty()); + + QFile resourceFile(m_runtimeResourceRcc); + QVERIFY2(resourceFile.open(QIODevice::ReadOnly), qPrintable(resourceFile.errorString())); + + // register once with the file name, which will attempt to use mmap() + // (uses QDynamicFileResourceRoot) QVERIFY(QResource::registerResource(m_runtimeResourceRcc)); - QVERIFY(QResource::registerResource(m_runtimeResourceRcc, "/secondary_root/")); + + // and register a second time with a gifted memory block + // (uses QDynamicBufferResourceRoot) + m_runtimeResourceData = resourceFile.readAll(); + auto resourcePtr = reinterpret_cast<const uchar *>(m_runtimeResourceData.constData()); + QVERIFY(QResource::registerResource(resourcePtr, "/secondary_root/")); } void tst_QResourceEngine::cleanupTestCase() { // make sure we don't leak memory QVERIFY(QResource::unregisterResource(m_runtimeResourceRcc)); - QVERIFY(QResource::unregisterResource(m_runtimeResourceRcc, "/secondary_root/")); + auto resourcePtr = reinterpret_cast<const uchar *>(m_runtimeResourceData.constData()); + QVERIFY(QResource::unregisterResource(resourcePtr, "/secondary_root/")); } void tst_QResourceEngine::compressedResource_data() @@ -188,7 +201,6 @@ void tst_QResourceEngine::checkStructure_data() << (QStringList() #if defined(BUILTIN_TESTDATA) << "parentdir.txt" - << "runtime_resource.rcc" #endif << "search_file.txt" #if defined(BUILTIN_TESTDATA) diff --git a/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp b/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp index 6a6339a8ec..1301053c29 100644 --- a/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp +++ b/tests/auto/corelib/io/qstorageinfo/tst_qstorageinfo.cpp @@ -24,6 +24,8 @@ # include "../../../../../src/corelib/io/qstorageinfo_linux_p.h" #endif +using namespace Qt::StringLiterals; + class tst_QStorageInfo : public QObject { Q_OBJECT @@ -262,7 +264,9 @@ void tst_QStorageInfo::freeSpaceUpdate() FlushFileBuffers(HANDLE(_get_osfhandle(file.handle()))); #elif _POSIX_VERSION >= 200112L fsync(file.handle()); +# ifndef Q_OS_VXWORKS sync(); +# endif // Q_OS_VXWORKS #endif }; @@ -317,64 +321,64 @@ void tst_QStorageInfo::testParseMountInfo_data() QTest::newRow("tmpfs") << "17 25 0:18 / /dev rw,nosuid,relatime shared:2 - tmpfs tmpfs rw,seclabel,mode=755\n"_ba - << MountInfo{"/dev", "tmpfs", "tmpfs", "", makedev(0, 18)}; + << MountInfo{"/dev", "tmpfs", "tmpfs", "", makedev(0, 18), 17}; QTest::newRow("proc") << "23 66 0:21 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw\n"_ba - << MountInfo{"/proc", "proc", "proc", "", makedev(0, 21)}; + << MountInfo{"/proc", "proc", "proc", "", makedev(0, 21), 23}; // E.g. on Android QTest::newRow("rootfs") << "618 618 0:1 / / ro,relatime master:1 - rootfs rootfs ro,seclabel\n"_ba - << MountInfo{"/", "rootfs", "rootfs", "", makedev(0, 1)}; + << MountInfo{"/", "rootfs", "rootfs", "", makedev(0, 1), 618}; QTest::newRow("ext4") << "47 66 8:3 / /home rw,relatime shared:50 - ext4 /dev/sda3 rw,stripe=32736\n"_ba - << MountInfo{"/home", "ext4", "/dev/sda3", "", makedev(8, 3)}; + << MountInfo{"/home", "ext4", "/dev/sda3", "", makedev(8, 3), 47}; QTest::newRow("empty-optional-field") << "23 25 0:22 / /apex rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,seclabel,mode=755\n"_ba - << MountInfo{"/apex", "tmpfs", "tmpfs", "", makedev(0, 22)}; + << MountInfo{"/apex", "tmpfs", "tmpfs", "", makedev(0, 22), 23}; QTest::newRow("one-optional-field") << "47 66 8:3 / /home rw,relatime shared:50 - ext4 /dev/sda3 rw,stripe=32736\n"_ba - << MountInfo{"/home", "ext4", "/dev/sda3", "", makedev(8, 3)}; + << MountInfo{"/home", "ext4", "/dev/sda3", "", makedev(8, 3), 47}; QTest::newRow("multiple-optional-fields") << "47 66 8:3 / /home rw,relatime shared:142 master:111 - ext4 /dev/sda3 rw,stripe=32736\n"_ba - << MountInfo{"/home", "ext4", "/dev/sda3", "", makedev(8, 3)}; + << MountInfo{"/home", "ext4", "/dev/sda3", "", makedev(8, 3), 47}; QTest::newRow("mountdir-with-utf8") << "129 66 8:51 / /mnt/lab\xC3\xA9l rw,relatime shared:234 - ext4 /dev/sdd3 rw\n"_ba - << MountInfo{"/mnt/labél", "ext4", "/dev/sdd3", "", makedev(8, 51)}; + << MountInfo{"/mnt/labél", "ext4", "/dev/sdd3", "", makedev(8, 51), 129}; QTest::newRow("mountdir-with-space") << "129 66 8:51 / /mnt/labe\\040l rw,relatime shared:234 - ext4 /dev/sdd3 rw\n"_ba - << MountInfo{"/mnt/labe l", "ext4", "/dev/sdd3", "", makedev(8, 51)}; + << MountInfo{"/mnt/labe l", "ext4", "/dev/sdd3", "", makedev(8, 51), 129}; QTest::newRow("mountdir-with-tab") << "129 66 8:51 / /mnt/labe\\011l rw,relatime shared:234 - ext4 /dev/sdd3 rw\n"_ba - << MountInfo{"/mnt/labe\tl", "ext4", "/dev/sdd3", "", makedev(8, 51)}; + << MountInfo{"/mnt/labe\tl", "ext4", "/dev/sdd3", "", makedev(8, 51), 129}; QTest::newRow("mountdir-with-backslash") << "129 66 8:51 / /mnt/labe\\134l rw,relatime shared:234 - ext4 /dev/sdd3 rw\n"_ba - << MountInfo{"/mnt/labe\\l", "ext4", "/dev/sdd3", "", makedev(8, 51)}; + << MountInfo{"/mnt/labe\\l", "ext4", "/dev/sdd3", "", makedev(8, 51), 129}; QTest::newRow("mountdir-with-newline") << "129 66 8:51 / /mnt/labe\\012l rw,relatime shared:234 - ext4 /dev/sdd3 rw\n"_ba - << MountInfo{"/mnt/labe\nl", "ext4", "/dev/sdd3", "", makedev(8, 51)}; + << MountInfo{"/mnt/labe\nl", "ext4", "/dev/sdd3", "", makedev(8, 51), 129}; QTest::newRow("btrfs-subvol") << "775 503 0:49 /foo/bar / rw,relatime shared:142 master:111 - btrfs " "/dev/mapper/vg0-stuff rw,ssd,discard,space_cache,subvolid=272,subvol=/foo/bar\n"_ba - << MountInfo{"/", "btrfs", "/dev/mapper/vg0-stuff", "/foo/bar", makedev(0, 49)}; + << MountInfo{"/", "btrfs", "/dev/mapper/vg0-stuff", "/foo/bar", makedev(0, 49), 775}; QTest::newRow("bind-mount") << "59 47 8:17 /rpmbuild /home/user/rpmbuild rw,relatime shared:48 - ext4 /dev/sdb1 rw\n"_ba - << MountInfo{"/home/user/rpmbuild", "ext4", "/dev/sdb1", "/rpmbuild", makedev(8, 17)}; + << MountInfo{"/home/user/rpmbuild", "ext4", "/dev/sdb1", "/rpmbuild", makedev(8, 17), 59}; QTest::newRow("space-dash-space") << "47 66 8:3 / /home\\040-\\040dir rw,relatime shared:50 - ext4 /dev/sda3 rw,stripe=32736\n"_ba - << MountInfo{"/home - dir", "ext4", "/dev/sda3", "", makedev(8, 3)}; + << MountInfo{"/home - dir", "ext4", "/dev/sda3", "", makedev(8, 3), 47}; QTest::newRow("btrfs-mount-bind-file") << "1799 1778 0:49 " @@ -383,7 +387,7 @@ void tst_QStorageInfo::testParseMountInfo_data() "rw,ssd,discard,space_cache,subvolid=1773,subvol=/var_lib_docker\n"_ba << MountInfo{"/etc/resolv.conf", "btrfs", "/dev/mapper/vg0-stuff", "/var_lib_docker/containers/81fde0fec3dd3d99765c3f7fd9cf1ab121b6ffcfd05d5d7ff434db933fe9d795/resolv.conf", - makedev(0, 49)}; + makedev(0, 49), 1799}; QTest::newRow("very-long-line-QTBUG-77059") << "727 26 0:52 / " @@ -400,13 +404,13 @@ void tst_QStorageInfo::testParseMountInfo_data() "workdir=/var/lib/docker/overlay2/f3fbad5eedef71145f00729f0826ea8c44defcfec8c92c58aee0aa2c5ea3fa3a/work," "index=off,xino=off\n"_ba << MountInfo{"/var/lib/docker/overlay2/f3fbad5eedef71145f00729f0826ea8c44defcfec8c92c58aee0aa2c5ea3fa3a/merged", - "overlay", "overlay", "", makedev(0, 52)}; + "overlay", "overlay", "", makedev(0, 52), 727}; QTest::newRow("sshfs-src-device-not-start-with-slash") << "128 92 0:64 / /mnt-point rw,nosuid,nodev,relatime shared:234 - " "fuse.sshfs admin@192.168.1.2:/storage/emulated/0 rw,user_id=1000,group_id=1000\n"_ba << MountInfo{"/mnt-point", "fuse.sshfs", - "admin@192.168.1.2:/storage/emulated/0", "", makedev(0, 64)}; + "admin@192.168.1.2:/storage/emulated/0", "", makedev(0, 64), 128}; } void tst_QStorageInfo::testParseMountInfo() @@ -422,6 +426,7 @@ void tst_QStorageInfo::testParseMountInfo() QCOMPARE(a.device, expected.device); QCOMPARE(a.fsRoot, expected.fsRoot); QCOMPARE(a.stDev, expected.stDev); + QCOMPARE(a.mntid, expected.mntid); } void tst_QStorageInfo::testParseMountInfo_filtered_data() diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp index 579e6d5511..1419f06f86 100644 --- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp +++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp @@ -592,7 +592,9 @@ void tst_QTemporaryFile::rename() void tst_QTemporaryFile::renameFdLeak() { #if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID) - const QByteArray sourceFile = QFile::encodeName(QFINDTESTDATA("CMakeLists.txt")); + QTemporaryFile file; + QVERIFY(file.open()); + const QByteArray sourceFile = QFile::encodeName(file.fileName()); QVERIFY(!sourceFile.isEmpty()); // Test this on Unix only diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index bd454fb695..2024968435 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -3776,13 +3776,13 @@ void tst_QUrl::setComponents_data() << PrettyDecoded << QString() << "foo:/path"; QTest::newRow("host-empty") << QUrl("foo://example.com/path") << int(Host) << "" << Tolerant << true - << PrettyDecoded << QString() << "foo:///path"; + << PrettyDecoded << "" << "foo:///path"; QTest::newRow("authority-null") << QUrl("foo://example.com/path") << int(Authority) << QString() << Tolerant << true << PrettyDecoded << QString() << "foo:/path"; QTest::newRow("authority-empty") << QUrl("foo://example.com/path") << int(Authority) << "" << Tolerant << true - << PrettyDecoded << QString() << "foo:///path"; + << PrettyDecoded << "" << "foo:///path"; QTest::newRow("query-null") << QUrl("http://example.com/?q=foo") << int(Query) << QString() << Tolerant << true << PrettyDecoded << QString() << "http://example.com/"; @@ -3840,10 +3840,10 @@ void tst_QUrl::setComponents_data() << PrettyDecoded << QString() << QString(); QTest::newRow("invalid-authority-1") << QUrl("http://example.com") << int(Authority) << "-not-valid-" << Tolerant << false - << PrettyDecoded << QString() << QString(); + << PrettyDecoded << "" << QString(); QTest::newRow("invalid-authority-2") << QUrl("http://example.com") << int(Authority) << "%31%30.%30.%30.%31" << Strict << false - << PrettyDecoded << QString() << QString(); + << PrettyDecoded << "" << QString(); QTest::newRow("invalid-path-0") << QUrl("http://example.com") << int(Path) << "{}" << Strict << false diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp index 6b1e4ce9ba..c48b79b260 100644 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -993,9 +993,9 @@ void tst_QAbstractItemModel::complexChangesWithPersistent() void tst_QAbstractItemModel::modelIndexComparisons() { - QTestPrivate::testEqualityOperatorsCompile<QModelIndex>(); - QTestPrivate::testEqualityOperatorsCompile<QPersistentModelIndex>(); - QTestPrivate::testEqualityOperatorsCompile<QPersistentModelIndex, QModelIndex>(); + QTestPrivate::testAllComparisonOperatorsCompile<QModelIndex>(); + QTestPrivate::testAllComparisonOperatorsCompile<QPersistentModelIndex>(); + QTestPrivate::testAllComparisonOperatorsCompile<QPersistentModelIndex, QModelIndex>(); QtTestModel model(3, 3); @@ -1003,13 +1003,28 @@ void tst_QAbstractItemModel::modelIndexComparisons() QModelIndex mi22 = model.index(2, 2); QPersistentModelIndex pmi11 = mi11; QPersistentModelIndex pmi22 = mi22; + QPersistentModelIndex pmiU; QT_TEST_EQUALITY_OPS(mi11, mi11, true); QT_TEST_EQUALITY_OPS(mi11, mi22, false); + QT_TEST_ALL_COMPARISON_OPS(mi11, mi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(mi11, mi22, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(mi22, mi11, Qt::strong_ordering::greater); QT_TEST_EQUALITY_OPS(pmi11, pmi11, true); QT_TEST_EQUALITY_OPS(pmi11, pmi22, false); QT_TEST_EQUALITY_OPS(pmi11, mi11, true); QT_TEST_EQUALITY_OPS(pmi11, mi22, false); + + QT_TEST_ALL_COMPARISON_OPS(pmi11, pmi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(pmi11, pmi22, Qt::strong_ordering::less); + // Disengaged QPMIs are sorted randomly (based on address of their Private) + // So all we can check here is QPMIs with d == nullptr, which should reliably + // come before any others. + QT_TEST_ALL_COMPARISON_OPS(pmiU, pmiU, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(pmi11, pmiU, Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(pmi11, mi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(pmi11, mi22, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(pmiU, mi11, Qt::strong_ordering::less); } void tst_QAbstractItemModel::testMoveSameParentDown_data() diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp b/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp index 5c521ad625..08233a1f7b 100644 --- a/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp +++ b/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp @@ -2981,7 +2981,7 @@ void tst_QItemSelectionModel::destroyModel() selectionModel->setCurrentIndex(itemModel->index(1, 0), QItemSelectionModel::Select); QVERIFY(selectionModel->currentIndex().isValid()); - QTest::failOnWarning(QRegularExpression(".*")); + QTest::failOnWarning(); itemModel.reset(); QVERIFY(!selectionModel->currentIndex().isValid()); QVERIFY(selectionModel->selection().isEmpty()); diff --git a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp index 62c402ae24..4711853c88 100644 --- a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp +++ b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp @@ -20,6 +20,10 @@ #include <qelapsedtimer.h> #include <qproperty.h> +#if defined(Q_OS_WIN32) +#include <qt_windows.h> +#endif + #if defined Q_OS_UNIX #include <unistd.h> #endif @@ -57,6 +61,7 @@ private slots: void recurringTimer(); void deleteLaterOnQChronoTimer(); // long name, don't want to shadow QObject::deleteLater() void moveToThread(); + void newTimerFiresTooSoon(); void restartedTimerFiresTooSoon(); void timerFiresOnlyOncePerProcessEvents_data(); void timerFiresOnlyOncePerProcessEvents(); @@ -619,6 +624,51 @@ void tst_QChronoTimer::moveToThread() QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff)); } +class TimerListener : public QObject +{ + Q_OBJECT +public: + void timerEvent(QTimerEvent *) override + { + m_timerElapsed = true; + } + + bool m_timerElapsed = false; +}; + +void tst_QChronoTimer::newTimerFiresTooSoon() +{ +#ifndef Q_OS_WIN32 + QSKIP("Only relevant on Windows"); +#else + // Arrange - Create timer and make sure it ticked + { + QTest::qWait(0 /*ms*/); // Clean up event queue from previous tests + + TimerListener listener; + const int timerId = listener.startTimer(50ms, Qt::CoarseTimer); + QThread::sleep(100ms); + + // Force WM_TIMER events on Windows event queue + MSG msg{}; + PeekMessage(&msg, nullptr, WM_TIMER, WM_TIMER, PM_NOREMOVE); + + listener.killTimer(timerId); + } + + // Act - Create new timer with long interval and make sure it does not immediately tick + TimerListener listener; + const int timerId = listener.startTimer(60s, Qt::CoarseTimer); + + QTest::qWait(0 /*ms*/); // Process event queue - Should not call timerEvent + listener.killTimer(timerId); + + // Assert + QEXPECT_FAIL("", "QTBUG-124496 - QObject::timerEvent may be called before the timer elapsed", Continue); + QVERIFY(!listener.m_timerElapsed); +#endif +} + class RestartedTimerFiresTooSoonObject : public QObject { Q_OBJECT diff --git a/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt b/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt index 9a40a2f905..eccde38df2 100644 --- a/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qelapsedtimer SOURCES tst_qelapsedtimer.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp b/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp index 7623fd2e43..7a2b12b2eb 100644 --- a/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp +++ b/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp @@ -5,6 +5,7 @@ #include <QtCore/QString> #include <QtCore/QElapsedTimer> #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <QTimer> static const int minResolution = 100; // the minimum resolution for the tests @@ -22,6 +23,7 @@ class tst_QElapsedTimer : public QObject Q_OBJECT private Q_SLOTS: + void compareCompiles(); void statics(); void validity(); void basics(); @@ -29,6 +31,11 @@ private Q_SLOTS: void msecsTo(); }; +void tst_QElapsedTimer::compareCompiles() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QElapsedTimer>(); +} + void tst_QElapsedTimer::statics() { // these have been required since Qt 6.6 @@ -77,6 +84,7 @@ void tst_QElapsedTimer::basics() QVERIFY(!(t1 < t1)); QCOMPARE(t1.msecsTo(t1), qint64(0)); QCOMPARE(t1.secsTo(t1), qint64(0)); + QT_TEST_ALL_COMPARISON_OPS(t1, t1, Qt::strong_ordering::equal); quint64 value1 = t1.msecsSinceReference(); qDebug() << "value1:" << value1 << "t1:" << t1; @@ -141,10 +149,16 @@ void tst_QElapsedTimer::msecsTo() QTest::qSleep(minResolution); QElapsedTimer t2; t2.start(); - - QVERIFY(t1 != t2); - QVERIFY(!(t1 == t2)); - QVERIFY(t1 < t2); + QTest::qSleep(minResolution); + QElapsedTimer t3; + t3.start(); + + QT_TEST_EQUALITY_OPS(t1, t2, false); + QT_TEST_EQUALITY_OPS(QElapsedTimer(), QElapsedTimer(), true); + QT_TEST_EQUALITY_OPS(QElapsedTimer(), t2, false); + QT_TEST_ALL_COMPARISON_OPS(t1, t2, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(t3, t2, Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(t3, QElapsedTimer(), Qt::strong_ordering::greater); auto diff = t1.msecsTo(t2); QVERIFY2(diff > 0, QString("difference t1 and t2 is %1").arg(diff).toLatin1()); diff --git a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp index b7b1f95b39..20dfcfd4d7 100644 --- a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp +++ b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp @@ -81,6 +81,7 @@ VERIFY_RETURN_FOR_TYPE(QJniArray<List>, QJniArray<List>); void tst_QJniArray::construct() { + // explicit { QStringList strings; for (int i = 0; i < 10000; ++i) @@ -89,13 +90,36 @@ void tst_QJniArray::construct() QCOMPARE(list.size(), 10000); } { - QJniArray<jint> list{1, 2, 3}; + QJniArray bytes = QJniArrayBase::fromContainer(QByteArray("abc")); + static_assert(std::is_same_v<decltype(bytes)::value_type, jbyte>); + QCOMPARE(bytes.size(), 3); + } + { + QJniArray list{1, 2, 3}; + static_assert(std::is_same_v<decltype(list), QJniArray<int>>); QCOMPARE(list.size(), 3); + list = {4, 5}; + QCOMPARE(list.size(), 2); } { QJniArray<jint> list(QList<int>{1, 2, 3}); QCOMPARE(list.size(), 3); } + // CTAD with deduction guide + { + QJniArray list(QList<int>{1, 2, 3}); + QCOMPARE(list.size(), 3); + } + { + QJniArray bytes(QByteArray("abc")); + static_assert(std::is_same_v<decltype(bytes)::value_type, jbyte>); + QCOMPARE(bytes.size(), 3); + } + { + QStringList strings{"a", "b", "c"}; + QJniArray list(strings); + QCOMPARE(list.size(), 3); + } { QJniArray<jint> list{QList<int>{1, 2, 3}}; QCOMPARE(list.size(), 3); @@ -110,7 +134,7 @@ void tst_QJniArray::size() QList<int> intList; intList.resize(10); - auto intArray = QJniArrayBase::fromContainer(intList); + auto intArray = QJniArray(intList); QCOMPARE(intArray.size(), 10); } diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 64b464e002..196874b6a5 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -1627,7 +1627,7 @@ void tst_QJniObject::templateApiCheck() QCOMPARE(array.size(), 3); QCOMPARE(array.toContainer(), "abc"); - QJniArray<jbyte> newArray = QJniArrayBase::fromContainer(QByteArray{"cba"}); + QJniArray newArray = QJniArray(QByteArray{"cba"}); QVERIFY(newArray.isValid()); const auto reverse = testClass.callMethod<jbyte[]>("reverseByteArray", newArray); QVERIFY(reverse.isValid()); diff --git a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp index e67bab7da4..3bf6211a53 100644 --- a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp +++ b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp @@ -335,7 +335,7 @@ void tst_QMetaProperty::conversion() void tst_QMetaProperty::enumsFlags() { // QTBUG-83689, verify that enumerations and flags can be assigned from int, - // which is important for Qt Designer. + // which is important for Qt Widgets Designer. EnumFlagsTester t; auto mo = t.metaObject(); diff --git a/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt b/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt index b93d961109..65bec3e187 100644 --- a/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt @@ -51,6 +51,7 @@ qt_internal_add_test(tst_qmetatype ../../../other/qvariant_common LIBRARIES Qt::CorePrivate + Qt::TestPrivate Qt::Gui qmetatype_lib1 qmetatype_lib2 diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h index 1694e49491..bcd2fe2def 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h @@ -111,6 +111,7 @@ private slots: void customDebugStream(); void unknownType(); void fromType(); + void compareCompiles(); void operatorEq_data(); void operatorEq(); void operatorEq2_data(); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp index 68bcb53056..661c1f6072 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp @@ -5,6 +5,7 @@ #include "tst_qmetatype_libs.h" #include <QtCore/private/qmetaobjectbuilder_p.h> +#include <QtTest/private/qcomparisontesthelper_p.h> void tst_QMetaType::constRefs() { @@ -418,6 +419,11 @@ struct CharTemplate } y; }; +void tst_QMetaType::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QMetaType>(); +} + void tst_QMetaType::operatorEq_data() { QTest::addColumn<QMetaType>("typeA"); @@ -447,10 +453,7 @@ void tst_QMetaType::operatorEq() QFETCH(QMetaType, typeB); QFETCH(bool, eq); - QCOMPARE(typeA == typeB, eq); - QCOMPARE(typeB == typeA, eq); - QCOMPARE(typeA != typeB, !eq); - QCOMPARE(typeB != typeA, !eq); + QT_TEST_EQUALITY_OPS(typeA, typeB, eq); #if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) // for built-in types or locally-defined types, this must also hold true @@ -487,10 +490,10 @@ FOR_EACH_CORE_METATYPE(GET_METATYPE_FROM_TYPE) QCOMPARE(fromId2.id(), type); // confirm that they're all equal - QCOMPARE(fromId1, fromId2); - QCOMPARE(fromType1, fromType2); - QCOMPARE(fromType1, fromId1); - QCOMPARE(fromType2, fromId2); + QT_TEST_EQUALITY_OPS(fromId1, fromId2, true); + QT_TEST_EQUALITY_OPS(fromType1, fromType2, true); + QT_TEST_EQUALITY_OPS(fromType1, fromId1, true); + QT_TEST_EQUALITY_OPS(fromType2, fromId2, true); #if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) // for built-in types (other than void), this must be true @@ -541,7 +544,7 @@ void tst_QMetaType::operatorEqAcrossLibs() // DO THIS FIRST: // if this isn't a built-in type, then the QMetaTypeInterface::typeId is // initially set to 0 - QCOMPARE(lib1Type, lib2Type); + QT_TEST_EQUALITY_OPS(lib1Type, lib2Type, true); int actualTypeId = localType.id(); bool builtinTypeExpected = builtinTypeId != QMetaType::UnknownType; @@ -559,8 +562,8 @@ void tst_QMetaType::operatorEqAcrossLibs() QCOMPARE(lib2Type.id(), actualTypeId); QCOMPARE(QByteArray(lib1Type.name()), QByteArray(localType.name())); QCOMPARE(QByteArray(lib2Type.name()), QByteArray(localType.name())); - QCOMPARE(lib1Type, localType); - QCOMPARE(lib2Type, localType); + QT_TEST_EQUALITY_OPS(lib1Type, localType, true); + QT_TEST_EQUALITY_OPS(lib2Type, localType, true); #if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) if (actualTypeId < QMetaType::FirstGuiType && actualTypeId != QMetaType::Void) { diff --git a/tests/auto/corelib/kernel/qpointer/CMakeLists.txt b/tests/auto/corelib/kernel/qpointer/CMakeLists.txt index b1570b8cef..0b2c4b7e45 100644 --- a/tests/auto/corelib/kernel/qpointer/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qpointer/CMakeLists.txt @@ -20,6 +20,7 @@ qt_internal_add_test(tst_qpointer tst_qpointer.cpp LIBRARIES Qt::Gui + Qt::TestPrivate ) ## Scopes: diff --git a/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp b/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp index 7365fee819..b88f1b5b0f 100644 --- a/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp +++ b/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <QRunnable> #include <QThreadPool> @@ -23,6 +24,7 @@ private slots: void conversion(); void destructor(); void assignment_operators(); + void compareCompiles(); void equality_operators(); void swap(); void isNull(); @@ -203,12 +205,21 @@ void tst_QPointer::assignment_operators() delete object; } +void tst_QPointer::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>>(); + QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, QObject*>(); + QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, QWidget*>(); + QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, QPointer<QWidget>>(); + QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, std::nullptr_t>(); +} + void tst_QPointer::equality_operators() { QPointer<QObject> p1; QPointer<QObject> p2; - QVERIFY(p1 == p2); + QT_TEST_EQUALITY_OPS(p1, p2, true); QObject *object = nullptr; #ifndef QT_NO_WIDGETS @@ -216,16 +227,15 @@ void tst_QPointer::equality_operators() #endif p1 = object; - QVERIFY(p1 == p2); - QVERIFY(p1 == object); + QT_TEST_EQUALITY_OPS(p1, p2, true); + QT_TEST_EQUALITY_OPS(p1, object, true); p2 = object; - QVERIFY(p2 == p1); - QVERIFY(p2 == object); - + QT_TEST_EQUALITY_OPS(p2, p1, true); + QT_TEST_EQUALITY_OPS(p2, object, true); p1 = this; - QVERIFY(p1 != p2); + QT_TEST_EQUALITY_OPS(p1, p2, false); p2 = p1; - QVERIFY(p1 == p2); + QT_TEST_EQUALITY_OPS(p1, p2, true); // compare to zero p1 = nullptr; @@ -233,19 +243,13 @@ void tst_QPointer::equality_operators() QVERIFY(0 == p1); QVERIFY(p2 != 0); QVERIFY(0 != p2); - QVERIFY(p1 == nullptr); - QVERIFY(nullptr == p1); - QVERIFY(p2 != nullptr); - QVERIFY(nullptr != p2); - QVERIFY(p1 == object); - QVERIFY(object == p1); - QVERIFY(p2 != object); - QVERIFY(object != p2); + QT_TEST_EQUALITY_OPS(p1, nullptr, true); + QT_TEST_EQUALITY_OPS(p2, nullptr, false); + QT_TEST_EQUALITY_OPS(p1, object, true); + QT_TEST_EQUALITY_OPS(p2, object, false); #ifndef QT_NO_WIDGETS - QVERIFY(p1 == widget); - QVERIFY(widget == p1); - QVERIFY(p2 != widget); - QVERIFY(widget != p2); + QT_TEST_EQUALITY_OPS(p1, widget, true); + QT_TEST_EQUALITY_OPS(p2, widget, false); #endif } diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 23d41cafb2..d79e0e82f8 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -5,6 +5,8 @@ #include <qvariant.h> +#include <QtCore/qttypetraits.h> + // don't assume <type_traits> template <typename T, typename U> constexpr inline bool my_is_same_v = false; @@ -544,6 +546,9 @@ void tst_QVariant::isNull() var3 = QVariant(QMetaType::fromType<QString>()); QVERIFY( var3.isNull() ); + var3.setValue(QString()); + QVERIFY( !var3.isNull() ); + QVariant var4( 0 ); QVERIFY( !var4.isNull() ); diff --git a/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp b/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp index 5ad961ee66..3c609238fc 100644 --- a/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp +++ b/tests/auto/corelib/platform/windows/qcomobject/tst_qcomobject.cpp @@ -69,6 +69,7 @@ struct QComObjectTraits<IDirect> }; } // namespace QtPrivate +QT_END_NAMESPACE class tst_QComObject : public QObject { @@ -263,6 +264,4 @@ void tst_QComObject::Release_decrementsReferenceCountByOne() QTEST_MAIN(tst_QComObject) # include "tst_qcomobject.moc" -QT_END_NAMESPACE - #endif // Q_OS_WIN diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/CMakeLists.txt b/tests/auto/corelib/plugin/qpluginloader/machtest/CMakeLists.txt index 6cb69fc46d..daf922b42c 100644 --- a/tests/auto/corelib/plugin/qpluginloader/machtest/CMakeLists.txt +++ b/tests/auto/corelib/plugin/qpluginloader/machtest/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause set_directory_properties(PROPERTIES _qt_good_targets "" _qt_stub_targets "" diff --git a/tests/auto/corelib/plugin/qpluginloader/machtest/stub.cpp b/tests/auto/corelib/plugin/qpluginloader/machtest/stub.cpp index 24bac391fb..eed7228a29 100644 --- a/tests/auto/corelib/plugin/qpluginloader/machtest/stub.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/machtest/stub.cpp @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only void dummy() {} diff --git a/tests/auto/corelib/plugin/quuid/test/CMakeLists.txt b/tests/auto/corelib/plugin/quuid/test/CMakeLists.txt index 1e1e820b14..ec6c1979f7 100644 --- a/tests/auto/corelib/plugin/quuid/test/CMakeLists.txt +++ b/tests/auto/corelib/plugin/quuid/test/CMakeLists.txt @@ -9,6 +9,8 @@ qt_internal_add_test(tst_quuid OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../" SOURCES ../tst_quuid.cpp + LIBRARIES + Qt::TestPrivate ) ## Scopes: diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp index c5ce490b61..08a5b826e9 100644 --- a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp +++ b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp @@ -3,6 +3,7 @@ #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #if QT_CONFIG(process) #include <QProcess> #endif @@ -17,6 +18,7 @@ class tst_QUuid : public QObject private slots: void initTestCase(); + void compareCompiles(); void fromChar(); void toString(); void fromString_data(); @@ -89,20 +91,28 @@ void tst_QUuid::initTestCase() uuidD = QUuid(0x21f7f8de, 0x8051, 0x5b89, 0x86, 0x80, 0x01, 0x95, 0xef, 0x79, 0x8b, 0x6a); } +void tst_QUuid::compareCompiles() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QUuid>(); +#if defined(Q_OS_WIN) + QTestPrivate::testEqualityOperatorsCompile<QUuid, GUID>(); +#endif +} + void tst_QUuid::fromChar() { - QCOMPARE(uuidA, QUuid("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}")); - QCOMPARE(uuidA, QUuid("fc69b59e-cc34-4436-a43c-ee95d128b8c5}")); - QCOMPARE(uuidA, QUuid("{fc69b59e-cc34-4436-a43c-ee95d128b8c5")); - QCOMPARE(uuidA, QUuid("fc69b59e-cc34-4436-a43c-ee95d128b8c5")); - QCOMPARE(QUuid(), QUuid("{fc69b59e-cc34-4436-a43c-ee95d128b8c")); - QCOMPARE(QUuid(), QUuid("{fc69b59e-cc34")); - QCOMPARE(QUuid(), QUuid("fc69b59e-cc34-")); - QCOMPARE(QUuid(), QUuid("fc69b59e-cc34")); - QCOMPARE(QUuid(), QUuid("cc34")); - QCOMPARE(QUuid(), QUuid(nullptr)); - - QCOMPARE(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"))); + QT_TEST_EQUALITY_OPS(uuidA, QUuid("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}"), true); + QT_TEST_EQUALITY_OPS(uuidA, QUuid("fc69b59e-cc34-4436-a43c-ee95d128b8c5}"), true); + QT_TEST_EQUALITY_OPS(uuidA, QUuid("{fc69b59e-cc34-4436-a43c-ee95d128b8c5"), true); + QT_TEST_EQUALITY_OPS(uuidA, QUuid("fc69b59e-cc34-4436-a43c-ee95d128b8c5"), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid("{fc69b59e-cc34-4436-a43c-ee95d128b8c"), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid("{fc69b59e-cc34"), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid("fc69b59e-cc34-"), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid("fc69b59e-cc34"), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid("cc34"), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid(nullptr), true); + + QT_TEST_EQUALITY_OPS(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}")), true); } void tst_QUuid::toString() @@ -162,21 +172,21 @@ void tst_QUuid::fromString() const auto inputL1 = input.toLatin1(); const auto inputU8 = input.toUtf8(); - QCOMPARE(expected, QUuid(input)); - QCOMPARE(expected, QUuid(inputU8)); - QCOMPARE(expected, QUuid(inputL1)); + QT_TEST_EQUALITY_OPS(expected, QUuid(input), true); + QT_TEST_EQUALITY_OPS(expected, QUuid(inputU8), true); + QT_TEST_EQUALITY_OPS(expected, QUuid(inputL1), true); - QCOMPARE(expected, QUuid::fromString(input)); + QT_TEST_EQUALITY_OPS(expected, QUuid::fromString(input), true); // for QLatin1String, construct one whose data() is not NUL-terminated: const auto longerInputL1 = inputL1 + '5'; // the '5' makes the premature end check incorrectly succeed const auto inputL1S = QLatin1String(longerInputL1.data(), inputL1.size()); - QCOMPARE(expected, QUuid::fromString(inputL1S)); + QT_TEST_EQUALITY_OPS(expected, QUuid::fromString(inputL1S), true); // for QUtf8StringView, too: const auto longerInputU8 = inputU8 + '5'; // the '5' makes the premature end check incorrectly succeed const auto inputU8S = QUtf8StringView(longerInputU8.data(), inputU8.size()); - QCOMPARE(expected, QUuid::fromString(inputU8S)); + QT_TEST_EQUALITY_OPS(expected, QUuid::fromString(inputU8S), true); } void tst_QUuid::toByteArray() @@ -196,27 +206,30 @@ void tst_QUuid::toByteArray() void tst_QUuid::fromByteArray() { - QCOMPARE(uuidA, QUuid(QByteArray("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}"))); - QCOMPARE(uuidA, QUuid(QByteArray("fc69b59e-cc34-4436-a43c-ee95d128b8c5}"))); - QCOMPARE(uuidA, QUuid(QByteArray("{fc69b59e-cc34-4436-a43c-ee95d128b8c5"))); - QCOMPARE(uuidA, QUuid(QByteArray("fc69b59e-cc34-4436-a43c-ee95d128b8c5"))); - QCOMPARE(QUuid(), QUuid(QByteArray("{fc69b59e-cc34-4436-a43c-ee95d128b8c"))); + QT_TEST_EQUALITY_OPS(uuidA, QUuid(QByteArray("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}")), true); + QT_TEST_EQUALITY_OPS(uuidA, QUuid(QByteArray("fc69b59e-cc34-4436-a43c-ee95d128b8c5}")), true); + QT_TEST_EQUALITY_OPS(uuidA, QUuid(QByteArray("{fc69b59e-cc34-4436-a43c-ee95d128b8c5")), true); + QT_TEST_EQUALITY_OPS(uuidA, QUuid(QByteArray("fc69b59e-cc34-4436-a43c-ee95d128b8c5")), true); + QT_TEST_EQUALITY_OPS(QUuid(), QUuid(QByteArray("{fc69b59e-cc34-4436-a43c-ee95d128b8c")), true); - QCOMPARE(uuidB, QUuid(QByteArray("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"))); + QT_TEST_EQUALITY_OPS(uuidB, QUuid(QByteArray("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}")), true); } void tst_QUuid::toRfc4122() { QCOMPARE(uuidA.toRfc4122(), QByteArray::fromHex("fc69b59ecc344436a43cee95d128b8c5")); - QCOMPARE(uuidB.toRfc4122(), QByteArray::fromHex("1ab6e93ab1cb4a87ba47ec7e99039a7b")); } void tst_QUuid::fromRfc4122() { - QCOMPARE(uuidA, QUuid::fromRfc4122(QByteArray::fromHex("fc69b59ecc344436a43cee95d128b8c5"))); + QT_TEST_EQUALITY_OPS( + uuidA, + QUuid::fromRfc4122(QByteArray::fromHex("fc69b59ecc344436a43cee95d128b8c5")), true); - QCOMPARE(uuidB, QUuid::fromRfc4122(QByteArray::fromHex("1ab6e93ab1cb4a87ba47ec7e99039a7b"))); + QT_TEST_EQUALITY_OPS( + uuidB, QUuid::fromRfc4122(QByteArray::fromHex("1ab6e93ab1cb4a87ba47ec7e99039a7b")), + true); } void tst_QUuid::id128() @@ -234,15 +247,15 @@ void tst_QUuid::id128() 0xba, 0x47, 0xec, 0x7e, 0x99, 0x03, 0x9a, 0x7b, } }; - QCOMPARE(QUuid(bytesA), uuidA); - QCOMPARE(QUuid(bytesB), uuidB); + QT_TEST_EQUALITY_OPS(QUuid(bytesA), uuidA, true); + QT_TEST_EQUALITY_OPS(QUuid(bytesB), uuidB, true); QVERIFY(memcmp(uuidA.toBytes().data, bytesA.data, sizeof(QUuid::Id128Bytes)) == 0); QVERIFY(memcmp(uuidB.toBytes().data, bytesB.data, sizeof(QUuid::Id128Bytes)) == 0); QUuid::Id128Bytes leBytesA = {}; for (int i = 0; i < 16; i++) leBytesA.data[15 - i] = bytesA.data[i]; - QCOMPARE(QUuid(leBytesA, QSysInfo::LittleEndian), uuidA); + QT_TEST_EQUALITY_OPS(QUuid(leBytesA, QSysInfo::LittleEndian), uuidA, true); QVERIFY(memcmp(uuidA.toBytes(QSysInfo::LittleEndian).data, leBytesA.data, sizeof(leBytesA)) == 0); // check the new q{To,From}{Big,Little}Endian() overloads @@ -271,16 +284,16 @@ void tst_QUuid::uint128() constexpr QUuid uuid = QUuid::fromUInt128(be); static_assert(uuid.toUInt128() == be, "Round-trip through QUuid failed"); - QCOMPARE(uuid, uuidA); + QT_TEST_EQUALITY_OPS(uuid, uuidA, true); QCOMPARE(uuid.toUInt128(), be); quint128 le = qFromBigEndian(be); QCOMPARE(uuid.toUInt128(QSysInfo::LittleEndian), le); - QCOMPARE(QUuid::fromUInt128(le, QSysInfo::LittleEndian), uuidA); + QT_TEST_EQUALITY_OPS(QUuid::fromUInt128(le, QSysInfo::LittleEndian), uuidA, true); QUuid::Id128Bytes bytes = { .data128 = { qToBigEndian(u) } }; QUuid uuid2(bytes); - QCOMPARE(uuid2, uuid); + QT_TEST_EQUALITY_OPS(uuid2, uuid, true); // verify that toBytes() and toUInt128() provide bytewise similar result constexpr quint128 val = uuid.toUInt128(); @@ -294,11 +307,11 @@ void tst_QUuid::uint128() void tst_QUuid::createUuidV3OrV5() { //"www.widgets.com" is also from RFC4122 - QCOMPARE(uuidC, QUuid::createUuidV3(uuidNS, QByteArray("www.widgets.com"))); - QCOMPARE(uuidC, QUuid::createUuidV3(uuidNS, QString("www.widgets.com"))); + QT_TEST_EQUALITY_OPS(uuidC, QUuid::createUuidV3(uuidNS, QByteArray("www.widgets.com")), true); + QT_TEST_EQUALITY_OPS(uuidC, QUuid::createUuidV3(uuidNS, QString("www.widgets.com")), true); - QCOMPARE(uuidD, QUuid::createUuidV5(uuidNS, QByteArray("www.widgets.com"))); - QCOMPARE(uuidD, QUuid::createUuidV5(uuidNS, QString("www.widgets.com"))); + QT_TEST_EQUALITY_OPS(uuidD, QUuid::createUuidV5(uuidNS, QByteArray("www.widgets.com")), true); + QT_TEST_EQUALITY_OPS(uuidD, QUuid::createUuidV5(uuidNS, QString("www.widgets.com")), true); } void tst_QUuid::check_QDataStream() @@ -314,7 +327,7 @@ void tst_QUuid::check_QDataStream() QDataStream in(&ar,QIODevice::ReadOnly); in.setByteOrder(QDataStream::BigEndian); in >> tmp; - QCOMPARE(uuidA, tmp); + QT_TEST_EQUALITY_OPS(uuidA, tmp, true); } { QDataStream out(&ar,QIODevice::WriteOnly); @@ -325,7 +338,7 @@ void tst_QUuid::check_QDataStream() QDataStream in(&ar,QIODevice::ReadOnly); in.setByteOrder(QDataStream::LittleEndian); in >> tmp; - QCOMPARE(uuidA, tmp); + QT_TEST_EQUALITY_OPS(uuidA, tmp, true); } } @@ -340,14 +353,14 @@ void tst_QUuid::isNull() void tst_QUuid::equal() { - QVERIFY( !(uuidA == uuidB) ); + QT_TEST_EQUALITY_OPS(uuidA, uuidB, false); QUuid copy(uuidA); - QCOMPARE(uuidA, copy); + QT_TEST_EQUALITY_OPS(uuidA, copy, true); QUuid assigned; assigned = uuidA; - QCOMPARE(uuidA, assigned); + QT_TEST_EQUALITY_OPS(uuidA, assigned, true); } @@ -384,10 +397,12 @@ void tst_QUuid::less() QVERIFY( uuidB <= uuidA); QVERIFY(!(uuidA < uuidB) ); QVERIFY(!(uuidA <= uuidB)); + QT_TEST_ALL_COMPARISON_OPS(uuidB, uuidA, Qt::strong_ordering::less); QUuid null_uuid; QVERIFY(null_uuid < uuidA); // Null uuid is always less than a valid one QVERIFY(null_uuid <= uuidA); + QT_TEST_ALL_COMPARISON_OPS(null_uuid, uuidA, Qt::strong_ordering::less); QVERIFY(null_uuid <= null_uuid); QVERIFY(uuidA <= uuidA); @@ -400,6 +415,7 @@ void tst_QUuid::more() QVERIFY( uuidA >= uuidB); QVERIFY(!(uuidB > uuidA)); QVERIFY(!(uuidB >= uuidA)); + QT_TEST_ALL_COMPARISON_OPS(uuidA, uuidB, Qt::strong_ordering::greater); QUuid null_uuid; QVERIFY(!(null_uuid > uuidA)); // Null uuid is always less than a valid one @@ -407,6 +423,7 @@ void tst_QUuid::more() QVERIFY(null_uuid >= null_uuid); QVERIFY(uuidA >= uuidA); + QT_TEST_ALL_COMPARISON_OPS(uuidA, uuidA, Qt::strong_ordering::equal); } @@ -512,7 +529,7 @@ void tst_QUuid::qvariant() QUuid uuid2 = v.value<QUuid>(); QVERIFY(!uuid2.isNull()); - QCOMPARE(uuid, uuid2); + QT_TEST_EQUALITY_OPS(uuid, uuid2, true); } void tst_QUuid::qvariant_conversion() @@ -544,7 +561,7 @@ void tst_QUuid::qvariant_conversion() QVariant sv = QVariant::fromValue(uuid.toByteArray()); QCOMPARE(sv.metaType(), QMetaType(QMetaType::QByteArray)); QVERIFY(sv.canConvert<QUuid>()); - QCOMPARE(sv.value<QUuid>(), uuid); + QT_TEST_EQUALITY_OPS(sv.value<QUuid>(), uuid, true); } } diff --git a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp index 0eaadb870c..398be96530 100644 --- a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp +++ b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp @@ -356,6 +356,29 @@ private Q_SLOTS: fromLiteral(u8"Hello, World!"); // char[] in <= C++17, char8_t[] in >= C++20 } + void fromChar() const { fromCharacter('\xE4', 1); } + void fromUChar() const { fromCharacter(static_cast<unsigned char>('\xE4'), 1); } + void fromSChar() const { fromCharacter(static_cast<signed char>('\xE4'), 1); } + void fromChar16T() const { fromCharacter(u'ä', 1); } + void fromUShort() const { fromCharacter(ushort(0xE4), 1); } + void fromChar32T() const { + fromCharacter(U'ä', 1); + if (QTest::currentTestFailed()) + return; + fromCharacter(U'\x1F0A0', 2); // U+1F0A0: PLAYING CARD BACK + } + void fromWCharT() const { + ONLY_WIN(fromCharacter(L'ä', 1)); // should work on Unix, too (char32_t does) + } + void fromQChar() const { fromCharacter(QChar(u'ä'), 1); } + void fromQLatin1Char() const { fromCharacter(QLatin1Char('\xE4'), 1); } + void fromQCharSpecialCharacter() const { + fromCharacter(QChar::ReplacementCharacter, 1); + if (QTest::currentTestFailed()) + return; + fromCharacter(QChar::LastValidCodePoint, 1); + } + void fromChar16TStar() const { fromLiteral(u"Hello, World!"); } void fromWCharTStar() const { ONLY_WIN(fromLiteral(L"Hello, World!")); } @@ -393,6 +416,8 @@ private: template <typename Char> void fromLiteral(const Char *arg) const; template <typename Char> + void fromCharacter(Char arg, qsizetype expectedSize) const; + template <typename Char> void fromRange() const; template <typename Char, typename Container> void fromContainer() const; @@ -673,6 +698,44 @@ void tst_QAnyStringView::fromLiteral(const Char *arg) const conversion_tests(arg); } +template<typename Char> +void tst_QAnyStringView::fromCharacter(Char arg, qsizetype expectedSize) const +{ + // Need to re-create a new QASV(arg) each time, QASV(Char).data() dangles + // after the end of the Full Expression: + + static_assert(noexcept(QAnyStringView(arg)), + "If this fails, we may be creating a temporary QString/QByteArray"); + + QCOMPARE(QAnyStringView(arg).size(), expectedSize); + + // QCOMPARE(QAnyStringView(arg), arg); // not all pairs compile, so do it manually: + + // Check implicit conversion: + const QChar chars[] = { + [](QAnyStringView v) { return v.front(); }(arg), + [](QAnyStringView v) { return v.back(); }(arg), + }; + + switch (expectedSize) { + case 1: + if constexpr (std::is_same_v<Char, signed char>) // QChar doesn't have a ctor for this + QCOMPARE(chars[0], QChar(uchar(arg))); + else + QCOMPARE(chars[0], QChar(arg)); + break; + case 2: + QCOMPARE_EQ(QAnyStringView(arg), QStringView::fromArray(chars)); + if constexpr (std::is_convertible_v<Char, char32_t>) + QCOMPARE_EQ(QAnyStringView(arg), QStringView(QChar::fromUcs4(arg))); + break; + default: + QFAIL("Don't know how to compare this type to QAnyStringView"); + } + + // conversion_tests() would produce dangling references +} + template <typename Char> void tst_QAnyStringView::fromRange() const { diff --git a/tests/auto/corelib/text/qbytearrayview/CMakeLists.txt b/tests/auto/corelib/text/qbytearrayview/CMakeLists.txt index c78a81c7bd..8922ae2267 100644 --- a/tests/auto/corelib/text/qbytearrayview/CMakeLists.txt +++ b/tests/auto/corelib/text/qbytearrayview/CMakeLists.txt @@ -15,3 +15,10 @@ qt_internal_add_test(tst_qbytearrayview SOURCES tst_qbytearrayview.cpp ) + +if(QT_FEATURE_sanitize_undefined) + qt_internal_extend_target(tst_qbytearrayview + DEFINES + QT_SANITIZE_UNDEFINED # GCC (in)famously doesn't provide a predefined macro for this + ) +endif() diff --git a/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp b/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp index 894f0430dd..702e1840da 100644 --- a/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp +++ b/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp @@ -262,10 +262,9 @@ void tst_QByteArrayView::constExpr() const static_assert(!bv2.empty()); static_assert(bv2.size() == 5); } -#if !defined(Q_CC_GNU) || defined(Q_CC_CLANG) +#if !defined(Q_CC_GNU_ONLY) || !defined(QT_SANITIZE_UNDEFINED) // Below checks are disabled because of a compilation issue with GCC and // -fsanitize=undefined. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71962. - // Note: Q_CC_GNU is also defined for Clang, so we need to check that too. { static constexpr char hello[] = "Hello"; constexpr QByteArrayView bv(hello); @@ -307,6 +306,9 @@ void tst_QByteArrayView::constExpr() const static_assert(bv.back() == 'o'); static_assert(bv.last() == 'o'); + constexpr auto bv2 = QByteArrayView::fromArray(hello); + QCOMPARE_EQ(bv, bv2); + constexpr std::string_view sv = bv; static_assert(bv.size() == sv.size()); #ifdef AMBIGUOUS_CALL // QTBUG-108805 @@ -416,7 +418,7 @@ void tst_QByteArrayView::fromArray() const { static constexpr char hello[] = "Hello\0abc\0\0."; - constexpr QByteArrayView bv = QByteArrayView::fromArray(hello); + const QByteArrayView bv = QByteArrayView::fromArray(hello); QCOMPARE(bv.size(), 13); QVERIFY(!bv.empty()); QVERIFY(!bv.isEmpty()); diff --git a/tests/auto/corelib/text/qlatin1stringmatcher/tst_qlatin1stringmatcher.cpp b/tests/auto/corelib/text/qlatin1stringmatcher/tst_qlatin1stringmatcher.cpp index 82e12bdfca..ba098fd23c 100644 --- a/tests/auto/corelib/text/qlatin1stringmatcher/tst_qlatin1stringmatcher.cpp +++ b/tests/auto/corelib/text/qlatin1stringmatcher/tst_qlatin1stringmatcher.cpp @@ -25,6 +25,7 @@ class tst_QLatin1StringMatcher : public QObject private slots: void overloads(); void staticOverloads(); + void staticOverloads_QStringViewHaystack(); void interface(); void indexIn(); void haystacksWithMoreThan4GiBWork(); @@ -44,6 +45,12 @@ void tst_QLatin1StringMatcher::overloads() QCOMPARE(m.indexIn("Hellohello"_L1), 5); QCOMPARE(m.indexIn("helloHello"_L1), 0); QCOMPARE(m.indexIn("helloHello"_L1, 1), -1); + + QCOMPARE(m.indexIn(u"hello"), 0); + QCOMPARE(m.indexIn(u"Hello"), -1); + QCOMPARE(m.indexIn(u"Hellohello"), 5); + QCOMPARE(m.indexIn(u"helloHello"), 0); + QCOMPARE(m.indexIn(u"helloHello", 1), -1); } { QLatin1StringMatcher m("Hello"_L1, Qt::CaseSensitive); @@ -53,6 +60,12 @@ void tst_QLatin1StringMatcher::overloads() QCOMPARE(m.indexIn("Hellohello"_L1), 0); QCOMPARE(m.indexIn("helloHello"_L1), 5); QCOMPARE(m.indexIn("helloHello"_L1, 6), -1); + + QCOMPARE(m.indexIn(u"hello"), -1); + QCOMPARE(m.indexIn(u"Hello"), 0); + QCOMPARE(m.indexIn(u"Hellohello"), 0); + QCOMPARE(m.indexIn(u"helloHello"), 5); + QCOMPARE(m.indexIn(u"helloHello", 6), -1); } { QLatin1StringMatcher m("hello"_L1, Qt::CaseInsensitive); @@ -63,6 +76,13 @@ void tst_QLatin1StringMatcher::overloads() QCOMPARE(m.indexIn("helloHello"_L1), 0); QCOMPARE(m.indexIn("helloHello"_L1, 1), 5); QCOMPARE(m.indexIn("helloHello"_L1, 6), -1); + + QCOMPARE(m.indexIn(u"hello"), 0); + QCOMPARE(m.indexIn(u"Hello"), 0); + QCOMPARE(m.indexIn(u"Hellohello"), 0); + QCOMPARE(m.indexIn(u"helloHello"), 0); + QCOMPARE(m.indexIn(u"helloHello", 1), 5); + QCOMPARE(m.indexIn(u"helloHello", 6), -1); } { QLatin1StringMatcher m("Hello"_L1, Qt::CaseInsensitive); @@ -73,6 +93,13 @@ void tst_QLatin1StringMatcher::overloads() QCOMPARE(m.indexIn("helloHello"_L1), 0); QCOMPARE(m.indexIn("helloHello"_L1, 1), 5); QCOMPARE(m.indexIn("helloHello"_L1, 6), -1); + + QCOMPARE(m.indexIn(u"hello"), 0); + QCOMPARE(m.indexIn(u"Hello"), 0); + QCOMPARE(m.indexIn(u"Hellohello"), 0); + QCOMPARE(m.indexIn(u"helloHello"), 0); + QCOMPARE(m.indexIn(u"helloHello", 1), 5); + QCOMPARE(m.indexIn(u"helloHello", 6), -1); } { QLatin1StringMatcher m(hello, Qt::CaseSensitive); @@ -81,6 +108,11 @@ void tst_QLatin1StringMatcher::overloads() QCOMPARE(m.indexIn(hello, 1), -1); QCOMPARE(m.indexIn(hello2, 1), hello.size()); QCOMPARE(m.indexIn(hello2, 6), -1); + + QCOMPARE(m.indexIn(QString::fromLatin1(hello)), 0); + QCOMPARE(m.indexIn(QString::fromLatin1(hello), 1), -1); + QCOMPARE(m.indexIn(QString::fromLatin1(hello2), 1), hello.size()); + QCOMPARE(m.indexIn(QString::fromLatin1(hello2), 6), -1); } } @@ -206,6 +238,129 @@ void tst_QLatin1StringMatcher::staticOverloads() #endif } +void tst_QLatin1StringMatcher::staticOverloads_QStringViewHaystack() +{ +#ifdef QT_STATIC_BOYER_MOORE_NOT_SUPPORTED + QSKIP("Test is only valid on an OS that supports static latin1 string matcher"); +#else + constexpr QStringView hello = u"hello"; + QString hello2B = QStringView(hello).toString().repeated(2); + hello2B += QStringView(u"🍉"); + QStringView hello2(hello2B); + { + static constexpr auto m = qMakeStaticCaseSensitiveLatin1StringMatcher("hel"); + QCOMPARE(m.indexIn(QStringView(u"hello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"Hello🍉")), -1); + QCOMPARE(m.indexIn(QStringView(u"Hellohello🍉")), 5); + QCOMPARE(m.indexIn(QStringView(u"helloHello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"he🍉")), -1); + QCOMPARE(m.indexIn(QStringView(u"hel🍉")), 0); + QCOMPARE(m.indexIn(hello), 0); + QCOMPARE(m.indexIn(hello, 1), -1); // from is 1 + QCOMPARE(m.indexIn(hello2, 2), hello.size()); // from is 2 + QCOMPARE(m.indexIn(hello2, 3), hello.size()); // from is 3 + QCOMPARE(m.indexIn(hello2, 6), -1); // from is 6 + static_assert(m.indexIn(QStringView(u"hello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"Hello🍉")) == -1); + static_assert(m.indexIn(QStringView(u"Hellohello🍉")) == 5); + static_assert(m.indexIn(QStringView(u"helloHello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"he🍉")) == -1); + static_assert(m.indexIn(QStringView(u"hel🍉")) == 0); + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 2) == 5); // from is 2 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 3) == 5); // from is 3 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 6) == -1); // from is 6 + } + { + static constexpr auto m = qMakeStaticCaseSensitiveLatin1StringMatcher("Hel"); + QCOMPARE(m.indexIn(QStringView(u"hello🍉")), -1); + QCOMPARE(m.indexIn(QStringView(u"Hello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"Hellohello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"helloHello🍉")), 5); + QCOMPARE(m.indexIn(QStringView(u"helloHello🍉"), 6), -1); + QCOMPARE(m.indexIn(QStringView(u"He🍉")), -1); + QCOMPARE(m.indexIn(QStringView(u"Hel🍉")), 0); + QCOMPARE(m.indexIn(hello), -1); + QCOMPARE(m.indexIn(hello2, 2), -1); // from is 2 + QCOMPARE(m.indexIn(hello2, 6), -1); // from is 6 + static_assert(m.indexIn(QStringView(u"hello🍉")) == -1); + static_assert(m.indexIn(QStringView(u"Hello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"Hellohello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"helloHello🍉")) == 5); + static_assert(m.indexIn(QStringView(u"helloHello🍉"), 6) == -1); + static_assert(m.indexIn(QStringView(u"He🍉")) == -1); + static_assert(m.indexIn(QStringView(u"Hel🍉")) == 0); + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 2) == -1); // from is 2 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 6) == -1); // from is 6 + } + { + static constexpr auto m = qMakeStaticCaseInsensitiveLatin1StringMatcher("hel"); + QCOMPARE(m.indexIn(QStringView(u"hello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"Hello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"Hellohello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"helloHello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"he🍉")), -1); + QCOMPARE(m.indexIn(QStringView(u"hel🍉")), 0); + QCOMPARE(m.indexIn(hello), 0); + QCOMPARE(m.indexIn(hello, 1), -1); + QCOMPARE(m.indexIn(hello2, 2), hello.size()); // from is 2 + QCOMPARE(m.indexIn(hello2, 3), hello.size()); // from is 3 + QCOMPARE(m.indexIn(hello2, 6), -1); // from is 6 + static_assert(m.indexIn(QStringView(u"hello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"Hello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"Hellohello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"helloHello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"he🍉")) == -1); + static_assert(m.indexIn(QStringView(u"hel🍉")) == 0); + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 2) == 5); // from is 2 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 3) == 5); // from is 3 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 6) == -1); // from is 6 + } + { + static constexpr auto m = qMakeStaticCaseInsensitiveLatin1StringMatcher("Hel"); + QCOMPARE(m.indexIn(QStringView(u"hello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"Hello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"Hellohello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"helloHello🍉")), 0); + QCOMPARE(m.indexIn(QStringView(u"he🍉")), -1); + QCOMPARE(m.indexIn(QStringView(u"hel🍉")), 0); + QCOMPARE(m.indexIn(hello), 0); + QCOMPARE(m.indexIn(hello, 1), -1); + QCOMPARE(m.indexIn(hello2, 2), hello.size()); // from is 2 + QCOMPARE(m.indexIn(hello2, 3), hello.size()); // from is 3 + QCOMPARE(m.indexIn(hello2, 6), -1); // from is 6 + static_assert(m.indexIn(QStringView(u"hello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"Hello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"Hellohello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"helloHello🍉")) == 0); + static_assert(m.indexIn(QStringView(u"he🍉")) == -1); + static_assert(m.indexIn(QStringView(u"hel🍉")) == 0); + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 2) == 5); // from is 2 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 3) == 5); // from is 3 + static_assert(m.indexIn(QStringView(u"hellohello🍉"), 6) == -1); // from is 6 + } + { + static constexpr auto m = qMakeStaticCaseInsensitiveLatin1StringMatcher("b\xF8"); + QCOMPARE(m.indexIn(QStringView(u"B\xD8")), 0); + QCOMPARE(m.indexIn(QStringView(u"B\xF8")), 0); + QCOMPARE(m.indexIn(QStringView(u"b\xD8")), 0); + QCOMPARE(m.indexIn(QStringView(u"b\xF8")), 0); + QCOMPARE(m.indexIn(QStringView(u"b\xF8lle")), 0); + QCOMPARE(m.indexIn(QStringView(u"m\xF8lle")), -1); + QCOMPARE(m.indexIn(QStringView(u"Si b\xF8")), 3); + } + { + static constexpr auto m = qMakeStaticCaseSensitiveLatin1StringMatcher("b\xF8"); + QCOMPARE(m.indexIn(QStringView(u"B\xD8")), -1); + QCOMPARE(m.indexIn(QStringView(u"B\xF8")), -1); + QCOMPARE(m.indexIn(QStringView(u"b\xD8")), -1); + QCOMPARE(m.indexIn(QStringView(u"b\xF8")), 0); + QCOMPARE(m.indexIn(QStringView(u"b\xF8lle")), 0); + QCOMPARE(m.indexIn(QStringView(u"m\xF8lle")), -1); + QCOMPARE(m.indexIn(QStringView(u"Si b\xF8")), 3); + } +#endif +} + void tst_QLatin1StringMatcher::interface() { QLatin1StringView needle = "abc123"_L1; @@ -387,25 +542,35 @@ void tst_QLatin1StringMatcher::haystacksWithMoreThan4GiBWork() QCOMPARE(large.size(), BaseSize + needle.size()); qDebug("created dataset in %lld ms", timer.elapsed()); - using MaybeThread = std::thread; - - // - // WHEN: trying to match an occurrence past the 4GiB mark - // - - qsizetype dynamicResult; - - auto t = MaybeThread{ [&] { - QLatin1StringMatcher m(QLatin1StringView(needle), Qt::CaseSensitive); - dynamicResult = m.indexIn(QLatin1StringView(large)); - } }; - t.join(); + { + // + // WHEN: trying to match an occurrence past the 4GiB mark + // + qsizetype dynamicResult; + auto t = std::thread{ [&] { + QLatin1StringMatcher m(QLatin1StringView(needle), Qt::CaseSensitive); + dynamicResult = m.indexIn(QLatin1StringView(large)); + } }; + t.join(); + + // + // THEN: the result index is not truncated + // + + QCOMPARE(dynamicResult, qsizetype(BaseSize)); + } - // - // THEN: the result index is not trucated - // + { + qsizetype dynamicResult; + auto t = std::thread{ [&] { + QLatin1StringMatcher m(QLatin1StringView(needle), Qt::CaseSensitive); + dynamicResult = m.indexIn(QStringView(QString::fromLatin1(large))); + } }; + t.join(); + + QCOMPARE(dynamicResult, qsizetype(BaseSize)); + } - QCOMPARE(dynamicResult, qsizetype(BaseSize)); #else QSKIP("This test is 64-bit only."); #endif diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index c9668cd4d4..eb2d73b9b2 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -4178,6 +4178,8 @@ void tst_QLocale::mySystemLocale() qDebug("\n\t%s", qPrintable(QLocale::system().uiLanguages().join(u"\n\t"))); }); QCOMPARE(QLocale::system().uiLanguages(), uiLanguages); + QCOMPARE(QLocale::system().uiLanguages(QLocale::TagSeparator::Underscore), + uiLanguages.replaceInStrings(u"-", u"_")); reporter.dismiss(); } diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index d56a9ebd20..92ea5b013d 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -6647,9 +6647,41 @@ void tst_QString::arg() // number overloads QCOMPARE( s4.arg(0), QLatin1String("[0]") ); QCOMPARE( s4.arg(-1), QLatin1String("[-1]") ); + QCOMPARE( s4.arg(0U), QLatin1String("[0]")); + QCOMPARE( s4.arg(qint8(-128)), QLatin1String("[-128]")); // signed char + QCOMPARE( s4.arg(quint8(255)), QLatin1String("[255]")); // unsigned char + QCOMPARE( s4.arg(short(-4200)), QLatin1String("[-4200]")); + QCOMPARE( s4.arg(ushort(42000)), QLatin1String("[42000]")); QCOMPARE( s4.arg(4294967295UL), QLatin1String("[4294967295]") ); // ULONG_MAX 32 QCOMPARE( s4.arg(Q_INT64_C(9223372036854775807)), // LLONG_MAX QLatin1String("[9223372036854775807]") ); + QCOMPARE( s4.arg(Q_UINT64_C(9223372036854775808)), // LLONG_MAX + 1 + QLatin1String("[9223372036854775808]") ); + + // FP overloads + QCOMPARE(s4.arg(2.25), QLatin1String("[2.25]")); + QCOMPARE(s4.arg(3.75f), QLatin1String("[3.75]")); +#if !QFLOAT16_IS_NATIVE // QTBUG-126055 + QCOMPARE(s4.arg(qfloat16{4.125f}), QLatin1String("[4.125]")); +#endif + + // char-ish overloads + QCOMPARE(s4.arg('\xE4'), QStringView(u"[ä]")); + QEXPECT_FAIL("", "QTBUG-125588", Continue); + QCOMPARE(s4.arg(u'ø'), QStringView(u"[ø]")); +#ifdef Q_OS_WIN + QCOMPARE(QLatin1String("[%1]").arg(L'ø'), QStringView(u"[ø]")); +#endif + QEXPECT_FAIL("", "QTBUG-126054", Continue); + QCOMPARE(s4.arg(L'ø'), QStringView(u"[ø]")); +#ifndef __cpp_char8_t +#ifndef QT_NO_CAST_FROM_ASCII + QCOMPARE(QLatin1String("[%1]").arg(u8'a'), QLatin1String("[a]")); +#endif +#else + QEXPECT_FAIL("", "QTBUG-126053", Continue); +#endif + QCOMPARE(s4.arg(u8'a'), QLatin1String("[a]")); QTest::ignoreMessage(QtWarningMsg, "QString::arg: Argument missing: , foo"); QCOMPARE(QString().arg(foo), QString()); diff --git a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp index 35a734cf02..06ef1aede0 100644 --- a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -545,6 +545,93 @@ private Q_SLOTS: void endsWith_QLatin1String_QLatin1Char() { endsWith_impl<QLatin1String, QLatin1Char>(); } private: + void arg1_data(bool argHasVariableLength = true) const; + template <typename Format, typename Argument> void arg1_impl() const; + +private Q_SLOTS: + // let Formats = {QString, QStringView, QLatin1String} + // let Arguments = Formats ∪ {QByteArray, const char*, const char8_t*. const char16_t*, std::u16string, char, QChar, QLatin1Char, char16_t} + // test Formats × Arguments: + void arg1_QString_QString_data() { arg1_data(); } + void arg1_QString_QString() { arg1_impl<QString, QString>(); } + void arg1_QString_QStringView_data() { arg1_data(); } + void arg1_QString_QStringView() { arg1_impl<QString, QStringView>(); } + void arg1_QString_QLatin1StringView_data() { arg1_data(); } + void arg1_QString_QLatin1StringView() { arg1_impl<QString, QLatin1StringView>(); } + void arg1_QString_QByteArray_data() { arg1_data(); } + void arg1_QString_QByteArray() { arg1_impl<QString, QByteArray>(); } + void arg1_QString_const_char_star_data() { arg1_data(); } + void arg1_QString_const_char_star() { arg1_impl<QString, const char*>(); } + void arg1_QString_const_char8_t_star_data() { arg1_data(); } + void arg1_QString_const_char8_t_star() { IF_CHAR8T((arg1_impl<QString, const char8_t*>())); } + void arg1_QString_const_char16_t_star_data() { arg1_data(); } + void arg1_QString_const_char16_t_star() { arg1_impl<QString, const char16_t*>(); } + void arg1_QString_stdu16string_data() { arg1_data(); } + void arg1_QString_stdu16string() { arg1_impl<QString, std::u16string>(); } + void arg1_QString_char_data() { arg1_data(false); } + void arg1_QString_char() { arg1_impl<QString, char>(); } + void arg1_QString_QChar_data() { arg1_data(false); } + void arg1_QString_QChar() { arg1_impl<QString, QChar>(); } + void arg1_QString_QLatin1Char_data() { arg1_data(false); } + void arg1_QString_QLatin1Char() { arg1_impl<QString, QLatin1Char>(); } + void arg1_QString_char16_t_data() { arg1_data(false); } + void arg1_QString_char16_t() { + QEXPECT_FAIL("%1/a", "QTBUG-125588", Continue); + QEXPECT_FAIL("%1/ä", "QTBUG-125588", Continue); + arg1_impl<QString, char16_t>(); + } + + void arg1_QStringView_QString_data() { arg1_data(); } + void arg1_QStringView_QString() { arg1_impl<QStringView, QString>(); } + void arg1_QStringView_QStringView_data() { arg1_data(); } + void arg1_QStringView_QStringView() { arg1_impl<QStringView, QStringView>(); } + void arg1_QStringView_QLatin1StringView_data() { arg1_data(); } + void arg1_QStringView_QLatin1StringView() { arg1_impl<QStringView, QLatin1StringView>(); } + void arg1_QStringView_QByteArray_data() { arg1_data(); } + void arg1_QStringView_QByteArray() { arg1_impl<QStringView, QByteArray>(); } + void arg1_QStringView_const_char_star_data() { arg1_data(); } + void arg1_QStringView_const_char_star() { arg1_impl<QStringView, const char*>(); } + void arg1_QStringView_const_char8_t_star_data() { arg1_data(); } + void arg1_QStringView_const_char8_t_star() { IF_CHAR8T((arg1_impl<QStringView, const char8_t*>())); } + void arg1_QStringView_const_char16_t_star_data() { arg1_data(); } + void arg1_QStringView_const_char16_t_star() { arg1_impl<QStringView, const char16_t*>(); } + void arg1_QStringView_stdu16string_data() { arg1_data(); } + void arg1_QStringView_stdu16string() { arg1_impl<QStringView, std::u16string>(); } + void arg1_QStringView_char_data() { arg1_data(false); } + void arg1_QStringView_char() { arg1_impl<QStringView, char>(); } + void arg1_QStringView_QChar_data() { arg1_data(false); } + void arg1_QStringView_QChar() { arg1_impl<QStringView, QChar>(); } + void arg1_QStringView_QLatin1Char_data() { arg1_data(false); } + void arg1_QStringView_QLatin1Char() { arg1_impl<QStringView, QLatin1Char>(); } + void arg1_QStringView_char16_t_data() { arg1_data(false); } + void arg1_QStringView_char16_t() { arg1_impl<QStringView, char16_t>(); } + + void arg1_QLatin1StringView_QString_data() { arg1_data(); } + void arg1_QLatin1StringView_QString() { arg1_impl<QLatin1StringView, QString>(); } + void arg1_QLatin1StringView_QStringView_data() { arg1_data(); } + void arg1_QLatin1StringView_QStringView() { arg1_impl<QLatin1StringView, QStringView>(); } + void arg1_QLatin1StringView_QLatin1StringView_data() { arg1_data(); } + void arg1_QLatin1StringView_QLatin1StringView() { arg1_impl<QLatin1StringView, QLatin1StringView>(); } + void arg1_QLatin1StringView_QByteArray_data() { arg1_data(); } + void arg1_QLatin1StringView_QByteArray() { arg1_impl<QLatin1StringView, QByteArray>(); } + void arg1_QLatin1StringView_const_char_star_data() { arg1_data(); } + void arg1_QLatin1StringView_const_char_star() { arg1_impl<QLatin1StringView, const char*>(); } + void arg1_QLatin1StringView_const_char8_t_star_data() { arg1_data(); } + void arg1_QLatin1StringView_const_char8_t_star() { IF_CHAR8T((arg1_impl<QLatin1StringView, const char8_t*>())); } + void arg1_QLatin1StringView_const_char16_t_star_data() { arg1_data(); } + void arg1_QLatin1StringView_const_char16_t_star() { arg1_impl<QLatin1StringView, const char16_t*>(); } + void arg1_QLatin1StringView_stdu16string_data() { arg1_data(); } + void arg1_QLatin1StringView_stdu16string() { arg1_impl<QLatin1StringView, std::u16string>(); } + void arg1_QLatin1StringView_char_data() { arg1_data(false); } + void arg1_QLatin1StringView_char() { arg1_impl<QLatin1StringView, char>(); } + void arg1_QLatin1StringView_QChar_data() { arg1_data(false); } + void arg1_QLatin1StringView_QChar() { arg1_impl<QLatin1StringView, QChar>(); } + void arg1_QLatin1StringView_QLatin1Char_data() { arg1_data(false); } + void arg1_QLatin1StringView_QLatin1Char() { arg1_impl<QLatin1StringView, QLatin1Char>(); } + void arg1_QLatin1StringView_char16_t_data() { arg1_data(false); } + void arg1_QLatin1StringView_char16_t() { arg1_impl<QLatin1StringView, char16_t>(); } + +private: void split_data(bool rhsHasVariableLength = true); template <typename Haystack, typename Needle> void split_impl() const; @@ -1112,6 +1199,10 @@ auto overload_s_v(QStringView s) { return s; } auto overload_sr_v(QString &&s) { return std::move(s); } auto overload_sr_v(QStringView s) { return s; } +Q_WEAK_OVERLOAD +auto overload_s_bav(const QString &s) { return s; } +auto overload_s_bav(QByteArrayView s) { return s; } + } // unnamed namespace template<typename T> @@ -1174,6 +1265,15 @@ void tst_QStringApiSymmetry::overload() overload_sr_v(CT()); } } + + if constexpr (std::is_convertible_v<T, QString> || std::is_convertible_v<T, QByteArrayView>) { + overload_s_bav(t); + overload_s_bav(ct); + if constexpr (!std::is_array_v<T>) { + overload_s_bav(T()); + overload_s_bav(CT()); + } + } QT_WARNING_POP } @@ -1290,6 +1390,7 @@ template <class Str> Str make(QStringView sf, QLatin1String l1, const QByteArra [[maybe_unused]] const QByteArray &u8) \ /*end*/ MAKE(QChar) { return sv.isEmpty() ? QChar() : sv.at(0); } +MAKE(char) { return sv.isEmpty() ? char() : char(sv.at(0).unicode()); } MAKE(char16_t) { return sv.isEmpty() ? char16_t() : char16_t{sv.at(0).unicode()}; } MAKE(QLatin1Char) { return l1.isEmpty() ? QLatin1Char('\0') : l1.at(0); } MAKE(QString) { return sv.toString(); } @@ -1298,6 +1399,9 @@ MAKE(QLatin1String) { return l1; } MAKE(QByteArray) { return u8; } MAKE(QByteArrayView) { return u8; } MAKE(const char *) { return u8.data(); } +#ifdef __cpp_char8_t +MAKE(const char8_t *) { return q_has_char8_t::QUtf8StringView(u8).data(); } +#endif MAKE(const char16_t *) { return sv.utf16(); } // assumes `sv` doesn't represent a substring MAKE(std::u16string) { return sv.toString().toStdU16String(); } MAKE(QUtf8StringView) { return u8; } @@ -1926,6 +2030,69 @@ void tst_QStringApiSymmetry::endsWith_impl() const QCOMPARE_EQ(haystack.endsWith(needle, Qt::CaseInsensitive), resultCIS); } +void tst_QStringApiSymmetry::arg1_data(bool argHasVariableLength) const +{ + QTest::addColumn<QString>("formatU16"); + QTest::addColumn<QString>("argumentU16"); + QTest::addColumn<QString>("expected"); + + const char *null = nullptr; + const char *empty = ""; + + auto row = [](const char *fmt, const char *arg, const char *res) { + auto protect = [](const char *s) { return s ? *s ? s : "<empty>" : "<null>"; }; + QTest::addRow("%s/%s", protect(fmt), protect(arg)) + << QString::fromUtf8(fmt) << QString::fromUtf8(arg) << QString::fromUtf8(res); + }; + + for (auto fmt : {null, empty}) { + if (argHasVariableLength) { + for (auto arg : {null, empty}) + row(fmt, arg, ""); + } + for (auto arg : {"a", "ä"}) + row(fmt, arg, ""); + if (argHasVariableLength) { + for (auto arg : {"äá", "Øåß"}) + row(fmt, arg, ""); + } + } + + for (auto fmt : {"%1"}) { + if (argHasVariableLength) { + for (auto arg : {null, empty}) + row(fmt, arg, arg); + } + for (auto arg : {"a", "ä"}) + row(fmt, arg, arg); + if (argHasVariableLength) { + for (auto arg : {"äá", "Øåß"}) + row(fmt, arg, arg); + } + } +} + +template <typename Format, typename Argument> +void tst_QStringApiSymmetry::arg1_impl() const +{ + QFETCH(const QString, formatU16); + QFETCH(const QString, argumentU16); + QFETCH(const QString, expected); + + const auto formatL1Pinned = formatU16.toLatin1(); + const auto formatL1 = QLatin1StringView{formatL1Pinned}; + const auto formatU8 = formatU16.toUtf8(); + + const auto argumentL1Pinned = argumentU16.toLatin1(); + const auto argumentL1= QLatin1StringView{argumentL1Pinned}; + const auto argumentU8 = argumentU16.toUtf8(); + + const auto format = make<Format>(formatU16, formatL1, formatU8); + const auto argument = make<Argument>(argumentU16, argumentL1, argumentU8); + + QCOMPARE(format.arg(argument), expected); +} + void tst_QStringApiSymmetry::split_data(bool rhsHasVariableLength) { QTest::addColumn<QStringView>("haystackU16"); diff --git a/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/stringbuilder.cpp b/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/stringbuilder.cpp index 59362d010a..7931363b4b 100644 --- a/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/stringbuilder.cpp +++ b/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/stringbuilder.cpp @@ -377,6 +377,8 @@ void runScenario() char chararray[3] = { 'H', 'i', '\0' }; const char constchararray[3] = { 'H', 'i', '\0' }; char achar = 'a'; + char embedded_NULs[16] = { 'H', 'i' }; + const char const_embedded_NULs[16] = { 'H', 'i' }; CHECK(P, bytearray, bytearray); CHECK(P, QByteArray(bytearray), bytearray); @@ -400,6 +402,33 @@ void runScenario() CHECK(P, bytearray, achar); CHECK(Q, bytearray, constchararray); CHECK(Q, bytearray, achar); + CHECK(Q, bytearray, embedded_NULs); + CHECK(Q, bytearray, const_embedded_NULs); + + CHECK(Q, baview, bytearray); + CHECK(Q, baview, charstar); + CHECK(Q, baview, chararray); + CHECK(Q, baview, constchararray); + CHECK(Q, baview, achar); + CHECK(Q, baview, embedded_NULs); + CHECK(Q, baview, const_embedded_NULs); + + // Check QString/QByteArray consistency when appending const char[] with embedded NULs: + { + const QByteArray ba = baview Q embedded_NULs; + QEXPECT_FAIL("", "QTBUG-117321", Continue); + QCOMPARE(ba.size(), baview.size() + q20::ssize(embedded_NULs) - 1); + +#ifndef QT_NO_CAST_FROM_ASCII + const auto l1s = QLatin1StringView{baview}; // l1string != baview + + const QString s = l1s Q embedded_NULs; + QCOMPARE(s.size(), l1s.size() + q20::ssize(embedded_NULs) - 1); + + QEXPECT_FAIL("", "QTBUG-117321", Continue); + QCOMPARE(s, ba); +#endif + } //CHECK(Q, charstar, charstar); // BUILTIN <-> BUILTIN cat't be overloaded //CHECK(Q, charstar, chararray); diff --git a/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/tst_qstringbuilder1.cpp b/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/tst_qstringbuilder1.cpp index 394372c398..e8365f500b 100644 --- a/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/tst_qstringbuilder1.cpp +++ b/tests/auto/corelib/text/qstringbuilder/qstringbuilder1/tst_qstringbuilder1.cpp @@ -15,6 +15,8 @@ #include <QtCore/QStringBuilder> #include <QtTest/QTest> +#include <QtCore/q20iterator.h> + #define LITERAL "some literal" namespace { diff --git a/tests/auto/corelib/text/qstringbuilder/qstringbuilder2/tst_qstringbuilder2.cpp b/tests/auto/corelib/text/qstringbuilder/qstringbuilder2/tst_qstringbuilder2.cpp index dc590304f5..1aaee114a3 100644 --- a/tests/auto/corelib/text/qstringbuilder/qstringbuilder2/tst_qstringbuilder2.cpp +++ b/tests/auto/corelib/text/qstringbuilder/qstringbuilder2/tst_qstringbuilder2.cpp @@ -16,6 +16,8 @@ #include <QtCore/QStringBuilder> #include <QtTest/QTest> +#include <QtCore/q20iterator.h> + #define LITERAL "some literal" namespace { diff --git a/tests/auto/corelib/text/qstringbuilder/qstringbuilder3/tst_qstringbuilder3.cpp b/tests/auto/corelib/text/qstringbuilder/qstringbuilder3/tst_qstringbuilder3.cpp index 3222b52713..aaa14a5d1a 100644 --- a/tests/auto/corelib/text/qstringbuilder/qstringbuilder3/tst_qstringbuilder3.cpp +++ b/tests/auto/corelib/text/qstringbuilder/qstringbuilder3/tst_qstringbuilder3.cpp @@ -15,6 +15,8 @@ #include <QtCore/QStringBuilder> #include <QtTest/QTest> +#include <QtCore/q20iterator.h> + #define LITERAL "some literal" namespace { diff --git a/tests/auto/corelib/text/qstringbuilder/qstringbuilder4/tst_qstringbuilder4.cpp b/tests/auto/corelib/text/qstringbuilder/qstringbuilder4/tst_qstringbuilder4.cpp index 442c5275d2..319708fd4b 100644 --- a/tests/auto/corelib/text/qstringbuilder/qstringbuilder4/tst_qstringbuilder4.cpp +++ b/tests/auto/corelib/text/qstringbuilder/qstringbuilder4/tst_qstringbuilder4.cpp @@ -16,6 +16,8 @@ #include <QtCore/QStringBuilder> #include <QtTest/QTest> +#include <QtCore/q20iterator.h> + #define LITERAL "some literal" namespace { diff --git a/tests/auto/corelib/text/qstringconverter/tst_qstringconverter.cpp b/tests/auto/corelib/text/qstringconverter/tst_qstringconverter.cpp index ed3f91ac94..342c343a42 100644 --- a/tests/auto/corelib/text/qstringconverter/tst_qstringconverter.cpp +++ b/tests/auto/corelib/text/qstringconverter/tst_qstringconverter.cpp @@ -257,7 +257,7 @@ void tst_QStringConverter::invalidConverter() QVERIFY(!encoder.hasError()); char buffer[100]; char *position = encoder.appendToBuffer(buffer, u"Even more"); - QCOMPARE(position, buffer); + QCOMPARE(position - buffer, 0); QVERIFY(encoder.hasError()); } @@ -283,7 +283,7 @@ void tst_QStringConverter::invalidConverter() QVERIFY(!decoder.hasError()); char16_t buffer[100]; char16_t *position = decoder.appendToBuffer(buffer, "Even more"); - QCOMPARE(position, buffer); + QCOMPARE(position - buffer, 0); QVERIFY(decoder.hasError()); } } diff --git a/tests/auto/corelib/text/qstringview/CMakeLists.txt b/tests/auto/corelib/text/qstringview/CMakeLists.txt index ba5f540838..b541cdd0ed 100644 --- a/tests/auto/corelib/text/qstringview/CMakeLists.txt +++ b/tests/auto/corelib/text/qstringview/CMakeLists.txt @@ -18,5 +18,12 @@ qt_internal_add_test(tst_qstringview Qt::CorePrivate ) +if(QT_FEATURE_sanitize_undefined) + qt_internal_extend_target(tst_qstringview + DEFINES + QT_SANITIZE_UNDEFINED # GCC (in)famously doesn't provide a predefined macro for this + ) +endif() + ## Scopes: ##################################################################### diff --git a/tests/auto/corelib/text/qstringview/tst_qstringview.cpp b/tests/auto/corelib/text/qstringview/tst_qstringview.cpp index df3ef94371..3f87ffc9fc 100644 --- a/tests/auto/corelib/text/qstringview/tst_qstringview.cpp +++ b/tests/auto/corelib/text/qstringview/tst_qstringview.cpp @@ -359,6 +359,30 @@ void tst_QStringView::constExpr() const static_assert(sv3.isEmpty()); static_assert(sv3.size() == 0); } +#if !defined(Q_CC_GNU_ONLY) || !defined(QT_SANITIZE_UNDEFINED) + // Below checks are disabled because of a compilation issue with GCC and + // -fsanitize=undefined. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71962. + { + static constexpr char16_t hello[] = u"Hello"; + constexpr QStringView sv(hello); + static_assert(sv.size() == 5); + static_assert(!sv.empty()); + static_assert(!sv.isEmpty()); + static_assert(!sv.isNull()); + static_assert(*sv.utf16() == 'H'); + static_assert(sv[0] == QLatin1Char('H')); + static_assert(sv.at(0) == QLatin1Char('H')); + static_assert(sv.front() == QLatin1Char('H')); + static_assert(sv.first() == QLatin1Char('H')); + static_assert(sv[4] == QLatin1Char('o')); + static_assert(sv.at(4) == QLatin1Char('o')); + static_assert(sv.back() == QLatin1Char('o')); + static_assert(sv.last() == QLatin1Char('o')); + + constexpr auto sv2 = QStringView::fromArray(hello); + QCOMPARE_EQ(sv, sv2.chopped(1)); + } +#endif // -fsanitize=undefined } void tst_QStringView::basics() const @@ -430,7 +454,7 @@ void tst_QStringView::fromArray() const { static constexpr char16_t hello[] = u"Hello\0abc\0\0."; - constexpr QStringView sv = QStringView::fromArray(hello); + const QStringView sv = QStringView::fromArray(hello); QCOMPARE(sv.size(), 13); QVERIFY(!sv.empty()); QVERIFY(!sv.isEmpty()); diff --git a/tests/auto/corelib/thread/qfuture/CMakeLists.txt b/tests/auto/corelib/thread/qfuture/CMakeLists.txt index aa989f3df1..ba5730d5cf 100644 --- a/tests/auto/corelib/thread/qfuture/CMakeLists.txt +++ b/tests/auto/corelib/thread/qfuture/CMakeLists.txt @@ -16,6 +16,7 @@ qt_internal_add_test(tst_qfuture tst_qfuture.cpp LIBRARIES Qt::CorePrivate + Qt::TestPrivate ) qt_internal_extend_target(tst_qfuture CONDITION MSVC diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 3fc796514d..4cb29c514a 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -17,6 +17,7 @@ #include <private/qobject_p.h> #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <qfuture.h> #include <qfuturewatcher.h> #include <qresultstore.h> @@ -161,6 +162,7 @@ class tst_QFuture: public QObject { Q_OBJECT private slots: + void compareCompiles(); void resultStore(); void future(); void futureToVoid(); @@ -273,6 +275,12 @@ private: QtPrivate::ResultStoreBase &store; }; +void tst_QFuture::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QFuture<int>::const_iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QFuture<QString>::const_iterator>(); +} + void tst_QFuture::resultStore() { int int0 = 0; @@ -1359,16 +1367,16 @@ void tst_QFuture::iterators() QFuture<int>::const_iterator i1 = f.begin(), i2 = i1 + 1; QFuture<int>::const_iterator c1 = i1, c2 = c1 + 1; - QCOMPARE(i1, i1); - QCOMPARE(i1, c1); - QCOMPARE(c1, i1); - QCOMPARE(c1, c1); - QCOMPARE(i2, i2); - QCOMPARE(i2, c2); - QCOMPARE(c2, i2); - QCOMPARE(c2, c2); - QCOMPARE(1 + i1, i1 + 1); - QCOMPARE(1 + c1, c1 + 1); + QT_TEST_EQUALITY_OPS(i1, i1, true); + QT_TEST_EQUALITY_OPS(i1, c1, true); + QT_TEST_EQUALITY_OPS(c1, i1, true); + QT_TEST_EQUALITY_OPS(c1, c1, true); + QT_TEST_EQUALITY_OPS(i2, i2, true); + QT_TEST_EQUALITY_OPS(i2, c2, true); + QT_TEST_EQUALITY_OPS(c2, i2, true); + QT_TEST_EQUALITY_OPS(c2, c2, true); + QT_TEST_EQUALITY_OPS(1 + i1, i1 + 1, true); + QT_TEST_EQUALITY_OPS(1 + c1, c1 + 1, true); QVERIFY(i1 != i2); QVERIFY(i1 != c2); diff --git a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt index 0f9d8d9e52..5abfc14ac6 100644 --- a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt +++ b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt @@ -16,4 +16,5 @@ qt_internal_add_test(tst_qresultstore tst_qresultstore.cpp LIBRARIES Qt::CorePrivate + Qt::TestPrivate ) diff --git a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp index 265b2cd1f6..722184a72a 100644 --- a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp +++ b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> - +#include <QtTest/private/qcomparisontesthelper_p.h> #include <qresultstore.h> using namespace QtPrivate; @@ -23,6 +23,7 @@ class tst_QtConcurrentResultStore : public QObject public slots: void init(); private slots: + void compareCompiles(); void construction(); void iterators(); void addResult(); @@ -52,6 +53,11 @@ void tst_QtConcurrentResultStore::init() vec1 = QList<int> { 4, 5 }; } +void tst_QtConcurrentResultStore::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<ResultIteratorBase>(); +} + void tst_QtConcurrentResultStore::construction() { ResultStoreBase store; @@ -74,17 +80,20 @@ void tst_QtConcurrentResultStore::iterators() storebase.addResult(1, &int1); // ResultStoreBase does not take ownership, only ResultStore<> does. ResultIteratorBase it = storebase.begin(); QCOMPARE(it.resultIndex(), 0); - QCOMPARE(it, storebase.begin()); + QT_TEST_EQUALITY_OPS(it, storebase.begin(), true); QVERIFY(it != storebase.end()); ++it; QCOMPARE(it.resultIndex(), 1); QVERIFY(it != storebase.begin()); QVERIFY(it != storebase.end()); + QT_TEST_EQUALITY_OPS(it, storebase.begin(), false); + QT_TEST_EQUALITY_OPS(it, storebase.end(), false); ++it; QVERIFY(it != storebase.begin()); QCOMPARE(it, storebase.end()); + QT_TEST_EQUALITY_OPS(it, storebase.end(), true); } } @@ -147,8 +156,8 @@ void tst_QtConcurrentResultStore::addResults() store.addResults(-1, &vec1); ResultIteratorBase it = store.begin(); QCOMPARE(it.resultIndex(), 0); - QCOMPARE(it, store.begin()); - QVERIFY(it != store.end()); + QT_TEST_EQUALITY_OPS(it, store.begin(), true); + QT_TEST_EQUALITY_OPS(it, store.end(), false); ++it; QCOMPARE(it.resultIndex(), 1); @@ -162,7 +171,7 @@ void tst_QtConcurrentResultStore::addResults() QCOMPARE(it.resultIndex(), 3); ++it; - QCOMPARE(it, store.end()); + QT_TEST_EQUALITY_OPS(it, store.end(), true); QList<int> empty; const auto countBefore = store.count(); @@ -184,22 +193,22 @@ void tst_QtConcurrentResultStore::resultIndex() ResultIteratorBase it = store.begin(); QCOMPARE(it.resultIndex(), 0); - QVERIFY(it == store.begin()); - QVERIFY(it != store.end()); + QT_TEST_EQUALITY_OPS(it, store.begin(), true); + QT_TEST_EQUALITY_OPS(it, store.end(), false); ++it; QCOMPARE(it.resultIndex(), 1); - QVERIFY(it != store.begin()); - QVERIFY(it != store.end()); + QT_TEST_EQUALITY_OPS(it, store.begin(), false); + QT_TEST_EQUALITY_OPS(it, store.end(), false); ++it; QCOMPARE(it.resultIndex(), 2); - QVERIFY(it != store.end()); + QT_TEST_EQUALITY_OPS(it, store.end(), false); ++it; QCOMPARE(it.resultIndex(), 3); - QVERIFY(it != store.end()); + QT_TEST_EQUALITY_OPS(it, store.end(), false); ++it; - QVERIFY(it == store.end()); + QT_TEST_EQUALITY_OPS(it, store.end(), true); QCOMPARE(store.resultAt(0).value<int>(), int0); QCOMPARE(store.resultAt(1).value<int>(), vec0[0]); diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index b163235d81..18c8d5fbd5 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -1299,8 +1299,16 @@ public: } void registerSocketNotifier(QSocketNotifier *) override {} void unregisterSocketNotifier(QSocketNotifier *) override {} - void registerTimer(Qt::TimerId, Duration, Qt::TimerType, QObject *) override {} - bool unregisterTimer(Qt::TimerId) override { return false; } + void registerTimer(Qt::TimerId id, Duration, Qt::TimerType, QObject *) override + { + if (registeredTimerId <= Qt::TimerId::Invalid) + registeredTimerId = id; + } + bool unregisterTimer(Qt::TimerId id) override + { + Qt::TimerId oldId = std::exchange(registeredTimerId, Qt::TimerId::Invalid); + return id == oldId; + } bool unregisterTimers(QObject *) override { return false; } QList<TimerInfoV2> timersForObject(QObject *) const override { return {}; } Duration remainingTime(Qt::TimerId) const override { return 0s; } @@ -1313,25 +1321,47 @@ public: #endif QBasicAtomicInt visited; // bool + Qt::TimerId registeredTimerId = Qt::TimerId::Invalid; }; -class ThreadObj : public QObject +struct ThreadLocalContent { - Q_OBJECT -public slots: - void visit() { - emit visited(); + static inline const QMetaObject *atStart; + static inline const QMetaObject *atEnd; + QSemaphore *sem; + QBasicTimer timer; + + ThreadLocalContent(QObject *obj, QSemaphore *sem) + : sem(sem) + { + ensureEventDispatcher(); + atStart = QAbstractEventDispatcher::instance()->metaObject(); + timer.start(10s, obj); + } + ~ThreadLocalContent() + { + ensureEventDispatcher(); + atEnd = QAbstractEventDispatcher::instance()->metaObject(); + timer.stop(); + sem->release(); + } + + void ensureEventDispatcher() + { + // QEventLoop's constructor has a call to QThreadData::ensureEventDispatcher() + QEventLoop dummy; } -signals: - void visited(); }; void tst_QThread::customEventDispatcher() { + ThreadLocalContent::atStart = ThreadLocalContent::atEnd = nullptr; + QThread thr; // there should be no ED yet QVERIFY(!thr.eventDispatcher()); DummyEventDispatcher *ed = new DummyEventDispatcher; + QPointer<DummyEventDispatcher> weak_ed(ed); thr.setEventDispatcher(ed); // the new ED should be set QCOMPARE(thr.eventDispatcher(), ed); @@ -1340,25 +1370,39 @@ void tst_QThread::customEventDispatcher() thr.start(); // start() should not overwrite the ED QCOMPARE(thr.eventDispatcher(), ed); + QVERIFY(!weak_ed.isNull()); - ThreadObj obj; + QObject obj; obj.moveToThread(&thr); // move was successful? QCOMPARE(obj.thread(), &thr); - QEventLoop loop; - connect(&obj, SIGNAL(visited()), &loop, SLOT(quit()), Qt::QueuedConnection); - QMetaObject::invokeMethod(&obj, "visit", Qt::QueuedConnection); - loop.exec(); + + QSemaphore threadLocalSemaphore; + QMetaObject::invokeMethod(&obj, [&]() { +#ifndef Q_OS_WIN + // On Windows, the thread_locals are unsequenced between DLLs, so this + // could run after QThreadPrivate::finish() + static thread_local +#endif + ThreadLocalContent d(&obj, &threadLocalSemaphore); + }, Qt::BlockingQueuedConnection); + // test that the ED has really been used QVERIFY(ed->visited.loadRelaxed()); + // and it's ours + QCOMPARE(ThreadLocalContent::atStart->className(), "DummyEventDispatcher"); - QPointer<DummyEventDispatcher> weak_ed(ed); QVERIFY(!weak_ed.isNull()); thr.quit(); + // wait for thread to be stopped QVERIFY(thr.wait(30000)); + QVERIFY(threadLocalSemaphore.tryAcquire(1, 30s)); + // test that ED has been deleted QVERIFY(weak_ed.isNull()); + // test that ED was ours + QCOMPARE(ThreadLocalContent::atEnd->className(), "DummyEventDispatcher"); } class Job : public QObject diff --git a/tests/auto/corelib/time/CMakeLists.txt b/tests/auto/corelib/time/CMakeLists.txt index fe249d3c4a..1fb95e9245 100644 --- a/tests/auto/corelib/time/CMakeLists.txt +++ b/tests/auto/corelib/time/CMakeLists.txt @@ -8,4 +8,6 @@ if(QT_FEATURE_datetimeparser) add_subdirectory(qdatetimeparser) endif() add_subdirectory(qtime) -add_subdirectory(qtimezone) +if(QT_FEATURE_timezone) + add_subdirectory(qtimezone) +endif() diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index a350ffeb04..e71f32a3ca 100644 --- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -7,6 +7,7 @@ #include <private/qcomparisontesthelper_p.h> #include <qlocale.h> +#include <qscopeguard.h> #if defined(Q_OS_WIN) #include <QOperatingSystemVersion> @@ -16,6 +17,8 @@ # define USING_WIN_TZ #endif +using namespace Qt::StringLiterals; + class tst_QTimeZone : public QObject { Q_OBJECT @@ -38,6 +41,8 @@ private Q_SLOTS: void availableTimeZoneIds(); void utcOffsetId_data(); void utcOffsetId(); + void aliasMatches_data(); + void aliasMatches(); void specificTransition_data(); void specificTransition(); void transitionEachZone_data(); @@ -558,9 +563,12 @@ void tst_QTimeZone::isTimeZoneIdAvailable() const QList<QByteArray> available = QTimeZone::availableTimeZoneIds(); for (const QByteArray &id : available) { QVERIFY2(QTimeZone::isTimeZoneIdAvailable(id), id); - QVERIFY2(QTimeZone(id).isValid(), id); - QCOMPARE(QTimeZone(id).id(), id); + const QTimeZone zone(id); + QVERIFY2(zone.isValid(), id); + QVERIFY2(zone.aliasMatches(id), zone.id() + " != " + id); } + // availableTimeZoneIds() doesn't list all possible offset IDs, but + // isTimeZoneIdAvailable() should accept them. for (qint32 offset = QTimeZone::MinUtcOffsetSecs; offset <= QTimeZone::MinUtcOffsetSecs; ++offset) { const QByteArray id = QTimeZone(offset).id(); @@ -584,7 +592,7 @@ void tst_QTimeZone::utcOffsetId_data() #define ROW(name, valid, offset) \ QTest::newRow(name) << QByteArray(name) << valid << offset - // See qtbase/util/locale_database/cldr2qtimezone.py for source + // See qtbase/util/locale_database/zonedata.py for source // CLDR v35.1 IDs: ROW("UTC", true, 0); ROW("UTC-14:00", true, -50400); @@ -700,6 +708,58 @@ void tst_QTimeZone::utcOffsetId() } } +void tst_QTimeZone::aliasMatches_data() +{ + QTest::addColumn<QByteArray>("iana"); + QTest::addColumn<QByteArray>("alias"); + + QTest::newRow("Montreal=Toronto") << "America/Toronto"_ba << "America/Montreal"_ba; + QTest::newRow("Asmera=Asmara") << "Africa/Asmara"_ba << "Africa/Asmera"_ba; + QTest::newRow("Argentina/Catamarca") + << "America/Argentina/Catamarca"_ba << "America/Catamarca"_ba; + QTest::newRow("Godthab=Nuuk") << "America/Nuuk"_ba << "America/Godthab"_ba; + QTest::newRow("Indiana/Indianapolis") + << "America/Indiana/Indianapolis"_ba << "America/Indianapolis"_ba; + QTest::newRow("Kentucky/Louisville") + << "America/Kentucky/Louisville"_ba << "America/Louisville"_ba; + QTest::newRow("Calcutta=Kolkata") << "Asia/Kolkata"_ba << "Asia/Calcutta"_ba; + QTest::newRow("Katmandu=Kathmandu") << "Asia/Kathmandu"_ba << "Asia/Katmandu"_ba; + QTest::newRow("Rangoon=Yangon") << "Asia/Yangon"_ba << "Asia/Rangoon"_ba; + QTest::newRow("Saigon=Ho_Chi_Minh") << "Asia/Ho_Chi_Minh"_ba << "Asia/Saigon"_ba; + QTest::newRow("Faeroe=Faroe") << "Atlantic/Faroe"_ba << "Atlantic/Faeroe"_ba; + QTest::newRow("Currie=Hobart") << "Australia/Hobart"_ba << "Australia/Currie"_ba; + QTest::newRow("Kiev=Kyiv") << "Europe/Kyiv"_ba << "Europe/Kiev"_ba; + QTest::newRow("Uzhgorod=Kyiv") << "Europe/Kyiv"_ba << "Europe/Uzhgorod"_ba; + QTest::newRow("Zaporozhye=Kyiv") << "Europe/Kyiv"_ba << "Europe/Zaporozhye"_ba; + QTest::newRow("Fiji=Fiji") << "Pacific/Fiji"_ba << "Pacific/Fiji"_ba; + QTest::newRow("Enderbury=Enderbury") << "Pacific/Enderbury"_ba << "Pacific/Enderbury"_ba; +} + +void tst_QTimeZone::aliasMatches() +{ + QFETCH(const QByteArray, iana); + QFETCH(const QByteArray, alias); + const QTimeZone zone(iana); + const QTimeZone peer(alias); + if (!zone.isValid()) + QSKIP("Backend doesn't support IANA ID"); + + auto report = qScopeGuard([zone, peer]() { + const QByteArray zid = zone.id(), pid = peer.id(); + qDebug("Using %s and %s", zid.constData(), pid.constData()); + }); + QVERIFY2(peer.isValid(), "Construction should have fallen back on IANA ID"); + QVERIFY(zone.aliasMatches(zone.id())); + QVERIFY(zone.aliasMatches(iana)); + QVERIFY(peer.aliasMatches(peer.id())); + QVERIFY(peer.aliasMatches(alias)); + QVERIFY(zone.aliasMatches(peer.id())); + QVERIFY(zone.aliasMatches(alias)); + QVERIFY(peer.aliasMatches(zone.id())); + QVERIFY(peer.aliasMatches(iana)); + report.dismiss(); +} + void tst_QTimeZone::specificTransition_data() { QTest::addColumn<QByteArray>("zone"); @@ -924,7 +984,7 @@ void tst_QTimeZone::stressTest() for (const QByteArray &id : idList) { QTimeZone testZone = QTimeZone(id); QCOMPARE(testZone.isValid(), true); - QCOMPARE(testZone.id(), id); + QVERIFY2(testZone.aliasMatches(id), testZone.id() + " != " + id); QDateTime testDate = QDateTime(QDate(2015, 1, 1), QTime(0, 0), UTC); testZone.territory(); testZone.comment(); @@ -972,7 +1032,7 @@ void tst_QTimeZone::windowsId() Current Windows zones for "Central Standard Time": Region IANA Id(s) Default "America/Chicago" - Canada "America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute" + Canada "America/Winnipeg America/Rankin_Inlet America/Resolute" Mexico "America/Matamoros" USA "America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee" "America/North_Dakota/Beulah America/North_Dakota/Center" @@ -1004,8 +1064,8 @@ void tst_QTimeZone::windowsId() "America/Chicago", "America/Indiana/Knox", "America/Indiana/Tell_City", "America/Matamoros", "America/Menominee", "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", - "America/Ojinaga", "America/Rainy_River", "America/Rankin_Inlet", - "America/Resolute", "America/Winnipeg", "CST6CDT" + "America/Ojinaga", "America/Rankin_Inlet", "America/Resolute", + "America/Winnipeg", "CST6CDT" }; QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time"), list); } @@ -1018,7 +1078,7 @@ void tst_QTimeZone::windowsId() { // Check valid country returns list in preference order const QList<QByteArray> list = { - "America/Winnipeg", "America/Rainy_River", "America/Rankin_Inlet", "America/Resolute" + "America/Winnipeg", "America/Rankin_Inlet", "America/Resolute" }; QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::Canada), list); } @@ -1280,7 +1340,7 @@ void tst_QTimeZone::utcTest() void tst_QTimeZone::icuTest() { -#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(icu) +#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(icu) && !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(); @@ -1323,7 +1383,7 @@ void tst_QTimeZone::icuTest() if (QTest::currentTestFailed()) return; testEpochTranPrivate(QIcuTimeZonePrivate("America/Toronto")); -#endif // icu +#endif // ICU not on Unix } void tst_QTimeZone::tzTest() @@ -1527,7 +1587,7 @@ void tst_QTimeZone::tzTest() QDateTime dt(QDate(2016, 3, 28), QTime(0, 0), UTC); QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, QString("+07")); } -#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !Q_OS_DARWIN +#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !Q_OS_DARWIN && !Q_OS_ANDROID } void tst_QTimeZone::macTest() @@ -1685,6 +1745,8 @@ void tst_QTimeZone::localeSpecificDisplayName() QVERIFY(zone.isValid()); const QString localeName = zone.displayName(timeType, QTimeZone::LongName, locale); + if (localeName.isEmpty()) // Backend doesn't know how to localize this zone's name + QEXPECT_FAIL("", "QTBUG-115158 zone name localization unknown", Continue); QCOMPARE(localeName, expectedName); } @@ -1857,18 +1919,10 @@ void tst_QTimeZone::stdCompatibility() QFETCH(const std::chrono::time_zone *, timeZone); QByteArrayView zoneName = QByteArrayView(timeZone->name()); QTimeZone tz = QTimeZone::fromStdTimeZonePtr(timeZone); - if (tz.isValid()) { - QCOMPARE(tz.id(), zoneName); - } else { - // QTBUG-102187: a few timezones reported by tzdb might not be - // recognized by QTimeZone. This happens for instance on Windows, where - // tzdb is using ICU, whose database does not match QTimeZone's. - const bool isKnownUnknown = - !zoneName.contains('/') - || zoneName == "Antarctica/Troll" - || zoneName.startsWith("SystemV/"); - QVERIFY(isKnownUnknown); - } + if (tz.isValid()) + QVERIFY2(tz.aliasMatches(zoneName), tz.id().constData()); + else + QVERIFY(!QTimeZone::isTimeZoneIdAvailable(zoneName.toByteArray())); #else QSKIP("This test requires C++20's <chrono>."); #endif diff --git a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp index c08afd67c4..47e0ead270 100644 --- a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp +++ b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp @@ -20,6 +20,8 @@ private slots: void repeated_result(); void intermediary_result_data(); void intermediary_result(); + void static_hash_data() { intermediary_result_data(); } + void static_hash(); void sha1(); void sha3_data(); void sha3(); @@ -29,9 +31,9 @@ private slots: void blake2(); void files_data(); void files(); - void hashLength_data(); + void hashLength_data() { all_methods(true); } void hashLength(); - void addDataAcceptsNullByteArrayView_data() { hashLength_data(); } + void addDataAcceptsNullByteArrayView_data() { all_methods(false); } void addDataAcceptsNullByteArrayView(); void move(); void swap(); @@ -40,6 +42,7 @@ private slots: void moreThan4GiBOfData(); void keccakBufferOverflow(); private: + void all_methods(bool includingNumAlgorithms) const; void ensureLargeData(); std::vector<char> large; }; @@ -197,6 +200,20 @@ void tst_QCryptographicHash::intermediary_result() hash.reset(); } +void tst_QCryptographicHash::static_hash() +{ + QFETCH(const int, algo); + QFETCH(const QByteArray, first); + QFETCH(const QByteArray, hash_first); + + const auto _algo = QCryptographicHash::Algorithm(algo); + + QCOMPARE(QCryptographicHash::hash(first, _algo), hash_first); + + std::byte buffer[1024]; + QCOMPARE(QCryptographicHash::hashInto(buffer, first, _algo), hash_first); +} + void tst_QCryptographicHash::sha1() { @@ -474,12 +491,14 @@ void tst_QCryptographicHash::files() } } -void tst_QCryptographicHash::hashLength_data() +void tst_QCryptographicHash::all_methods(bool inclNumAlgos) const { QTest::addColumn<QCryptographicHash::Algorithm>("algorithm"); auto metaEnum = QMetaEnum::fromType<QCryptographicHash::Algorithm>(); for (int i = 0, value = metaEnum.value(i); value != -1; value = metaEnum.value(++i)) { auto algorithm = QCryptographicHash::Algorithm(value); + if (!inclNumAlgos && algorithm == QCryptographicHash::Algorithm::NumAlgorithms) + continue; QTest::addRow("%s", metaEnum.key(i)) << algorithm; } } @@ -495,6 +514,9 @@ void tst_QCryptographicHash::hashLength() expectedSize = 0; } else { expectedSize = QCryptographicHash::hash("test", algorithm).size(); + + std::byte buffer[1024]; + QCOMPARE(QCryptographicHash::hashInto(buffer, "foo", algorithm).size(), expectedSize); } QCOMPARE(QCryptographicHash::hashLength(algorithm), expectedSize); } diff --git a/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt b/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt index 3f76f8a38f..2569e0c7a9 100644 --- a/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt +++ b/tests/auto/corelib/tools/qeasingcurve/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qeasingcurve SOURCES tst_qeasingcurve.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp index fc8c1a3e5c..0a933a1e2b 100644 --- a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp +++ b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> +#include <private/qcomparisontesthelper_p.h> #include <qeasingcurve.h> @@ -16,6 +17,7 @@ private slots: void valueForProgress_data(); void valueForProgress(); void setCustomType(); + void comparisonCompiles(); void operators(); void properties(); void metaTypes(); @@ -420,6 +422,11 @@ void tst_QEasingCurve::setCustomType() QCOMPARE(curve.valueForProgress(0.99), 0.99); } +void tst_QEasingCurve::comparisonCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QEasingCurve>(); +} + void tst_QEasingCurve::operators() { { // member-swap() @@ -447,28 +454,28 @@ void tst_QEasingCurve::operators() curve2 = curve; curve2.setOvershoot(qreal(1.70158)); QCOMPARE(curve.overshoot(), curve2.overshoot()); - QVERIFY(curve2 == curve); + QT_TEST_EQUALITY_OPS(curve2, curve, true); curve.setOvershoot(3.0); - QVERIFY(curve2 != curve); + QT_TEST_EQUALITY_OPS(curve2, curve, false); curve2.setOvershoot(3.0); - QVERIFY(curve2 == curve); + QT_TEST_EQUALITY_OPS(curve2, curve, true); curve2.setType(QEasingCurve::Linear); QCOMPARE(curve.overshoot(), curve2.overshoot()); - QVERIFY(curve2 != curve); + QT_TEST_EQUALITY_OPS(curve2, curve, false); curve2.setType(QEasingCurve::InBack); QCOMPARE(curve.overshoot(), curve2.overshoot()); - QVERIFY(curve2 == curve); + QT_TEST_EQUALITY_OPS(curve2, curve, true); QEasingCurve curve3; QEasingCurve curve4; curve4.setAmplitude(curve4.amplitude()); QEasingCurve curve5; curve5.setAmplitude(0.12345); - QVERIFY(curve3 == curve4); // default value and not assigned - QVERIFY(curve3 != curve5); // unassinged and other value - QVERIFY(curve4 != curve5); + QT_TEST_EQUALITY_OPS(curve3, curve4, true); // default value and not assigned + QT_TEST_EQUALITY_OPS(curve3, curve5, false); // unassinged and other value + QT_TEST_EQUALITY_OPS(curve4, curve5, false); } class tst_QEasingProperties : public QObject @@ -890,7 +897,7 @@ void tst_QEasingCurve::streamInOut() dsw << orig; dsr >> copy; - QCOMPARE(copy == orig, equality); + QT_TEST_EQUALITY_OPS(copy, orig, equality); } QTEST_MAIN(tst_QEasingCurve) diff --git a/tests/auto/corelib/tools/qline/CMakeLists.txt b/tests/auto/corelib/tools/qline/CMakeLists.txt index 17a3a1bcef..7d9fdf51a9 100644 --- a/tests/auto/corelib/tools/qline/CMakeLists.txt +++ b/tests/auto/corelib/tools/qline/CMakeLists.txt @@ -14,6 +14,8 @@ endif() qt_internal_add_test(tst_qline SOURCES tst_qline.cpp + LIBRARIES + Qt::TestPrivate ) ## Scopes: diff --git a/tests/auto/corelib/tools/qline/tst_qline.cpp b/tests/auto/corelib/tools/qline/tst_qline.cpp index 51f1f8ac79..10069e821b 100644 --- a/tests/auto/corelib/tools/qline/tst_qline.cpp +++ b/tests/auto/corelib/tools/qline/tst_qline.cpp @@ -4,6 +4,7 @@ #include <QTest> #include <qline.h> #include <qmath.h> +#include <private/qcomparisontesthelper_p.h> #include <array> @@ -11,6 +12,16 @@ class tst_QLine : public QObject { Q_OBJECT private slots: + void testComparisonCompiles(); + void testComparison_data(); + void testComparison(); + + void testFuzzyCompare_data(); + void testFuzzyCompare(); + + void testIsNull_data(); + void testIsNull(); + void testIntersection(); void testIntersection_data(); @@ -41,6 +52,120 @@ private slots: }; const qreal epsilon = sizeof(qreal) == sizeof(double) ? 1e-8 : 1e-4; +constexpr static qreal qreal_min = std::numeric_limits<qreal>::min(); + +void tst_QLine::testComparisonCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QLine>(); + QTestPrivate::testEqualityOperatorsCompile<QLineF>(); + QTestPrivate::testEqualityOperatorsCompile<QLineF, QLine>(); +} + +void tst_QLine::testComparison_data() +{ + QTest::addColumn<double>("xa1"); + QTest::addColumn<double>("ya1"); + QTest::addColumn<double>("xa2"); + QTest::addColumn<double>("ya2"); + QTest::addColumn<double>("xb1"); + QTest::addColumn<double>("yb1"); + QTest::addColumn<double>("xb2"); + QTest::addColumn<double>("yb2"); + QTest::addColumn<bool>("result"); + QTest::addColumn<bool>("floatResult"); + QTest::addColumn<bool>("mixedResult"); + + auto row = [&](double xa1, double ya1, double xa2, double ya2, + double xb1, double yb1, double xb2, double yb2, + bool result, bool floatResult, bool mixedResult) + { + QString str; + QDebug dbg(&str); + dbg.nospace() << "[(" << xa1 << ", " << ya1 << "); (" << xa2 << ", " << ya2 << ")] vs [(" + << xb1 << ", " << yb1 << "); (" << xb2 << ", " << yb2 << ")]"; + QTest::addRow("%s", str.toLatin1().constData()) + << xa1 << ya1 << xa2 << ya2 << xb1 << yb1 << xb2 << yb2 + << result << floatResult << mixedResult; + }; + + row(-1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, true, true, true); + row(-1.1, -0.9, 1.1, 0.9, -1.0, -1.0, 1.0, 1.0, true, false, false); + row(-1.0, -1.0, 1.0, 1.0, -0.9, -1.1, 0.9, 1.1, true, false, true); + row(-qreal_min, -1.0, 1.0, qreal_min, 0.0, -1.1, 0.9, 0.0, true, false, true); +} + +void tst_QLine::testComparison() +{ + QFETCH(double, xa1); + QFETCH(double, ya1); + QFETCH(double, xa2); + QFETCH(double, ya2); + QFETCH(double, xb1); + QFETCH(double, yb1); + QFETCH(double, xb2); + QFETCH(double, yb2); + QFETCH(bool, result); + QFETCH(bool, floatResult); + QFETCH(bool, mixedResult); + + const QLineF l1f(xa1, ya1, xa2, ya2); + const QLine l1 = l1f.toLine(); + + const QLineF l2f(xb1, yb1, xb2, yb2); + const QLine l2 = l2f.toLine(); + + QT_TEST_EQUALITY_OPS(l1, l2, result); + QT_TEST_EQUALITY_OPS(l1f, l2f, floatResult); + QT_TEST_EQUALITY_OPS(l1f, l2, mixedResult); +} + +void tst_QLine::testFuzzyCompare_data() +{ + testComparison_data(); +} + +void tst_QLine::testFuzzyCompare() +{ + QFETCH(double, xa1); + QFETCH(double, ya1); + QFETCH(double, xa2); + QFETCH(double, ya2); + QFETCH(double, xb1); + QFETCH(double, yb1); + QFETCH(double, xb2); + QFETCH(double, yb2); + QFETCH(bool, floatResult); + + const QLineF l1f(xa1, ya1, xa2, ya2); + const QLineF l2f(xb1, yb1, xb2, yb2); + + QCOMPARE_EQ(qFuzzyCompare(l1f, l2f), floatResult); +} + +void tst_QLine::testIsNull_data() +{ + QTest::addColumn<QLineF>("lineF"); + QTest::addColumn<bool>("result"); + QTest::addColumn<bool>("floatResult"); + + QTest::newRow("non-null") << QLineF(1.0, 1.0, 2.0, 2.0) << false << false; + QTest::newRow("null") << QLineF(1.0, 1.0, 1.0, 1.0) << true << true; + QTest::newRow("null_int_non-null_float") << QLineF(1.0, 1.0, 1.1, 1.1) << true << false; + QTest::newRow("with_qreal_min") << QLineF(-qreal_min, qreal_min, 0.0, 0.0) << true << true; +} + +void tst_QLine::testIsNull() +{ + QFETCH(QLineF, lineF); + QFETCH(bool, result); + QFETCH(bool, floatResult); + + const QLine line = lineF.toLine(); + + QCOMPARE_EQ(line.isNull(), result); + QCOMPARE_EQ(lineF.isNull(), floatResult); + QCOMPARE_EQ(qFuzzyIsNull(lineF), floatResult); +} void tst_QLine::testSet() { diff --git a/tests/auto/corelib/tools/qmargins/CMakeLists.txt b/tests/auto/corelib/tools/qmargins/CMakeLists.txt index 2e0ea797ff..b0adf63f40 100644 --- a/tests/auto/corelib/tools/qmargins/CMakeLists.txt +++ b/tests/auto/corelib/tools/qmargins/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qmargins SOURCES tst_qmargins.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp index 2611f62f01..dc0b0e4085 100644 --- a/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp +++ b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp @@ -32,15 +32,28 @@ CHECK(const &&); #include <QTest> #include <qmargins.h> +#include <private/qcomparisontesthelper_p.h> #include <array> Q_DECLARE_METATYPE(QMargins) +constexpr static qreal qreal_min = std::numeric_limits<qreal>::min(); + class tst_QMargins : public QObject { Q_OBJECT private slots: + void comparisonCompiles(); + void comparison_data(); + void comparison(); + + void fuzzyComparison_data(); + void fuzzyComparison(); + + void isNull_data(); + void isNull(); + void getSetCheck(); #ifndef QT_NO_DATASTREAM void dataStreamCheck(); @@ -65,6 +78,92 @@ private slots: void toMarginsF(); }; +void tst_QMargins::comparisonCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QMargins>(); + QTestPrivate::testEqualityOperatorsCompile<QMarginsF>(); + QTestPrivate::testEqualityOperatorsCompile<QMarginsF, QMargins>(); +} + +void tst_QMargins::comparison_data() +{ + QTest::addColumn<QMarginsF>("lhs"); + QTest::addColumn<QMarginsF>("rhs"); + QTest::addColumn<bool>("result"); + QTest::addColumn<bool>("floatResult"); + QTest::addColumn<bool>("mixedResult"); + + auto row = [](const QMarginsF &lhs, const QMarginsF &rhs, bool res, bool fRes, bool mRes) { + QString str; + QDebug dbg(&str); + dbg.nospace() << "(" << lhs.left() << ", " << lhs.top() << ", " << lhs.right() << ", " + << lhs.bottom() << ") vs (" << rhs.left() << ", " << rhs.top() << ", " + << rhs.right() << ", " << rhs.bottom() << ")"; + QTest::addRow("%s", str.toLatin1().constData()) << lhs << rhs << res << fRes << mRes; + }; + + row(QMarginsF(0.0, 0.0, 0.0, 0.0), QMarginsF(0.0, 0.0, 0.0, 0.0), true, true, true); + row(QMarginsF(qreal_min, -qreal_min, -qreal_min, qreal_min), QMarginsF(0.0, 0.0, 0.0, 0.0), true, true, true); + row(QMarginsF(1.0, 2.0, 3.0, 4.0), QMarginsF(1.1, 2.1, 2.9, 3.9), true, false, true); + row(QMarginsF(1.5, 2.5, 3.0, 4.0), QMarginsF(1.1, 2.1, 2.9, 3.9), false, false, false); +} + +void tst_QMargins::comparison() +{ + QFETCH(const QMarginsF, lhs); + QFETCH(const QMarginsF, rhs); + QFETCH(const bool, result); + QFETCH(const bool, floatResult); + QFETCH(const bool, mixedResult); + + QT_TEST_EQUALITY_OPS(lhs, rhs, floatResult); + + const QMargins lhsInt = lhs.toMargins(); + const QMargins rhsInt = rhs.toMargins(); + QT_TEST_EQUALITY_OPS(lhsInt, rhsInt, result); + + QT_TEST_EQUALITY_OPS(lhs, rhsInt, mixedResult); +} + +void tst_QMargins::fuzzyComparison_data() +{ + comparison_data(); +} + +void tst_QMargins::fuzzyComparison() +{ + QFETCH(const QMarginsF, lhs); + QFETCH(const QMarginsF, rhs); + QFETCH(const bool, floatResult); + + QCOMPARE_EQ(qFuzzyCompare(lhs, rhs), floatResult); +} + +void tst_QMargins::isNull_data() +{ + QTest::addColumn<QMarginsF>("margins"); + QTest::addColumn<bool>("result"); + + QTest::newRow("null") << QMarginsF(0.0, 0.0, 0.0, 0.0) << true; + QTest::newRow("non_null_left") << QMarginsF(1.0, 0.0, 0.0, 0.0) << false; + QTest::newRow("non_null_top") << QMarginsF(0.0, 0.5, 0.0, 0.0) << false; + QTest::newRow("non_null_right") << QMarginsF(0.0, 0.0, -2.0, 0.0) << false; + QTest::newRow("non_null_bottom") << QMarginsF(0.0, 0.0, 0.0, -0.6) << false; + QTest::newRow("almost_null") << QMarginsF(qreal_min, -qreal_min, qreal_min, -qreal_min) << true; +} + +void tst_QMargins::isNull() +{ + QFETCH(const QMarginsF, margins); + QFETCH(const bool, result); + + QCOMPARE_EQ(margins.isNull(), result); + QCOMPARE_EQ(qFuzzyIsNull(margins), result); + + const QMargins marginsInt = margins.toMargins(); + QCOMPARE_EQ(marginsInt.isNull(), result); +} + // Testing get/set functions void tst_QMargins::getSetCheck() { diff --git a/tests/auto/corelib/tools/qpoint/CMakeLists.txt b/tests/auto/corelib/tools/qpoint/CMakeLists.txt index f1402d8815..82ece1fc15 100644 --- a/tests/auto/corelib/tools/qpoint/CMakeLists.txt +++ b/tests/auto/corelib/tools/qpoint/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qpoint SOURCES tst_qpoint.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp index 7fea787131..4763c1bf07 100644 --- a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp +++ b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp @@ -5,6 +5,7 @@ #ifdef QVARIANT_H # error "This test requires qpoint.h to not include qvariant.h" #endif +#include <private/qcomparisontesthelper_p.h> // don't assume <type_traits> template <typename T, typename U> @@ -71,6 +72,7 @@ private slots: void operator_unary_minus_data(); void operator_unary_minus(); + void operatorsCompile(); void operator_eq_data(); void operator_eq(); @@ -155,6 +157,8 @@ void tst_QPoint::toPointF() QFETCH(const QPointF, result); QCOMPARE(input.toPointF(), result); + // test also mixed-type comparison + QT_TEST_EQUALITY_OPS(input, result, true); } void tst_QPoint::transposed() @@ -350,6 +354,12 @@ void tst_QPoint::operator_unary_minus() QCOMPARE(-point, expected); } +void tst_QPoint::operatorsCompile() +{ + // Mixed-type comparison is tested in tst_QPointF. + QTestPrivate::testEqualityOperatorsCompile<QPoint>(); +} + void tst_QPoint::operator_eq_data() { QTest::addColumn<QPoint>("point1"); @@ -371,12 +381,9 @@ void tst_QPoint::operator_eq() QFETCH(QPoint, point2); QFETCH(bool, expectEqual); - bool equal = point1 == point2; - QCOMPARE(equal, expectEqual); - bool notEqual = point1 != point2; - QCOMPARE(notEqual, !expectEqual); + QT_TEST_EQUALITY_OPS(point1, point2, expectEqual); - if (equal) + if (expectEqual) QCOMPARE(qHash(point1), qHash(point2)); } diff --git a/tests/auto/corelib/tools/qpointf/CMakeLists.txt b/tests/auto/corelib/tools/qpointf/CMakeLists.txt index 16e5a9036a..28cbe185b2 100644 --- a/tests/auto/corelib/tools/qpointf/CMakeLists.txt +++ b/tests/auto/corelib/tools/qpointf/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qpointf SOURCES tst_qpointf.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp index 392c22c70a..ebbac4ec7c 100644 --- a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp +++ b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp @@ -5,6 +5,7 @@ #ifdef QVARIANT_H # error "This test requires qpoint.h to not include qvariant.h" #endif +#include <private/qcomparisontesthelper_p.h> // don't assume <type_traits> template <typename T, typename U> @@ -71,9 +72,13 @@ private slots: void operator_unary_minus_data(); void operator_unary_minus(); + void operatorsCompile(); void operator_eq_data(); void operator_eq(); + void fuzzyCompare_data(); + void fuzzyCompare(); + void toPoint_data(); void toPoint(); @@ -101,15 +106,19 @@ void tst_QPointF::isNull() { QPointF point(0, 0); QVERIFY(point.isNull()); + QVERIFY(qFuzzyIsNull(point)); ++point.rx(); QVERIFY(!point.isNull()); + QVERIFY(!qFuzzyIsNull(point)); point.rx() -= 2; QVERIFY(!point.isNull()); + QVERIFY(!qFuzzyIsNull(point)); QPointF nullNegativeZero(qreal(-0.0), qreal(-0.0)); QCOMPARE(nullNegativeZero.x(), (qreal)-0.0f); QCOMPARE(nullNegativeZero.y(), (qreal)-0.0f); QVERIFY(nullNegativeZero.isNull()); + QVERIFY(qFuzzyIsNull(nullNegativeZero)); } void tst_QPointF::manhattanLength_data() @@ -345,21 +354,29 @@ void tst_QPointF::operator_unary_minus() QCOMPARE(-point, expected); } +void tst_QPointF::operatorsCompile() +{ + QTestPrivate::testEqualityOperatorsCompile<QPointF>(); + QTestPrivate::testEqualityOperatorsCompile<QPointF, QPoint>(); +} + void tst_QPointF::operator_eq_data() { QTest::addColumn<QPointF>("point1"); QTest::addColumn<QPointF>("point2"); QTest::addColumn<bool>("expectEqual"); - - QTest::newRow("(0, 0) == (0, 0)") << QPointF(0, 0) << QPointF(0, 0) << true; - QTest::newRow("(-1, 0) == (-1, 0)") << QPointF(-1, 0) << QPointF(-1, 0) << true; - QTest::newRow("(-1, 0) != (0, 0)") << QPointF(-1, 0) << QPointF(0, 0) << false; - QTest::newRow("(-1, 0) != (0, -1)") << QPointF(-1, 0) << QPointF(0, -1) << false; - QTest::newRow("(-1.125, 0.25) == (-1.125, 0.25)") << QPointF(-1.125, 0.25) << QPointF(-1.125, 0.25) << true; + QTest::addColumn<bool>("expectIntEqual"); + + QTest::newRow("(0, 0) == (0, 0)") << QPointF(0, 0) << QPointF(0, 0) << true << true; + QTest::newRow("(-1, 0) == (-1, 0)") << QPointF(-1, 0) << QPointF(-1, 0) << true << true; + QTest::newRow("(-1, 0) != (0, 0)") << QPointF(-1, 0) << QPointF(0, 0) << false << false; + QTest::newRow("(-1, 0) != (0, -1)") << QPointF(-1, 0) << QPointF(0, -1) << false << false; + QTest::newRow("(-1.125, 0.25) == (-1.125, 0.25)") + << QPointF(-1.125, 0.25) << QPointF(-1.125, 0.25) << true << false; QTest::newRow("(QREAL_MIN, QREAL_MIN) == (QREAL_MIN, QREAL_MIN)") - << QPointF(QREAL_MIN, QREAL_MIN) << QPointF(QREAL_MIN, QREAL_MIN) << true; + << QPointF(QREAL_MIN, QREAL_MIN) << QPointF(QREAL_MIN, QREAL_MIN) << true << true; QTest::newRow("(QREAL_MAX, QREAL_MAX) == (QREAL_MAX, QREAL_MAX)") - << QPointF(QREAL_MAX, QREAL_MAX) << QPointF(QREAL_MAX, QREAL_MAX) << true; + << QPointF(QREAL_MAX, QREAL_MAX) << QPointF(QREAL_MAX, QREAL_MAX) << true << false; } void tst_QPointF::operator_eq() @@ -367,11 +384,26 @@ void tst_QPointF::operator_eq() QFETCH(QPointF, point1); QFETCH(QPointF, point2); QFETCH(bool, expectEqual); + QFETCH(bool, expectIntEqual); + + QT_TEST_EQUALITY_OPS(point1, point2, expectEqual); + + const QPoint intPoint2 = point2.toPoint(); + QT_TEST_EQUALITY_OPS(point1, intPoint2, expectIntEqual); +} + +void tst_QPointF::fuzzyCompare_data() +{ + operator_eq_data(); +} + +void tst_QPointF::fuzzyCompare() +{ + QFETCH(QPointF, point1); + QFETCH(QPointF, point2); + QFETCH(bool, expectEqual); - bool equal = point1 == point2; - QCOMPARE(equal, expectEqual); - bool notEqual = point1 != point2; - QCOMPARE(notEqual, !expectEqual); + QCOMPARE_EQ(qFuzzyCompare(point1, point2), expectEqual); } void tst_QPointF::toPoint_data() diff --git a/tests/auto/corelib/tools/qrect/CMakeLists.txt b/tests/auto/corelib/tools/qrect/CMakeLists.txt index a02e1c33a5..c98c836379 100644 --- a/tests/auto/corelib/tools/qrect/CMakeLists.txt +++ b/tests/auto/corelib/tools/qrect/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qrect SOURCES tst_qrect.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qrect/tst_qrect.cpp b/tests/auto/corelib/tools/qrect/tst_qrect.cpp index 0f3dd1a0ef..c7c8b3a560 100644 --- a/tests/auto/corelib/tools/qrect/tst_qrect.cpp +++ b/tests/auto/corelib/tools/qrect/tst_qrect.cpp @@ -7,6 +7,8 @@ #include <limits.h> #include <qdebug.h> +#include <private/qcomparisontesthelper_p.h> + #include <array> class tst_QRect : public QObject @@ -33,8 +35,14 @@ public: static QPoint getQPointCase( QPointCases p ); private slots: + void comparisonCompiles(); + void comparison_data(); + void comparison(); + void fuzzyComparison_data(); + void fuzzyComparison(); void isNull_data(); void isNull(); + void fuzzyIsNull(); void newIsEmpty_data(); void newIsEmpty(); void newIsValid_data(); @@ -160,6 +168,8 @@ private slots: #define LARGE 1000000000 static bool isLarge(int x) { return x > LARGE || x < -LARGE; } +static constexpr qreal qreal_min = std::numeric_limits<qreal>::min(); + QRect tst_QRect::getQRectCase( QRectCases c ) { // Should return the best variety of possible QRects, if a @@ -242,6 +252,80 @@ QPoint tst_QRect::getQPointCase( QPointCases p ) } } +void tst_QRect::comparisonCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QRect>(); + QTestPrivate::testEqualityOperatorsCompile<QRectF>(); + QTestPrivate::testEqualityOperatorsCompile<QRectF, QRect>(); +} + +void tst_QRect::comparison_data() +{ + QTest::addColumn<QRectF>("lhsF"); + QTest::addColumn<QRectF>("rhsF"); + QTest::addColumn<bool>("result"); + QTest::addColumn<bool>("floatResult"); + QTest::addColumn<bool>("mixedResult"); + + QTest::newRow("Invalid_vs_Invalid") << getQRectCase(InvalidQRect).toRectF() + << getQRectCase(InvalidQRect).toRectF() + << true << true << true; + + QTest::newRow("Null_vs_Null") << getQRectCase(NullQRect).toRectF() + << getQRectCase(NullQRect).toRectF() + << true << true << true; + + QTest::newRow("Empty_vs_Empty") << getQRectCase(EmptyQRect).toRectF() + << getQRectCase(EmptyQRect).toRectF() + << true << true << true; + + QTest::newRow("NegativeSize_vs_NegativeSize") << getQRectCase(NegativeSizeQRect).toRectF() + << getQRectCase(NegativeSizeQRect).toRectF() + << true << true << true; + + QTest::newRow("Invalid_vs_Null") << getQRectCase(InvalidQRect).toRectF() + << getQRectCase(NullQRect).toRectF() + << false << false << false; + + QTest::newRow("NearlySimilar") << QRectF(QPointF(1.1, 9.9), QPointF(9.9, 1.1)) + << QRectF(QPointF(1., 10.), QPointF(10., 1.)) + << true << false << true; + + QTest::newRow("WithQREAL_MIN") << QRectF(QPointF(0., -10.), QPointF(-1., 0.)) + << QRectF(QPointF(-qreal_min, -10.), QPointF(-1., qreal_min)) + << true << true << true; +} + +void tst_QRect::comparison() +{ + QFETCH(const QRectF, lhsF); + QFETCH(const QRectF, rhsF); + QFETCH(const bool, result); + QFETCH(const bool, floatResult); + QFETCH(const bool, mixedResult); + + const QRect lhs = lhsF.toRect(); + const QRect rhs = rhsF.toRect(); + + QT_TEST_EQUALITY_OPS(lhs, rhs, result); + QT_TEST_EQUALITY_OPS(lhsF, rhsF, floatResult); + QT_TEST_EQUALITY_OPS(lhs, rhsF, mixedResult); +} + +void tst_QRect::fuzzyComparison_data() +{ + comparison_data(); +} + +void tst_QRect::fuzzyComparison() +{ + QFETCH(const QRectF, lhsF); + QFETCH(const QRectF, rhsF); + QFETCH(const bool, floatResult); + + QCOMPARE_EQ(qFuzzyCompare(lhsF, rhsF), floatResult); +} + void tst_QRect::isNull_data() { QTest::addColumn<QRect>("r"); @@ -271,6 +355,14 @@ void tst_QRect::isNull() QVERIFY( rf.isNull() == isNull ); } +void tst_QRect::fuzzyIsNull() +{ + QRectF rf(QPointF(-qreal_min, qreal_min), QPointF(qreal_min, -qreal_min)); + + QVERIFY(!rf.isNull()); // QRectF::isNull() does strict comparison + QVERIFY(qFuzzyIsNull(rf)); +} + void tst_QRect::newIsEmpty_data() { QTest::addColumn<QRect>("r"); diff --git a/tests/auto/corelib/tools/qsize/CMakeLists.txt b/tests/auto/corelib/tools/qsize/CMakeLists.txt index 91de696ddd..4a4c96b52c 100644 --- a/tests/auto/corelib/tools/qsize/CMakeLists.txt +++ b/tests/auto/corelib/tools/qsize/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qsize SOURCES tst_qsize.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qsize/tst_qsize.cpp b/tests/auto/corelib/tools/qsize/tst_qsize.cpp index c9699c5e76..d379275dd8 100644 --- a/tests/auto/corelib/tools/qsize/tst_qsize.cpp +++ b/tests/auto/corelib/tools/qsize/tst_qsize.cpp @@ -24,6 +24,7 @@ CHECK(const &&); #undef CHECK #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <qsize.h> #include <array> @@ -34,6 +35,10 @@ class tst_QSize : public QObject { Q_OBJECT private slots: + void compareCompiles(); + void compare_data(); + void compare(); + void getSetCheck(); void scale(); @@ -55,6 +60,38 @@ private slots: void structuredBinding(); }; +void tst_QSize::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QSize>(); +} + +void tst_QSize::compare_data() +{ + QTest::addColumn<QSize>("lhs"); + QTest::addColumn<QSize>("rhs"); + QTest::addColumn<bool>("result"); + + auto row = [](QSize lhs, QSize rhs, bool res) { + QTest::addRow("(%d, %d) vs (%d, %d)", lhs.width(), lhs.height(), rhs.width(), rhs.height()) + << lhs << rhs << res; + }; + + row(QSize(0, 0), QSize(0, 0), true); + row(QSize(1, 0), QSize(0, 1), false); + row(QSize(-1, -1), QSize(-1, -1), true); + row(QSize(-1, -1), QSize(1, 1), false); + row(QSize(INT_MIN, INT_MAX), QSize(INT_MAX, INT_MIN), false); +} + +void tst_QSize::compare() +{ + QFETCH(QSize, lhs); + QFETCH(QSize, rhs); + QFETCH(bool, result); + + QT_TEST_EQUALITY_OPS(lhs, rhs, result); +} + // Testing get/set functions void tst_QSize::getSetCheck() { diff --git a/tests/auto/corelib/tools/qsizef/CMakeLists.txt b/tests/auto/corelib/tools/qsizef/CMakeLists.txt index 9adaafe2ea..d8a1c7f46e 100644 --- a/tests/auto/corelib/tools/qsizef/CMakeLists.txt +++ b/tests/auto/corelib/tools/qsizef/CMakeLists.txt @@ -14,4 +14,6 @@ endif() qt_internal_add_test(tst_qsizef SOURCES tst_qsizef.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp index ee33fa13b6..bb087e89de 100644 --- a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp +++ b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp @@ -24,17 +24,30 @@ CHECK(const &&); #undef CHECK #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <qsize.h> Q_DECLARE_METATYPE(QMarginsF) +static constexpr qreal qreal_min = std::numeric_limits<qreal>::min(); + class tst_QSizeF : public QObject { Q_OBJECT private slots: + void compareCompiles(); + void compare_data(); + void compare(); + + void fuzzyCompare_data(); + void fuzzyCompare(); + void isNull_data(); void isNull(); + void fuzzyIsNull_data(); + void fuzzyIsNull(); + void scale(); void expandedTo(); @@ -52,6 +65,61 @@ private slots: void structuredBinding(); }; +void tst_QSizeF::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QSizeF>(); + QTestPrivate::testEqualityOperatorsCompile<QSizeF, QSize>(); +} + +void tst_QSizeF::compare_data() +{ + QTest::addColumn<QSizeF>("lhs"); + QTest::addColumn<QSizeF>("rhs"); + QTest::addColumn<bool>("result"); + QTest::addColumn<bool>("mixedResult"); + + auto row = [&](QSizeF lhs, QSizeF rhs, bool res, bool mixedRes) { + QString str; + QDebug dbg(&str); + dbg.nospace() << "(" << lhs.width() << ", " << lhs.height() << ") vs " + << "(" << rhs.width() << ", " << rhs.height() << ")"; + QTest::addRow("%s", str.toLatin1().constData()) << lhs << rhs << res << mixedRes; + }; + + row(QSizeF(0.0, 0.0), QSizeF(0.0, 0.0), true, true); + row(QSizeF(1.0, 2.0), QSizeF(1.0, 2.0), true, true); + row(QSizeF(1.0, -1.0), QSizeF(-1.0, 1.0), false, false); + row(QSizeF(0.1, 1.1), QSizeF(0.1, 1.1), true, false); + row(QSizeF(qreal_min, 0.0), QSizeF(0.0, -qreal_min), true, true); +} + +void tst_QSizeF::compare() +{ + QFETCH(QSizeF, lhs); + QFETCH(QSizeF, rhs); + QFETCH(bool, result); + QFETCH(bool, mixedResult); + + QT_TEST_EQUALITY_OPS(lhs, rhs, result); + + const QSize rhsFixed = rhs.toSize(); + QT_TEST_EQUALITY_OPS(lhs, rhsFixed, mixedResult); +} + +void tst_QSizeF::fuzzyCompare_data() +{ + compare_data(); +} + +void tst_QSizeF::fuzzyCompare() +{ + QFETCH(QSizeF, lhs); + QFETCH(QSizeF, rhs); + QFETCH(bool, result); + + QCOMPARE_EQ(qFuzzyCompare(lhs, rhs), result); +} + void tst_QSizeF::isNull_data() { QTest::addColumn<qreal>("width"); @@ -66,6 +134,7 @@ void tst_QSizeF::isNull_data() QTest::newRow("0, -0.1") << qreal(0) << qreal(-0.1) << false; QTest::newRow("0.1, 0") << qreal(0.1) << qreal(0) << false; QTest::newRow("0, 0.1") << qreal(0) << qreal(0.1) << false; + QTest::newRow("qreal_min, -qreal_min") << qreal_min << -qreal_min << false; } void tst_QSizeF::isNull() @@ -80,6 +149,33 @@ void tst_QSizeF::isNull() QCOMPARE(size.isNull(), isNull); } +void tst_QSizeF::fuzzyIsNull_data() +{ + QTest::addColumn<qreal>("width"); + QTest::addColumn<qreal>("height"); + QTest::addColumn<bool>("fuzzyNull"); + + QTest::newRow("0, 0") << qreal(0.0) << qreal(0.0) << true; + QTest::newRow("-0, -0") << qreal(-0.0) << qreal(-0.0) << true; + QTest::newRow("0, -0") << qreal(0) << qreal(-0.0) << true; + QTest::newRow("-0, 0") << qreal(-0.0) << qreal(0) << true; + QTest::newRow("-0.1, 0") << qreal(-0.1) << qreal(0) << false; + QTest::newRow("0, -0.1") << qreal(0) << qreal(-0.1) << false; + QTest::newRow("0.1, 0") << qreal(0.1) << qreal(0) << false; + QTest::newRow("0, 0.1") << qreal(0) << qreal(0.1) << false; + QTest::newRow("qreal_min, -qreal_min") << qreal_min << -qreal_min << true; +} + +void tst_QSizeF::fuzzyIsNull() +{ + QFETCH(qreal, width); + QFETCH(qreal, height); + QFETCH(bool, fuzzyNull); + + QSizeF size(width, height); + QCOMPARE(qFuzzyIsNull(size), fuzzyNull); +} + void tst_QSizeF::scale() { QSizeF t1(10.4, 12.8); t1.scale(60.6, 60.6, Qt::IgnoreAspectRatio); diff --git a/tests/auto/corelib/tools/qspan/tst_qspan.cpp b/tests/auto/corelib/tools/qspan/tst_qspan.cpp index 91d2ecf739..c7456ac7f2 100644 --- a/tests/auto/corelib/tools/qspan/tst_qspan.cpp +++ b/tests/auto/corelib/tools/qspan/tst_qspan.cpp @@ -141,6 +141,9 @@ private: void from_variable_size_container_impl(C &&c) const; }; +template <typename T> +const void *as_const_void(T *p) noexcept { return static_cast<const void *>(p); } + #define RETURN_IF_FAILED() \ do { if (QTest::currentTestFailed()) return; } while (false) @@ -149,7 +152,7 @@ void tst_QSpan::onlyZeroExtentSpansHaveDefaultCtors() const static_assert(std::is_nothrow_default_constructible_v<QSpan<int, 0>>); static_assert(std::is_nothrow_default_constructible_v<QSpan<const int, 0>>); static_assert(std::is_nothrow_default_constructible_v<QSpan<int>>); - static_assert(std::is_nothrow_default_constructible_v<QSpan<const int, 0>>); + static_assert(std::is_nothrow_default_constructible_v<QSpan<const int>>); QSpan<int, 0> si; check_null_span(si); @@ -173,7 +176,7 @@ void tst_QSpan::onlyZeroExtentSpansHaveDefaultCtors() const void tst_QSpan::zeroExtentSpansMaintainADataPointer() const { - int i; + int i = 0; QSpan<int, 0> si{&i, 0}; QCOMPARE(si.data(), &i); check_empty_span_incl_subspans(si); @@ -334,6 +337,8 @@ void tst_QSpan::from_container_impl(C &&c) const const auto c_data = QSpanPrivate::adl_data(c); using V = std::remove_reference_t<QSpanPrivate::range_reference_t<C>>; + constexpr auto ExpectedBytesExtent + = ExpectedExtent == q20::dynamic_extent ? q20::dynamic_extent : ExpectedExtent * sizeof(V); { QSpan si = c; // CTAD static_assert(std::is_same_v<decltype(si), QSpan<V, ExpectedExtent>>); @@ -344,6 +349,20 @@ void tst_QSpan::from_container_impl(C &&c) const check_nonempty_span(si, c_size); RETURN_IF_FAILED(); + auto bi = as_bytes(si); + static_assert(std::is_same_v<decltype(bi), QSpan<const std::byte, ExpectedBytesExtent>>); + QCOMPARE_EQ(bi.size(), si.size_bytes()); + QCOMPARE_EQ(as_const_void(bi.data()), + as_const_void(si.data())); + + if constexpr (!std::is_const_v<V>) { // e.g. std::initializer_list<int> + auto wbi = as_writable_bytes(si); + static_assert(std::is_same_v<decltype(wbi), QSpan<std::byte, ExpectedBytesExtent>>); + QCOMPARE_EQ(wbi.size(), si.size_bytes()); + QCOMPARE_EQ(as_const_void(wbi.data()), + as_const_void(si.data())); + } + QSpan<const int> sci = c; QCOMPARE_EQ(sci.size(), c_size); @@ -351,6 +370,12 @@ void tst_QSpan::from_container_impl(C &&c) const check_nonempty_span(sci, c_size); RETURN_IF_FAILED(); + + auto bci = as_bytes(sci); + static_assert(std::is_same_v<decltype(bci), QSpan<const std::byte>>); + QCOMPARE_EQ(bci.size(), sci.size_bytes()); + QCOMPARE_EQ(as_const_void(bci.data()), + as_const_void(sci.data())); } { QSpan sci = std::as_const(c); // CTAD @@ -361,6 +386,12 @@ void tst_QSpan::from_container_impl(C &&c) const check_nonempty_span(sci, c_size); RETURN_IF_FAILED(); + + auto bci = as_bytes(sci); + static_assert(std::is_same_v<decltype(bci), QSpan<const std::byte, ExpectedBytesExtent>>); + QCOMPARE_EQ(bci.size(), sci.size_bytes()); + QCOMPARE_EQ(as_const_void(bci.data()), + as_const_void(sci.data())); } } diff --git a/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt b/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt index 8f6ed66841..5b9fa60c64 100644 --- a/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt +++ b/tests/auto/corelib/tools/qversionnumber/CMakeLists.txt @@ -14,6 +14,8 @@ endif() qt_internal_add_test(tst_qversionnumber SOURCES tst_qversionnumber.cpp + LIBRARIES + Qt::TestPrivate ) ## Scopes: diff --git a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp index da9dcc9366..5ccf56c1d1 100644 --- a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp +++ b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <QtCore/qversionnumber.h> #include <QtCore/qlibraryinfo.h> @@ -16,24 +17,15 @@ private: private slots: void initTestCase(); + void compareCompiles(); void constructorDefault(); void constructorVersioned_data(); void constructorVersioned(); void constructorExplicit(); void constructorCopy_data(); void constructorCopy(); - void compareGreater_data(); - void compareGreater(); - void compareGreaterEqual_data(); - void compareGreaterEqual(); - void compareLess_data(); - void compareLess(); - void compareLessEqual_data(); - void compareLessEqual(); - void compareEqual_data(); - void compareEqual(); - void compareNotEqual_data(); - void compareNotEqual(); + void comparisonOperators_data(); + void comparisonOperators(); void compare_data(); void compare(); void isPrefixOf_data(); @@ -121,74 +113,69 @@ void tst_QVersionNumber::comparisonData() { QTest::addColumn<QVersionNumber>("lhs"); QTest::addColumn<QVersionNumber>("rhs"); - QTest::addColumn<bool>("equal"); - QTest::addColumn<bool>("notEqual"); - QTest::addColumn<bool>("lessThan"); - QTest::addColumn<bool>("lessThanOrEqual"); - QTest::addColumn<bool>("greaterThan"); - QTest::addColumn<bool>("greaterThanOrEqual"); + QTest::addColumn<Qt::strong_ordering>("ordering"); QTest::addColumn<int>("compareResult"); QTest::addColumn<bool>("isPrefix"); QTest::addColumn<QVersionNumber>("common"); - // LHS RHS == != < <= > >= compareResult isPrefixOf commonPrefix - QTest::newRow("null, null") << QVersionNumber() << QVersionNumber() << true << false << false << true << false << true << 0 << true << QVersionNumber(); - QTest::newRow("null, 0") << QVersionNumber() << QVersionNumber(0) << false << true << true << true << false << false << -1 << true << QVersionNumber(); - QTest::newRow("0, null") << QVersionNumber(0) << QVersionNumber() << false << true << false << false << true << true << 1 << false << QVersionNumber(); - QTest::newRow("0, 0") << QVersionNumber(0) << QVersionNumber(0) << true << false << false << true << false << true << 0 << true << QVersionNumber(0); - QTest::newRow("1.0, 1.0") << QVersionNumber(1, 0) << QVersionNumber(1, 0) << true << false << false << true << false << true << 0 << true << QVersionNumber(1, 0); - QTest::newRow("1, 1.0") << QVersionNumber(1) << QVersionNumber(1, 0) << false << true << true << true << false << false << -1 << true << QVersionNumber(1); - QTest::newRow("1.0, 1") << QVersionNumber(1, 0) << QVersionNumber(1) << false << true << false << false << true << true << 1 << false << QVersionNumber(1); - - QTest::newRow("0.1.2, 0.1") << QVersionNumber(0, 1, 2) << QVersionNumber(0, 1) << false << true << false << false << true << true << 2 << false << QVersionNumber(0, 1); - QTest::newRow("0.1, 0.1.2") << QVersionNumber(0, 1) << QVersionNumber(0, 1, 2) << false << true << true << true << false << false << -2 << true << QVersionNumber(0, 1); - QTest::newRow("0.1.2, 0.1.2") << QVersionNumber(0, 1, 2) << QVersionNumber(0, 1, 2) << true << false << false << true << false << true << 0 << true << QVersionNumber(0, 1, 2); - QTest::newRow("0.1.2, 1.1.2") << QVersionNumber(0, 1, 2) << QVersionNumber(1, 1, 2) << false << true << true << true << false << false << -1 << false << QVersionNumber(); - QTest::newRow("1.1.2, 0.1.2") << QVersionNumber(1, 1, 2) << QVersionNumber(0, 1, 2) << false << true << false << false << true << true << 1 << false << QVersionNumber(); - QTest::newRow("1, -1") << QVersionNumber(1) << QVersionNumber(-1) << false << true << false << false << true << true << 2 << false << QVersionNumber(); - QTest::newRow("-1, 1") << QVersionNumber(-1) << QVersionNumber(1) << false << true << true << true << false << false << -2 << false << QVersionNumber(); - QTest::newRow("0.1, 0.-1") << QVersionNumber(0, 1) << QVersionNumber(0, -1) << false << true << false << false << true << true << 2 << false << QVersionNumber(0); - QTest::newRow("0.-1, 0.1") << QVersionNumber(0, -1) << QVersionNumber(0, 1) << false << true << true << true << false << false << -2 << false << QVersionNumber(0); - QTest::newRow("0.-1, 0") << QVersionNumber(0, -1) << QVersionNumber(0) << false << true << true << true << false << false << -1 << false << QVersionNumber(0); - QTest::newRow("0, 0.-1") << QVersionNumber(0) << QVersionNumber(0, -1) << false << true << false << false << true << true << 1 << true << QVersionNumber(0); - - QTest::newRow("0.127.2, 0.127") << QVersionNumber(0, 127, 2) << QVersionNumber(0, 127) << false << true << false << false << true << true << 2 << false << QVersionNumber(0, 127); - QTest::newRow("0.127, 0.127.2") << QVersionNumber(0, 127) << QVersionNumber(0, 127, 2) << false << true << true << true << false << false << -2 << true << QVersionNumber(0, 127); - QTest::newRow("0.127.2, 0.127.2") << QVersionNumber(0, 127, 2) << QVersionNumber(0, 127, 2) << true << false << false << true << false << true << 0 << true << QVersionNumber(0, 127, 2); - QTest::newRow("0.127.2, 127.127.2") << QVersionNumber(0, 127, 2) << QVersionNumber(127, 127, 2) << false << true << true << true << false << false << -127 << false << QVersionNumber(); - QTest::newRow("127.127.2, 0.127.2") << QVersionNumber(127, 127, 2) << QVersionNumber(0, 127, 2) << false << true << false << false << true << true << 127 << false << QVersionNumber(); - QTest::newRow("127, -128") << QVersionNumber(127) << QVersionNumber(-128) << false << true << false << false << true << true << 255 << false << QVersionNumber(); - QTest::newRow("-128, 127") << QVersionNumber(-128) << QVersionNumber(127) << false << true << true << true << false << false << -255 << false << QVersionNumber(); - QTest::newRow("0.127, 0.-128") << QVersionNumber(0, 127) << QVersionNumber(0, -128) << false << true << false << false << true << true << 255 << false << QVersionNumber(0); - QTest::newRow("0.-128, 0.127") << QVersionNumber(0, -128) << QVersionNumber(0, 127) << false << true << true << true << false << false << -255 << false << QVersionNumber(0); - QTest::newRow("0.-128, 0") << QVersionNumber(0, -128) << QVersionNumber(0) << false << true << true << true << false << false << -128 << false << QVersionNumber(0); - QTest::newRow("0, 0.-128") << QVersionNumber(0) << QVersionNumber(0, -128) << false << true << false << false << true << true << 128 << true << QVersionNumber(0); - - QTest::newRow("0.128.2, 0.128") << QVersionNumber(0, 128, 2) << QVersionNumber(0, 128) << false << true << false << false << true << true << 2 << false << QVersionNumber(0, 128); - QTest::newRow("0.128, 0.128.2") << QVersionNumber(0, 128) << QVersionNumber(0, 128, 2) << false << true << true << true << false << false << -2 << true << QVersionNumber(0, 128); - QTest::newRow("0.128.2, 0.128.2") << QVersionNumber(0, 128, 2) << QVersionNumber(0, 128, 2) << true << false << false << true << false << true << 0 << true << QVersionNumber(0, 128, 2); - QTest::newRow("0.128.2, 128.128.2") << QVersionNumber(0, 128, 2) << QVersionNumber(128, 128, 2) << false << true << true << true << false << false << -128 << false << QVersionNumber(); - QTest::newRow("128.128.2, 0.128.2") << QVersionNumber(128, 128, 2) << QVersionNumber(0, 128, 2) << false << true << false << false << true << true << 128 << false << QVersionNumber(); - QTest::newRow("128, -129") << QVersionNumber(128) << QVersionNumber(-129) << false << true << false << false << true << true << 257 << false << QVersionNumber(); - QTest::newRow("-129, 128") << QVersionNumber(-129) << QVersionNumber(128) << false << true << true << true << false << false << -257 << false << QVersionNumber(); - QTest::newRow("0.128, 0.-129") << QVersionNumber(0, 128) << QVersionNumber(0, -129) << false << true << false << false << true << true << 257 << false << QVersionNumber(0); - QTest::newRow("0.-129, 0.128") << QVersionNumber(0, -129) << QVersionNumber(0, 128) << false << true << true << true << false << false << -257 << false << QVersionNumber(0); - QTest::newRow("0.-129, 0") << QVersionNumber(0, -129) << QVersionNumber(0) << false << true << true << true << false << false << -129 << false << QVersionNumber(0); - QTest::newRow("0, 0.-129") << QVersionNumber(0) << QVersionNumber(0, -129) << false << true << false << false << true << true << 129 << true << QVersionNumber(0); + // LHS RHS ordering compareResult isPrefixOf commonPrefix + QTest::newRow("null, null") << QVersionNumber() << QVersionNumber() << Qt::strong_ordering::equal << 0 << true << QVersionNumber(); + QTest::newRow("null, 0") << QVersionNumber() << QVersionNumber(0) << Qt::strong_ordering::less << -1 << true << QVersionNumber(); + QTest::newRow("0, null") << QVersionNumber(0) << QVersionNumber() << Qt::strong_ordering::greater << 1 << false << QVersionNumber(); + QTest::newRow("0, 0") << QVersionNumber(0) << QVersionNumber(0) << Qt::strong_ordering::equal << 0 << true << QVersionNumber(0); + QTest::newRow("1.0, 1.0") << QVersionNumber(1, 0) << QVersionNumber(1, 0) << Qt::strong_ordering::equal << 0 << true << QVersionNumber(1, 0); + QTest::newRow("1, 1.0") << QVersionNumber(1) << QVersionNumber(1, 0) << Qt::strong_ordering::less << -1 << true << QVersionNumber(1); + QTest::newRow("1.0, 1") << QVersionNumber(1, 0) << QVersionNumber(1) << Qt::strong_ordering::greater << 1 << false << QVersionNumber(1); + + QTest::newRow("0.1.2, 0.1") << QVersionNumber(0, 1, 2) << QVersionNumber(0, 1) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(0, 1); + QTest::newRow("0.1, 0.1.2") << QVersionNumber(0, 1) << QVersionNumber(0, 1, 2) << Qt::strong_ordering::less << -2 << true << QVersionNumber(0, 1); + QTest::newRow("0.1.2, 0.1.2") << QVersionNumber(0, 1, 2) << QVersionNumber(0, 1, 2) << Qt::strong_ordering::equal << 0 << true << QVersionNumber(0, 1, 2); + QTest::newRow("0.1.2, 1.1.2") << QVersionNumber(0, 1, 2) << QVersionNumber(1, 1, 2) << Qt::strong_ordering::less << -1 << false << QVersionNumber(); + QTest::newRow("1.1.2, 0.1.2") << QVersionNumber(1, 1, 2) << QVersionNumber(0, 1, 2) << Qt::strong_ordering::greater << 1 << false << QVersionNumber(); + QTest::newRow("1, -1") << QVersionNumber(1) << QVersionNumber(-1) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(); + QTest::newRow("-1, 1") << QVersionNumber(-1) << QVersionNumber(1) << Qt::strong_ordering::less << -2 << false << QVersionNumber(); + QTest::newRow("0.1, 0.-1") << QVersionNumber(0, 1) << QVersionNumber(0, -1) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(0); + QTest::newRow("0.-1, 0.1") << QVersionNumber(0, -1) << QVersionNumber(0, 1) << Qt::strong_ordering::less << -2 << false << QVersionNumber(0); + QTest::newRow("0.-1, 0") << QVersionNumber(0, -1) << QVersionNumber(0) << Qt::strong_ordering::less << -1 << false << QVersionNumber(0); + QTest::newRow("0, 0.-1") << QVersionNumber(0) << QVersionNumber(0, -1) << Qt::strong_ordering::greater << 1 << true << QVersionNumber(0); + + QTest::newRow("0.127.2, 0.127") << QVersionNumber(0, 127, 2) << QVersionNumber(0, 127) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(0, 127); + QTest::newRow("0.127, 0.127.2") << QVersionNumber(0, 127) << QVersionNumber(0, 127, 2) << Qt::strong_ordering::less << -2 << true << QVersionNumber(0, 127); + QTest::newRow("0.127.2, 0.127.2") << QVersionNumber(0, 127, 2) << QVersionNumber(0, 127, 2) << Qt::strong_ordering::equal << 0 << true << QVersionNumber(0, 127, 2); + QTest::newRow("0.127.2, 127.127.2") << QVersionNumber(0, 127, 2) << QVersionNumber(127, 127, 2) << Qt::strong_ordering::less << -127 << false << QVersionNumber(); + QTest::newRow("127.127.2, 0.127.2") << QVersionNumber(127, 127, 2) << QVersionNumber(0, 127, 2) << Qt::strong_ordering::greater << 127 << false << QVersionNumber(); + QTest::newRow("127, -128") << QVersionNumber(127) << QVersionNumber(-128) << Qt::strong_ordering::greater << 255 << false << QVersionNumber(); + QTest::newRow("-128, 127") << QVersionNumber(-128) << QVersionNumber(127) << Qt::strong_ordering::less << -255 << false << QVersionNumber(); + QTest::newRow("0.127, 0.-128") << QVersionNumber(0, 127) << QVersionNumber(0, -128) << Qt::strong_ordering::greater << 255 << false << QVersionNumber(0); + QTest::newRow("0.-128, 0.127") << QVersionNumber(0, -128) << QVersionNumber(0, 127) << Qt::strong_ordering::less << -255 << false << QVersionNumber(0); + QTest::newRow("0.-128, 0") << QVersionNumber(0, -128) << QVersionNumber(0) << Qt::strong_ordering::less << -128 << false << QVersionNumber(0); + QTest::newRow("0, 0.-128") << QVersionNumber(0) << QVersionNumber(0, -128) << Qt::strong_ordering::greater << 128 << true << QVersionNumber(0); + + QTest::newRow("0.128.2, 0.128") << QVersionNumber(0, 128, 2) << QVersionNumber(0, 128) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(0, 128); + QTest::newRow("0.128, 0.128.2") << QVersionNumber(0, 128) << QVersionNumber(0, 128, 2) << Qt::strong_ordering::less << -2 << true << QVersionNumber(0, 128); + QTest::newRow("0.128.2, 0.128.2") << QVersionNumber(0, 128, 2) << QVersionNumber(0, 128, 2) << Qt::strong_ordering::equal << 0 << true << QVersionNumber(0, 128, 2); + QTest::newRow("0.128.2, 128.128.2") << QVersionNumber(0, 128, 2) << QVersionNumber(128, 128, 2) << Qt::strong_ordering::less << -128 << false << QVersionNumber(); + QTest::newRow("128.128.2, 0.128.2") << QVersionNumber(128, 128, 2) << QVersionNumber(0, 128, 2) << Qt::strong_ordering::greater << 128 << false << QVersionNumber(); + QTest::newRow("128, -129") << QVersionNumber(128) << QVersionNumber(-129) << Qt::strong_ordering::greater << 257 << false << QVersionNumber(); + QTest::newRow("-129, 128") << QVersionNumber(-129) << QVersionNumber(128) << Qt::strong_ordering::less << -257 << false << QVersionNumber(); + QTest::newRow("0.128, 0.-129") << QVersionNumber(0, 128) << QVersionNumber(0, -129) << Qt::strong_ordering::greater << 257 << false << QVersionNumber(0); + QTest::newRow("0.-129, 0.128") << QVersionNumber(0, -129) << QVersionNumber(0, 128) << Qt::strong_ordering::less << -257 << false << QVersionNumber(0); + QTest::newRow("0.-129, 0") << QVersionNumber(0, -129) << QVersionNumber(0) << Qt::strong_ordering::less << -129 << false << QVersionNumber(0); + QTest::newRow("0, 0.-129") << QVersionNumber(0) << QVersionNumber(0, -129) << Qt::strong_ordering::greater << 129 << true << QVersionNumber(0); const QList<int> common = QList<int>({ 0, 1, 2, 3, 4, 5, 6 }); using namespace UglyOperator; - QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.0.1") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 0 + 1) << false << true << false << false << true << true << 2 << false << QVersionNumber(common + 0 + 1); - QTest::newRow("0.1.2.3.4.5.6.0.1, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 0 + 1) << QVersionNumber(common + 0 + 1 + 2) << false << true << true << true << false << false << -2 << true << QVersionNumber(common + 0 + 1); - QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 0 + 1 + 2) << true << false << false << true << false << true << 0 << true << QVersionNumber(common + 0 + 1 + 2); - QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.1.1.2") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 1 + 1 + 2) << false << true << true << true << false << false << -1 << false << QVersionNumber(common); - QTest::newRow("0.1.2.3.4.5.6.1.1.2, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 1 + 1 + 2) << QVersionNumber(common + 0 + 1 + 2) << false << true << false << false << true << true << 1 << false << QVersionNumber(common); - QTest::newRow("0.1.2.3.4.5.6.1, 0.1.2.3.4.5.6.-1") << QVersionNumber(common + 1) << QVersionNumber(common + -1) << false << true << false << false << true << true << 2 << false << QVersionNumber(common); - QTest::newRow("0.1.2.3.4.5.6.-1, 0.1.2.3.4.5.6.1") << QVersionNumber(common + -1) << QVersionNumber(common + 1) << false << true << true << true << false << false << -2 << false << QVersionNumber(common); - QTest::newRow("0.1.2.3.4.5.6.0.1, 0.1.2.3.4.5.6.0.-1") << QVersionNumber(common + 0 + 1) << QVersionNumber(common + 0 + -1) << false << true << false << false << true << true << 2 << false << QVersionNumber(common + 0); - QTest::newRow("0.1.2.3.4.5.6.0.-1, 0.1.2.3.4.5.6.0.1") << QVersionNumber(common + 0 + -1) << QVersionNumber(common + 0 + 1) << false << true << true << true << false << false << -2 << false << QVersionNumber(common + 0); - QTest::newRow("0.1.2.3.4.5.6.0.-1, 0.1.2.3.4.5.6.0") << QVersionNumber(common + 0 + -1) << QVersionNumber(common + 0) << false << true << true << true << false << false << -1 << false << QVersionNumber(common + 0); - QTest::newRow("0.1.2.3.4.5.6.0, 0.1.2.3.4.5.6.0.-1") << QVersionNumber(common + 0) << QVersionNumber(common + 0 + -1) << false << true << false << false << true << true << 1 << true << QVersionNumber(common + 0); + QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.0.1") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 0 + 1) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(common + 0 + 1); + QTest::newRow("0.1.2.3.4.5.6.0.1, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 0 + 1) << QVersionNumber(common + 0 + 1 + 2) << Qt::strong_ordering::less << -2 << true << QVersionNumber(common + 0 + 1); + QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 0 + 1 + 2) << Qt::strong_ordering::equal << 0 << true << QVersionNumber(common + 0 + 1 + 2); + QTest::newRow("0.1.2.3.4.5.6.0.1.2, 0.1.2.3.4.5.6.1.1.2") << QVersionNumber(common + 0 + 1 + 2) << QVersionNumber(common + 1 + 1 + 2) << Qt::strong_ordering::less << -1 << false << QVersionNumber(common); + QTest::newRow("0.1.2.3.4.5.6.1.1.2, 0.1.2.3.4.5.6.0.1.2") << QVersionNumber(common + 1 + 1 + 2) << QVersionNumber(common + 0 + 1 + 2) << Qt::strong_ordering::greater << 1 << false << QVersionNumber(common); + QTest::newRow("0.1.2.3.4.5.6.1, 0.1.2.3.4.5.6.-1") << QVersionNumber(common + 1) << QVersionNumber(common + -1) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(common); + QTest::newRow("0.1.2.3.4.5.6.-1, 0.1.2.3.4.5.6.1") << QVersionNumber(common + -1) << QVersionNumber(common + 1) << Qt::strong_ordering::less << -2 << false << QVersionNumber(common); + QTest::newRow("0.1.2.3.4.5.6.0.1, 0.1.2.3.4.5.6.0.-1") << QVersionNumber(common + 0 + 1) << QVersionNumber(common + 0 + -1) << Qt::strong_ordering::greater << 2 << false << QVersionNumber(common + 0); + QTest::newRow("0.1.2.3.4.5.6.0.-1, 0.1.2.3.4.5.6.0.1") << QVersionNumber(common + 0 + -1) << QVersionNumber(common + 0 + 1) << Qt::strong_ordering::less << -2 << false << QVersionNumber(common + 0); + QTest::newRow("0.1.2.3.4.5.6.0.-1, 0.1.2.3.4.5.6.0") << QVersionNumber(common + 0 + -1) << QVersionNumber(common + 0) << Qt::strong_ordering::less << -1 << false << QVersionNumber(common + 0); + QTest::newRow("0.1.2.3.4.5.6.0, 0.1.2.3.4.5.6.0.-1") << QVersionNumber(common + 0) << QVersionNumber(common + 0 + -1) << Qt::strong_ordering::greater << 1 << true << QVersionNumber(common + 0); } void tst_QVersionNumber::initTestCase() @@ -196,6 +183,11 @@ void tst_QVersionNumber::initTestCase() qRegisterMetaType<QList<int>>(); } +void tst_QVersionNumber::compareCompiles() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QVersionNumber>(); +} + void tst_QVersionNumber::constructorDefault() { QVersionNumber version; @@ -270,88 +262,18 @@ void tst_QVersionNumber::constructorCopy() QCOMPARE(version.segments(), expectedVersion.segments()); } -void tst_QVersionNumber::compareGreater_data() -{ - comparisonData(); -} - -void tst_QVersionNumber::compareGreater() -{ - QFETCH(QVersionNumber, lhs); - QFETCH(QVersionNumber, rhs); - QFETCH(bool, greaterThan); - - QCOMPARE(lhs > rhs, greaterThan); -} - -void tst_QVersionNumber::compareGreaterEqual_data() -{ - comparisonData(); -} - -void tst_QVersionNumber::compareGreaterEqual() -{ - QFETCH(QVersionNumber, lhs); - QFETCH(QVersionNumber, rhs); - QFETCH(bool, greaterThanOrEqual); - - QCOMPARE(lhs >= rhs, greaterThanOrEqual); -} - -void tst_QVersionNumber::compareLess_data() -{ - comparisonData(); -} - -void tst_QVersionNumber::compareLess() -{ - QFETCH(QVersionNumber, lhs); - QFETCH(QVersionNumber, rhs); - QFETCH(bool, lessThan); - - QCOMPARE(lhs < rhs, lessThan); -} - -void tst_QVersionNumber::compareLessEqual_data() -{ - comparisonData(); -} - -void tst_QVersionNumber::compareLessEqual() -{ - QFETCH(QVersionNumber, lhs); - QFETCH(QVersionNumber, rhs); - QFETCH(bool, lessThanOrEqual); - - QCOMPARE(lhs <= rhs, lessThanOrEqual); -} - -void tst_QVersionNumber::compareEqual_data() -{ - comparisonData(); -} - -void tst_QVersionNumber::compareEqual() -{ - QFETCH(QVersionNumber, lhs); - QFETCH(QVersionNumber, rhs); - QFETCH(bool, equal); - - QCOMPARE(lhs == rhs, equal); -} - -void tst_QVersionNumber::compareNotEqual_data() +void tst_QVersionNumber::comparisonOperators_data() { comparisonData(); } -void tst_QVersionNumber::compareNotEqual() +void tst_QVersionNumber::comparisonOperators() { QFETCH(QVersionNumber, lhs); QFETCH(QVersionNumber, rhs); - QFETCH(bool, notEqual); + QFETCH(Qt::strong_ordering, ordering); - QCOMPARE(lhs != rhs, notEqual); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs, ordering); } void tst_QVersionNumber::compare_data() @@ -394,7 +316,7 @@ void tst_QVersionNumber::commonPrefix() QFETCH(QVersionNumber, common); QVersionNumber calculatedPrefix = QVersionNumber::commonPrefix(lhs, rhs); - QCOMPARE(calculatedPrefix, common); + QT_TEST_EQUALITY_OPS(calculatedPrefix, common, true); QCOMPARE(calculatedPrefix.segments(), common.segments()); } @@ -530,18 +452,18 @@ void tst_QVersionNumber::fromString_extra() // when passing explicit nullptr: { auto v = QVersionNumber::fromString("1.2.3-rc1", nullptr); - QCOMPARE(v, QVersionNumber({1, 2, 3})); + QT_TEST_EQUALITY_OPS(v, QVersionNumber({1, 2, 3}), true); } { auto v = QVersionNumber::fromString("1.2.3-rc1", 0); - QCOMPARE(v, QVersionNumber({1, 2, 3})); + QT_TEST_EQUALITY_OPS(v, QVersionNumber({1, 2, 3}), true); } // check the UTF16->L1 conversion isn't doing something weird { qsizetype i = -1; auto v = QVersionNumber::fromString(u"1.0ı", &i); // LATIN SMALL LETTER DOTLESS I - QCOMPARE(v, QVersionNumber(1, 0)); + QT_TEST_EQUALITY_OPS(v, QVersionNumber(1, 0), true); QCOMPARE(i, 3); } } @@ -652,14 +574,14 @@ void tst_QVersionNumber::moveSemantics() { QVersionNumber v1(1, 2, 3); QVersionNumber v2 = std::move(v1); - QCOMPARE(v2, QVersionNumber(1, 2, 3)); + QT_TEST_EQUALITY_OPS(v2, QVersionNumber(1, 2, 3), true); } // QVersionNumber &operator=(QVersionNumber &&) { QVersionNumber v1(1, 2, 3); QVersionNumber v2; v2 = std::move(v1); - QCOMPARE(v2, QVersionNumber(1, 2, 3)); + QT_TEST_EQUALITY_OPS(v2, QVersionNumber(1, 2, 3), true); } // QVersionNumber(QList<int> &&) { @@ -668,7 +590,7 @@ void tst_QVersionNumber::moveSemantics() QVersionNumber v2(std::move(segments)); QVERIFY(!v1.isNull()); QVERIFY(!v2.isNull()); - QCOMPARE(v1, v2); + QT_TEST_EQUALITY_OPS(v1, v2, true); } #ifdef Q_COMPILER_REF_QUALIFIERS // normalized() diff --git a/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp b/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp index 504d1a4fea..ab750dff33 100644 --- a/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp +++ b/tests/auto/dbus/qdbusconnection/tst_qdbusconnection.cpp @@ -1422,7 +1422,7 @@ void tst_QDBusConnection::connectionLimit() QProcess daemon; daemon.start("dbus-daemon", - QStringList() << "--config-file" << QFINDTESTDATA("tst_qdbusconnection.conf") + QStringList() << "--config-file" << QFINDTESTDATA("../qdbusconnection/tst_qdbusconnection.conf") << "--nofork" << "--print-address"); QVERIFY2(daemon.waitForReadyRead(2000), diff --git a/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp b/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp index e7a8273115..355a65bc59 100644 --- a/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp +++ b/tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp @@ -161,7 +161,9 @@ void basicStringTypes_data() { QTest::newRow("string") << QVariant("ping") << "s" << "\"ping\""; QTest::newRow("objectpath") << QVariant::fromValue(QDBusObjectPath("/org/kde")) << "o" << "[ObjectPath: /org/kde]"; + QTest::newRow("emptysignature") << QVariant::fromValue(QDBusSignature(QString())) << "g" << "[Signature: ]"; QTest::newRow("signature") << QVariant::fromValue(QDBusSignature("g")) << "g" << "[Signature: g]"; + QTest::newRow("multisignature") << QVariant::fromValue(QDBusSignature("bit")) << "g" << "[Signature: bit]"; QTest::newRow("emptystring") << QVariant("") << "s" << "\"\""; QTest::newRow("nullstring") << QVariant(QString()) << "s" << "\"\""; } @@ -907,7 +909,7 @@ void tst_QDBusMarshall::sendSignalErrors() QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: error: could not send signal to service \"\" path \"/foo\" interface \"local.interfaceName\" member \"signalName\": Marshalling failed: Invalid object path passed in arguments"); QVERIFY(!con.send(msg)); - QDBusSignature sig; + QDBusSignature sig(QChar(0)); msg.setArguments(QVariantList() << QVariant::fromValue(sig)); QTest::ignoreMessage(QtWarningMsg, "QDBusConnection: error: could not send signal to service \"\" path \"/foo\" interface \"local.interfaceName\" member \"signalName\": Marshalling failed: Invalid signature passed in arguments"); QVERIFY(!con.send(msg)); @@ -992,7 +994,7 @@ void tst_QDBusMarshall::sendCallErrors_data() << ""; QTest::newRow("invalid-signature-arg") << serviceName << objectPath << interfaceName << "ping" - << (QVariantList() << QVariant::fromValue(QDBusSignature())) + << (QVariantList() << QVariant::fromValue(QDBusSignature(QChar(0)))) << "org.freedesktop.DBus.Error.Failed" << "Marshalling failed: Invalid signature passed in arguments" << ""; diff --git a/tests/auto/dbus/qdbustype/tst_qdbustype.cpp b/tests/auto/dbus/qdbustype/tst_qdbustype.cpp index f4ad4cb77a..63cb7d4a65 100644 --- a/tests/auto/dbus/qdbustype/tst_qdbustype.cpp +++ b/tests/auto/dbus/qdbustype/tst_qdbustype.cpp @@ -206,6 +206,7 @@ void tst_QDBusType::isValidBasicType() void tst_QDBusType::isValidSingleSignature_data() { addColumns(); + QTest::newRow("empty") << "" << false; addSingleSignatures(); addNakedDictEntry(); } @@ -222,6 +223,7 @@ void tst_QDBusType::isValidSingleSignature() void tst_QDBusType::isValidArray_data() { addColumns(); + QTest::newRow("empty") << "" << false; addSingleSignatures(); } @@ -241,7 +243,10 @@ void tst_QDBusType::isValidArray() void tst_QDBusType::isValidSignature_data() { - isValidSingleSignature_data(); + addColumns(); + QTest::newRow("empty") << "" << true; + addSingleSignatures(); + addNakedDictEntry(); } void tst_QDBusType::isValidSignature() @@ -250,8 +255,10 @@ void tst_QDBusType::isValidSignature() QFETCH(bool, result); data.append(data); - if (data.at(0).unicode()) - QCOMPARE(bool(q_dbus_signature_validate(data.toLatin1(), 0)), result); + if (!data.isEmpty() && data.at(0).unicode()) { + // libdbus-1 API can't deal with string containing NULs + QCOMPARE(bool(q_dbus_signature_validate(data.toLatin1(), nullptr)), result); + } QCOMPARE(QDBusUtil::isValidSignature(data), result); } diff --git a/tests/auto/gui/image/CMakeLists.txt b/tests/auto/gui/image/CMakeLists.txt index 3535d5f01e..c7fc6f07d3 100644 --- a/tests/auto/gui/image/CMakeLists.txt +++ b/tests/auto/gui/image/CMakeLists.txt @@ -23,7 +23,4 @@ if(QT_FEATURE_private_tests) add_subdirectory(qpixmapcache) endif() -# QTBUG-87669 -if(NOT ANDROID) - add_subdirectory(qicon) -endif() +add_subdirectory(qicon) diff --git a/tests/auto/gui/image/qicon/CMakeLists.txt b/tests/auto/gui/image/qicon/CMakeLists.txt index c693c559cc..93f75741c0 100644 --- a/tests/auto/gui/image/qicon/CMakeLists.txt +++ b/tests/auto/gui/image/qicon/CMakeLists.txt @@ -37,11 +37,14 @@ file(GLOB_RECURSE test_data_glob *.svgz) list(APPEND test_data ${test_data_glob}) +add_subdirectory(plugin) + qt_internal_add_test(tst_qicon SOURCES tst_qicon.cpp LIBRARIES Qt::Gui + TestIconPlugin TESTDATA ${test_data} ) @@ -105,3 +108,5 @@ qt_internal_extend_target(tst_qicon CONDITION TARGET Qt::Widgets LIBRARIES Qt::Widgets ) + +add_dependencies(tst_qicon TestIconPlugin) diff --git a/tests/auto/gui/image/qicon/plugin/CMakeLists.txt b/tests/auto/gui/image/qicon/plugin/CMakeLists.txt new file mode 100644 index 0000000000..cae49b2df1 --- /dev/null +++ b/tests/auto/gui/image/qicon/plugin/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## TestIconEngine Plugin: +##################################################################### + +qt_internal_add_plugin(TestIconPlugin + STATIC + OUTPUT_NAME qtesticonplugin + OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" + SKIP_INSTALL + PLUGIN_TYPE iconengines + DEFAULT_IF TRUE + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui +) diff --git a/tests/auto/gui/image/qicon/plugin/main.cpp b/tests/auto/gui/image/qicon/plugin/main.cpp new file mode 100644 index 0000000000..58d9807142 --- /dev/null +++ b/tests/auto/gui/image/qicon/plugin/main.cpp @@ -0,0 +1,73 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <qiconengineplugin.h> +#include <qiconengine.h> + +QT_BEGIN_NAMESPACE + +class TestIconPlugin : public QIconEnginePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QIconEngineFactoryInterface" FILE "plugin.json") + +public: + QIconEngine *create(const QString &icon) override; +}; + +class TestIconEngine : public QIconEngine +{ +public: + TestIconEngine(const QString &icon) + : m_iconName(QIcon::themeName() + "/" + icon) + { + } + + ~TestIconEngine() + {} + + QIconEngine *clone() const override + { + return new TestIconEngine(m_iconName); + } + + QString key() const override + { + return QStringLiteral("TestIconEngine"); + } + + QString iconName() override + { + return m_iconName; + } + + bool isNull() override + { + return m_iconName.isNull(); + } + + QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override + { + return {{16, 16}, {48, 48}, {64, 64}}; + } + + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override + { + Q_UNUSED(painter); + Q_UNUSED(rect); + Q_UNUSED(mode); + Q_UNUSED(state); + } + +private: + const QString m_iconName; +}; + +QIconEngine *TestIconPlugin::create(const QString &icon) +{ + return new TestIconEngine(icon); +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/tests/auto/gui/image/qicon/plugin/plugin.json b/tests/auto/gui/image/qicon/plugin/plugin.json new file mode 100644 index 0000000000..96b59aa79e --- /dev/null +++ b/tests/auto/gui/image/qicon/plugin/plugin.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "plugintheme", "SpecialTheme" ] +} diff --git a/tests/auto/gui/image/qicon/tst_qicon.cpp b/tests/auto/gui/image/qicon/tst_qicon.cpp index 99b4a0589e..d95ee66fb6 100644 --- a/tests/auto/gui/image/qicon/tst_qicon.cpp +++ b/tests/auto/gui/image/qicon/tst_qicon.cpp @@ -48,6 +48,9 @@ private slots: #endif void task223279_inconsistentAddFile(); + void themeFromPlugin_data(); + void themeFromPlugin(); + private: bool haveImageFormat(QByteArray const&); @@ -872,6 +875,32 @@ void tst_QIcon::task223279_inconsistentAddFile() QCOMPARE(pm1.size(), pm2.size()); } +Q_IMPORT_PLUGIN(TestIconPlugin) + +void tst_QIcon::themeFromPlugin_data() +{ + QTest::addColumn<QString>("themeName"); + + QTest::addRow("plugintheme") << "plugintheme"; + QTest::addRow("specialtheme") << "specialTheme"; // deliberately not matching case +} + +void tst_QIcon::themeFromPlugin() +{ + QFETCH(const QString, themeName); + auto restoreTheme = qScopeGuard([oldTheme = QIcon::themeName()]{ + QIcon::setThemeName(oldTheme); + }); + + QIcon icon = QIcon::fromTheme("icon1"); + QVERIFY(icon.isNull()); + + QIcon::setThemeName(themeName); + + icon = QIcon::fromTheme("icon1"); + QVERIFY(!icon.isNull()); + QCOMPARE(icon.name(), themeName + "/icon1"); +} QTEST_MAIN(tst_QIcon) #include "tst_qicon.moc" diff --git a/tests/auto/gui/image/qimage/images/VideoHD.icc b/tests/auto/gui/image/qimage/images/VideoHD.icc Binary files differnew file mode 100644 index 0000000000..b96eb68136 --- /dev/null +++ b/tests/auto/gui/image/qimage/images/VideoHD.icc diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index 3e3d0a49bc..8086ffcc28 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -15,6 +15,7 @@ #include <stdio.h> #include <qpainter.h> +#include <private/qcmyk_p.h> #include <private/qimage_p.h> #include <private/qdrawhelper_p.h> @@ -110,6 +111,8 @@ private slots: void smoothScaleFormats(); void smoothScaleNoConversion_data(); void smoothScaleNoConversion(); + void smoothScale_CMYK_data(); + void smoothScale_CMYK(); void transformed_data(); void transformed(); @@ -173,6 +176,10 @@ private slots: void colorSpaceRgbConversion(); void colorSpaceCmykConversion_data(); void colorSpaceCmykConversion(); + void colorSpaceFromGrayConversion_data(); + void colorSpaceFromGrayConversion(); + void colorSpaceToGrayConversion_data(); + void colorSpaceToGrayConversion(); void deepCopyWhenPaintingActive(); void scaled_QTBUG19157(); @@ -2091,6 +2098,76 @@ void tst_QImage::smoothScaleNoConversion() QVERIFY(img.hasAlphaChannel()); } +void tst_QImage::smoothScale_CMYK_data() +{ + QTest::addColumn<int>("size"); + + const int sizes[] = { 2, 3, 4, 6, 7, 8, 10, 16, 20, 32, 40, 64, 100, 101, 128 }; + for (int size : sizes) + QTest::addRow("%d x %d", size, size) << size; +} + +void tst_QImage::smoothScale_CMYK() +{ + QFETCH(int, size); + QImage img(size, size, QImage::Format_CMYK8888); + QCmyk32 expected(31, 63, 127, 127); + img.fill(expected.toUint()); + + auto getCmykPixel = [](const QImage &image, int x, int y) { + Q_ASSERT(image.format() == QImage::Format_CMYK8888); + const uint *line = reinterpret_cast<const uint *>(image.scanLine(y)); + const uint pixel = line[x]; + return QCmyk32::fromCmyk32(pixel); + }; + + // scale x down, y down + QImage scaled = img.scaled(QSize(1, 1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + QCmyk32 pixel = getCmykPixel(scaled, 0, 0); + QCOMPARE(pixel, expected); + + // scale x down, y up + scaled = img.scaled(QSize(1, size * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + for (int y = 0; y < scaled.height(); ++y) { + pixel = getCmykPixel(scaled, 0, y); + QCOMPARE(pixel, expected); + } + + // scale x up, y down + scaled = img.scaled(QSize(size * 2, 1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + for (int x = 0; x < scaled.width(); ++x) { + pixel = getCmykPixel(scaled, x, 0); + QCOMPARE(pixel, expected); + } + + // scale x up + scaled = img.scaled(QSize(size, size * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + for (int y = 0; y < scaled.height(); ++y) { + for (int x = 0; x < scaled.width(); ++x) { + pixel = getCmykPixel(scaled, x, y); + QCOMPARE(pixel, expected); + } + } + + // scale y up + scaled = img.scaled(QSize(size * 2, size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + for (int y = 0; y < scaled.height(); ++y) { + for (int x = 0; x < scaled.width(); ++x) { + pixel = getCmykPixel(scaled, x, y); + QCOMPARE(pixel, expected); + } + } + + // scale x up, y up + scaled = img.scaled(QSize(size * 2, size * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + for (int y = 0; y < scaled.height(); ++y) { + for (int x = 0; x < scaled.width(); ++x) { + pixel = getCmykPixel(scaled, x, y); + QCOMPARE(pixel, expected); + } + } +} + static int count(const QImage &img, int x, int y, int dx, int dy, QRgb pixel) { int i = 0; @@ -3387,6 +3464,120 @@ void tst_QImage::colorSpaceCmykConversion() } } +void tst_QImage::colorSpaceFromGrayConversion_data() +{ + QTest::addColumn<QImage::Format>("fromFormat"); + QTest::addColumn<QColorSpace>("fromCS"); + QTest::addColumn<QColorSpace>("toCS"); + + QImage::Format formats[] = { + QImage::Format_Grayscale8, + QImage::Format_Grayscale16, + }; + + QList<QColorSpace> colorSpaces = { + QColorSpace::SRgbLinear, + QColorSpace::DisplayP3, + QColorSpace(QPointF(0.31271, 0.32902), QColorSpace::TransferFunction::SRgb), + QColorSpace(QPointF(0.30, 0.33), QColorSpace::TransferFunction::Linear) + }; + std::string names[] = { + "sRgbLinear", + "displayP3", + "graySRgb", + "grayOther", + "videoHD(A2B)" + }; + + QFile iccProfile(m_prefix + "VideoHD.icc"); + iccProfile.open(QIODevice::ReadOnly); + colorSpaces.append(QColorSpace::fromIccProfile(iccProfile.readAll())); + + for (auto fromFormat : formats) { + for (int from = 0; from < 5; ++from) { + for (int to = 0; to < 4; ++to) { + QTest::addRow("%s: %s -> %s", formatToString(fromFormat).data(), names[from].c_str(), names[to].c_str()) + << fromFormat << colorSpaces[from] << colorSpaces[to]; + } + } + } +} + +void tst_QImage::colorSpaceFromGrayConversion() +{ + QFETCH(QImage::Format, fromFormat); + QFETCH(QColorSpace, fromCS); + QFETCH(QColorSpace, toCS); + + QImage image(16, 16, fromFormat); + image.setColorSpace(fromCS); + QVERIFY(image.colorSpace().isValid()); + + for (int i = 0; i < image.height(); ++i) { + for (int j = 0; j < image.width(); ++j) { + image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8)); + } + } + QImage imageConverted = image.convertedToColorSpace(toCS); + QCOMPARE(imageConverted.format(), fromFormat); + QCOMPARE(imageConverted.size(), image.size()); + int gray = 0; + for (int x = 0; x < image.width(); ++x) { + int newGray = qGray(imageConverted.pixel(x, 3)); + QCOMPARE_GE(newGray, gray); + gray = newGray; + } +} + +void tst_QImage::colorSpaceToGrayConversion_data() +{ + QTest::addColumn<QImage::Format>("fromFormat"); + + QImage::Format formats[] = { + QImage::Format_RGB32, + QImage::Format_ARGB32, + QImage::Format_ARGB32_Premultiplied, + QImage::Format_RGBX64, + QImage::Format_RGBA64, + QImage::Format_RGBA64_Premultiplied, + QImage::Format_RGBX32FPx4, + QImage::Format_RGBA32FPx4, + QImage::Format_RGBA32FPx4_Premultiplied, + QImage::Format_Grayscale8, + QImage::Format_Grayscale16, + }; + + for (auto fromFormat : formats) + QTest::addRow("%s -> Gray", formatToString(fromFormat).data()) << fromFormat; +} + +void tst_QImage::colorSpaceToGrayConversion() +{ + QFETCH(QImage::Format, fromFormat); + + QImage image(16, 16, fromFormat); + image.setColorSpace(QColorSpace::DisplayP3); + QVERIFY(image.colorSpace().isValid()); + + for (int i = 0; i < image.height(); ++i) { + for (int j = 0; j < image.width(); ++j) { + image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8)); + } + } + + QColorSpace grayColorSpace(QPointF(0.31271, 0.32902), QColorSpace::TransferFunction::SRgb); + + QImage imageConverted = image.convertedToColorSpace(grayColorSpace); + QVERIFY(imageConverted.format() == QImage::Format_Grayscale8 || imageConverted.format() == QImage::Format_Grayscale16); + QCOMPARE(imageConverted.size(), image.size()); + int gray = 0; + for (int x = 0; x < image.width(); ++x) { + int newGray = qGray(imageConverted.pixel(x, 11)); + QCOMPARE_GE(newGray, gray); + gray = newGray; + } +} + void tst_QImage::deepCopyWhenPaintingActive() { QImage image(64, 64, QImage::Format_ARGB32_Premultiplied); diff --git a/tests/auto/gui/itemmodels/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/gui/itemmodels/qfilesystemmodel/tst_qfilesystemmodel.cpp index 8ef0b6272a..4ba3ae11de 100644 --- a/tests/auto/gui/itemmodels/qfilesystemmodel/tst_qfilesystemmodel.cpp +++ b/tests/auto/gui/itemmodels/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -117,8 +117,7 @@ protected: bool createFiles(QFileSystemModel *model, const QString &test_path, const QStringList &initial_files, int existingFileCount = 0, const QStringList &initial_dirs = QStringList()); - QModelIndex prepareTestModelRoot(QFileSystemModel *model, const QString &test_path, - QSignalSpy **spy2 = nullptr, QSignalSpy **spy3 = nullptr); + QModelIndex prepareTestModelRoot(QFileSystemModel *model, const QString &test_path); private: QString flatDirTestPath; @@ -382,17 +381,12 @@ bool tst_QFileSystemModel::createFiles(QFileSystemModel *model, const QString &t return true; } -QModelIndex tst_QFileSystemModel::prepareTestModelRoot(QFileSystemModel *model, const QString &test_path, - QSignalSpy **spy2, QSignalSpy **spy3) +QModelIndex tst_QFileSystemModel::prepareTestModelRoot(QFileSystemModel *model, + const QString &test_path) { if (model->rowCount(model->index(test_path)) != 0) return QModelIndex(); - if (spy2) - *spy2 = new QSignalSpy(model, &QFileSystemModel::rowsInserted); - if (spy3) - *spy3 = new QSignalSpy(model, &QFileSystemModel::rowsAboutToBeInserted); - QStringList files = { "b", "d", "f", "h", "j", ".a", ".c", ".e", ".g" }; if (!createFiles(model, test_path, files)) @@ -412,16 +406,16 @@ QModelIndex tst_QFileSystemModel::prepareTestModelRoot(QFileSystemModel *model, void tst_QFileSystemModel::rowCount() { - QSignalSpy *spy2 = nullptr; - QSignalSpy *spy3 = nullptr; QScopedPointer<QFileSystemModel> model(new QFileSystemModel); QAbstractItemModelTester tester(model.get()); + QSignalSpy rowsInsertedSpy(model.get(), &QFileSystemModel::rowsInserted); + QSignalSpy rowsAboutToBeInsertedSpy(model.get(), &QFileSystemModel::rowsAboutToBeInserted); tester.setUseFetchMore(false); - QModelIndex root = prepareTestModelRoot(model.data(), flatDirTestPath, &spy2, &spy3); + QModelIndex root = prepareTestModelRoot(model.data(), flatDirTestPath); QVERIFY(root.isValid()); - QVERIFY(spy2 && spy2->size() > 0); - QVERIFY(spy3 && spy3->size() > 0); + QCOMPARE_GT(rowsInsertedSpy.size(), 0); + QCOMPARE_GT(rowsAboutToBeInsertedSpy.size(), 0); } void tst_QFileSystemModel::rowsInserted_data() diff --git a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp index 526abd6ea3..fdb1b333ef 100644 --- a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp @@ -172,6 +172,9 @@ void tst_ForeignWindow::destroyWhenParentIsDestroyed() // Reparenting into a window will result in creating it QVERIFY(parentWindow.handle()); + parentWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&parentWindow)); + // Destroying the parent window of the foreign window results // in destroying the foreign window as well, as the foreign // window no longer has a parent it can be embedded in. @@ -184,6 +187,9 @@ void tst_ForeignWindow::destroyWhenParentIsDestroyed() foreignWindow->create(); QVERIFY(foreignWindow->handle()); QTRY_COMPARE(nativeWindow.parentWinId(), parentWindow.winId()); + + parentWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&parentWindow)); } #include <tst_foreignwindow.moc> diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index a9e2c5f882..227fd77e1a 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -1509,6 +1509,7 @@ void tst_QWindow::touchCancelWithTouchToMouse() void tst_QWindow::touchInterruptedByPopup() { InputTestWindow window; + window.setObjectName("main"); window.setTitle(QLatin1String(QTest::currentTestFunction())); window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize)); window.show(); @@ -1529,6 +1530,7 @@ void tst_QWindow::touchInterruptedByPopup() // Launch a popup window InputTestWindow popup; + window.setObjectName("popup"); popup.setFlags(Qt::Popup); popup.setModality(Qt::WindowModal); popup.resize(m_testWindowSize / 2); @@ -1551,9 +1553,6 @@ void tst_QWindow::touchInterruptedByPopup() QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points); QCoreApplication::processEvents(); QTRY_COMPARE(window.touchReleasedCount, 0); - - // Due to temporary fix for QTBUG-37371: the original window should receive a TouchCancel - QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel); } void tst_QWindow::orientation() diff --git a/tests/auto/gui/math3d/qmatrixnxn/tst_qmatrixnxn.cpp b/tests/auto/gui/math3d/qmatrixnxn/tst_qmatrixnxn.cpp index 38ecaa7e44..e761f8cb3c 100644 --- a/tests/auto/gui/math3d/qmatrixnxn/tst_qmatrixnxn.cpp +++ b/tests/auto/gui/math3d/qmatrixnxn/tst_qmatrixnxn.cpp @@ -2343,7 +2343,7 @@ void tst_QMatrixNxN::rotate4x4() ROTATE4(2.0f, 3.0f, 0.0f, 1.0f, p1x, p1y, p1z, p1w); p1x /= p1w; p1y /= p1w; - p1z /= p1w; + Q_UNUSED(p1z); QVector3D v1(2.0f, 3.0f, -4.0f); QVector3D v2 = m1.map(v1); diff --git a/tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.icc b/tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.icc Binary files differnew file mode 100644 index 0000000000..e0ceaa12f8 --- /dev/null +++ b/tests/auto/gui/painting/qcolorspace/resources/Rec. ITU-R BT.2100 PQ.icc diff --git a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp index 7505d463ed..6c103ce4f3 100644 --- a/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp +++ b/tests/auto/gui/painting/qcolorspace/tst_qcolorspace.cpp @@ -71,6 +71,8 @@ private slots: void setWhitePoint(); void grayColorSpace(); void grayColorSpaceEffectivelySRgb(); + + void scaleAlphaValue(); }; tst_QColorSpace::tst_QColorSpace() @@ -121,6 +123,15 @@ void tst_QColorSpace::namedColorSpaces_data() QTest::newRow("ProPhoto RGB") << QColorSpace::ProPhotoRgb << true << QColorSpace::Primaries::ProPhotoRgb << QColorSpace::TransferFunction::ProPhotoRgb; + QTest::newRow("BT.2020") << QColorSpace::Bt2020 << true + << QColorSpace::Primaries::Bt2020 + << QColorSpace::TransferFunction::Bt2020; + QTest::newRow("BT.2100 PQ") << QColorSpace::Bt2100Pq << true + << QColorSpace::Primaries::Bt2020 + << QColorSpace::TransferFunction::St2084; + QTest::newRow("BT.2100 HLG") << QColorSpace::Bt2100Hlg << true + << QColorSpace::Primaries::Bt2020 + << QColorSpace::TransferFunction::Hlg; QTest::newRow("0") << QColorSpace::NamedColorSpace(0) << false << QColorSpace::Primaries::Custom @@ -223,6 +234,11 @@ void tst_QColorSpace::fromIccProfile_data() << QColorSpace::TransferFunction::Custom << QColorSpace::TransformModel::ElementListProcessing << QColorSpace::ColorModel::Cmyk << QString("uCMY"); + // BT.2100 PQ profile + QTest::newRow("BT.2100 PQ") << prefix + "Rec. ITU-R BT.2100 PQ.icc" << QColorSpace::Bt2100Pq + << QColorSpace::TransferFunction::St2084 + << QColorSpace::TransformModel::ThreeComponentMatrix + << QColorSpace::ColorModel::Rgb << QString("Rec. ITU-R BT.2100 PQ"); } void tst_QColorSpace::fromIccProfile() @@ -252,6 +268,15 @@ void tst_QColorSpace::fromIccProfile() QCOMPARE(iccProfile, iccProfile2); QColorSpace fileColorSpace2 = QColorSpace::fromIccProfile(iccProfile2); QCOMPARE(fileColorSpace2, fileColorSpace); + + // Change description to force generation of new icc profile data. + fileColorSpace2.setDescription("Hello my QTest description"); + iccProfile2 = fileColorSpace2.iccProfile(); + QCOMPARE_NE(iccProfile, iccProfile2); + fileColorSpace2 = QColorSpace::fromIccProfile(iccProfile2); + QVERIFY(fileColorSpace2.isValid()); + // Note, we do not currently compare description in color space equality + QCOMPARE(fileColorSpace2, fileColorSpace); } void tst_QColorSpace::imageConversion_data() @@ -1030,5 +1055,14 @@ void tst_QColorSpace::grayColorSpaceEffectivelySRgb() QCOMPARE(rgbImage1, rgbImage2); } +void tst_QColorSpace::scaleAlphaValue() +{ + QImage image(1, 1, QImage::Format_ARGB32); + image.setPixel(0, 0, qRgba(255, 255, 255, 125)); + image.setColorSpace(QColorSpace::SRgb); + image.convertToColorSpace(QColorSpace::SRgbLinear, QImage::Format_RGBA64); + QCOMPARE(reinterpret_cast<const QRgba64 *>(image.constBits())->alpha(), 257 * 125); +} + QTEST_MAIN(tst_QColorSpace) #include "tst_qcolorspace.moc" diff --git a/tests/auto/gui/painting/qregion/tst_qregion.cpp b/tests/auto/gui/painting/qregion/tst_qregion.cpp index 3d60e62fc1..934725844a 100644 --- a/tests/auto/gui/painting/qregion/tst_qregion.cpp +++ b/tests/auto/gui/painting/qregion/tst_qregion.cpp @@ -138,12 +138,15 @@ void tst_QRegion::rects() QRegion region(rect); QVERIFY(region.isEmpty()); QCOMPARE(region.begin(), region.end()); + QVERIFY(region.rects().isEmpty()); } { QRect rect(10, -20, 30, 40); QRegion region(rect); QCOMPARE(region.end(), region.begin() + 1); QCOMPARE(*region.begin(), rect); + QCOMPARE(region.rects().size(), 1); + QCOMPARE(region.rects()[0], rect); } { QRect r(QPoint(10, 10), QPoint(40, 40)); @@ -190,6 +193,7 @@ void tst_QRegion::setRects() QCOMPARE(region, QRegion()); QCOMPARE(region.begin(), region.end()); QVERIFY(!region.boundingRect().isValid()); + QVERIFY(region.rects().isEmpty()); } { QRegion region; @@ -197,12 +201,15 @@ void tst_QRegion::setRects() region.setRects(&rect, 1); QCOMPARE(region.begin(), region.end()); QVERIFY(!region.boundingRect().isValid()); + QVERIFY(region.rects().isEmpty()); } { QRegion region; QRect rect(10, -20, 30, 40); region.setRects(&rect, 1); QCOMPARE(region.end(), region.begin() + 1); + QCOMPARE(region.rects().size(), 1); + QCOMPARE(region.rects()[0], rect); QCOMPARE(*region.begin(), rect); } } @@ -316,10 +323,12 @@ void tst_QRegion::emptyPolygonRegion() QRegion r(pa); QTEST(r.isEmpty(), "isEmpty"); QTEST(int(std::distance(r.begin(), r.end())), "numRects"); - QList<QRect> rects; - std::copy(r.begin(), r.end(), std::back_inserter(rects)); + QList<QRect> rects{r.begin(), r.end()}; QTEST(int(rects.size()), "numRects"); QTEST(rects, "rects"); + const auto span = r.rects(); + rects.assign(span.begin(), span.end()); + QTEST(rects, "rects"); } @@ -862,6 +871,7 @@ void tst_QRegion::isEmpty() QCOMPARE(region, QRegion()); QCOMPARE(region.rectCount(), 0); QCOMPARE(region.boundingRect(), QRect()); + QVERIFY(region.rects().isEmpty()); } void tst_QRegion::regionFromPath() @@ -877,6 +887,10 @@ void tst_QRegion::regionFromPath() QCOMPARE(rgn.begin()[0], QRect(0, 0, 10, 10)); QCOMPARE(rgn.begin()[1], QRect(0, 100, 100, 1000)); + QCOMPARE(rgn.rects().size(), 2); + QCOMPARE(rgn.rects()[0], QRect(0, 0, 10, 10)); + QCOMPARE(rgn.rects()[1], QRect(0, 100, 100, 1000)); + QCOMPARE(rgn.boundingRect(), QRect(0, 0, 100, 1100)); } @@ -893,6 +907,12 @@ void tst_QRegion::regionFromPath() QCOMPARE(rgn.begin()[2], QRect(90, 10, 10, 80)); QCOMPARE(rgn.begin()[3], QRect(0, 90, 100, 10)); + QCOMPARE(rgn.rects().size(), 4); + QCOMPARE(rgn.rects()[0], QRect(0, 0, 100, 10)); + QCOMPARE(rgn.rects()[1], QRect(0, 10, 10, 80)); + QCOMPARE(rgn.rects()[2], QRect(90, 10, 10, 80)); + QCOMPARE(rgn.rects()[3], QRect(0, 90, 100, 10)); + QCOMPARE(rgn.boundingRect(), QRect(0, 0, 100, 100)); } } diff --git a/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp b/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp index a438d7ebc8..203fe003a0 100644 --- a/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp +++ b/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp @@ -56,6 +56,10 @@ private slots: void quotedAndUnquotedIdentifiers(); void whitespaceValues_data(); void whitespaceValues(); + void strokeLineCapValues_data(); + void strokeLineCapValues(); + void strokeLineJoinValues_data(); + void strokeLineJoinValues(); }; void tst_QCssParser::scanner_data() @@ -1759,6 +1763,57 @@ void tst_QCssParser::whitespaceValues() QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value); } +void tst_QCssParser::strokeLineCapValues_data() +{ + QTest::addColumn<QString>("value"); + + QTest::newRow("flatcap") << "flatcap"; + QTest::newRow("roundcap") << "roundcap"; + QTest::newRow("squarecap") << "squarecap"; +} + +void tst_QCssParser::strokeLineCapValues() +{ + QFETCH(QString, value); + QCss::Parser parser(QString("foo { -qt-stroke-linecap: %1 }").arg(value)); + QCss::StyleSheet sheet; + QVERIFY(parser.parse(&sheet)); + + QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ? + sheet.styleRules.at(0) : *sheet.nameIndex.begin(); + QCOMPARE(rule.declarations.size(), 1); + + QCOMPARE(rule.declarations.at(0).d->property, QLatin1String("-qt-stroke-linecap")); + QCOMPARE(rule.declarations.at(0).d->values.first().type, QCss::Value::KnownIdentifier); + QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value); +} + +void tst_QCssParser::strokeLineJoinValues_data() +{ + QTest::addColumn<QString>("value"); + + QTest::newRow("beveljoin") << "beveljoin"; + QTest::newRow("miterjoin") << "miterjoin"; + QTest::newRow("roundjoin") << "roundjoin"; + QTest::newRow("svgmiterjoin") << "svgmiterjoin"; +} + +void tst_QCssParser::strokeLineJoinValues() +{ + QFETCH(QString, value); + QCss::Parser parser(QString("foo { -qt-stroke-linejoin: %1 }").arg(value)); + QCss::StyleSheet sheet; + QVERIFY(parser.parse(&sheet)); + + QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ? + sheet.styleRules.at(0) : *sheet.nameIndex.begin(); + QCOMPARE(rule.declarations.size(), 1); + + QCOMPARE(rule.declarations.at(0).d->property, QLatin1String("-qt-stroke-linejoin")); + QCOMPARE(rule.declarations.at(0).d->values.first().type, QCss::Value::KnownIdentifier); + QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value); +} + QTEST_MAIN(tst_QCssParser) #include "tst_qcssparser.moc" diff --git a/tests/auto/gui/text/qfontdatabase/CMakeLists.txt b/tests/auto/gui/text/qfontdatabase/CMakeLists.txt index 18b96ded5d..0cb6e8d7c8 100644 --- a/tests/auto/gui/text/qfontdatabase/CMakeLists.txt +++ b/tests/auto/gui/text/qfontdatabase/CMakeLists.txt @@ -47,6 +47,8 @@ set(testdata_resource_files "../../../shared/resources/testfont_open.otf" "../../../shared/resources/testfont_variable.ttf" "LED_REAL.TTF" + "QtTestLimitedFont-Regular.ttf" + "QtTestFallbackFont-Regular.ttf" ) qt_internal_add_resource(tst_qfontdatabase "testdata" diff --git a/tests/auto/gui/text/qfontdatabase/QtTestFallbackFont-Regular.ttf b/tests/auto/gui/text/qfontdatabase/QtTestFallbackFont-Regular.ttf Binary files differnew file mode 100644 index 0000000000..ae21fec9a5 --- /dev/null +++ b/tests/auto/gui/text/qfontdatabase/QtTestFallbackFont-Regular.ttf diff --git a/tests/auto/gui/text/qfontdatabase/QtTestLimitedFont-Regular.ttf b/tests/auto/gui/text/qfontdatabase/QtTestLimitedFont-Regular.ttf Binary files differnew file mode 100644 index 0000000000..2891f8aeff --- /dev/null +++ b/tests/auto/gui/text/qfontdatabase/QtTestLimitedFont-Regular.ttf diff --git a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp index 849e7432d1..8733f64d97 100644 --- a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp +++ b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp @@ -78,6 +78,8 @@ private: QString m_testFontCondensed; QString m_testFontItalic; QString m_testFontVariable; + QString m_limitedFont; + QString m_fallbackFont; }; tst_QFontDatabase::tst_QFontDatabase() @@ -91,11 +93,15 @@ void tst_QFontDatabase::initTestCase() m_testFontCondensed = QFINDTESTDATA("testfont_condensed.ttf"); m_testFontItalic = QFINDTESTDATA("testfont_italic.ttf"); m_testFontVariable = QFINDTESTDATA("testfont_variable.ttf"); + m_limitedFont = QFINDTESTDATA("QtTestLimitedFont-Regular.ttf"); + m_fallbackFont = QFINDTESTDATA("QtTestFallbackFont-Regular.ttf"); QVERIFY(!m_ledFont.isEmpty()); QVERIFY(!m_testFont.isEmpty()); QVERIFY(!m_testFontCondensed.isEmpty()); QVERIFY(!m_testFontItalic.isEmpty()); QVERIFY(!m_testFontVariable.isEmpty()); + QVERIFY(!m_limitedFont.isEmpty()); + QVERIFY(!m_fallbackFont.isEmpty()); } void tst_QFontDatabase::styles_data() @@ -391,8 +397,14 @@ void tst_QFontDatabase::condensedFontWidthNoFontMerging() void tst_QFontDatabase::condensedFontWidth() { - QFontDatabase::addApplicationFont(m_testFont); - QFontDatabase::addApplicationFont(m_testFontCondensed); + int testFontId = QFontDatabase::addApplicationFont(m_testFont); + int testFontCondensedId = QFontDatabase::addApplicationFont(m_testFontCondensed); + auto cleanup = qScopeGuard([&testFontId, &testFontCondensedId] { + if (testFontId >= 0) + QFontDatabase::removeApplicationFont(testFontId); + if (testFontCondensedId >= 0) + QFontDatabase::removeApplicationFont(testFontCondensedId); + }); QVERIFY(QFontDatabase::hasFamily("QtBidiTestFont")); if (!QFontDatabase::hasFamily("QtBidiTestFontCondensed")) @@ -410,10 +422,16 @@ void tst_QFontDatabase::condensedFontWidth() void tst_QFontDatabase::condensedFontMatching() { QFontDatabase::removeAllApplicationFonts(); - QFontDatabase::addApplicationFont(m_testFontCondensed); + int testFontCondensedId = QFontDatabase::addApplicationFont(m_testFontCondensed); if (!QFontDatabase::hasFamily("QtBidiTestFont")) QSKIP("This platform doesn't support preferred font family names (QTBUG-53478)"); - QFontDatabase::addApplicationFont(m_testFont); + int testFontId = QFontDatabase::addApplicationFont(m_testFont); + auto cleanup = qScopeGuard([&testFontId, &testFontCondensedId] { + if (testFontId >= 0) + QFontDatabase::removeApplicationFont(testFontId); + if (testFontCondensedId >= 0) + QFontDatabase::removeApplicationFont(testFontCondensedId); + }); // Test we correctly get the condensed font using different font matching methods: QFont tfcByStretch("QtBidiTestFont"); @@ -561,11 +579,17 @@ void tst_QFontDatabase::addApplicationFontFallback() { int ledId = -1; int id = -1; - auto cleanup = qScopeGuard([&id, &ledId] { + int limitedId = -1; + int fallbackId = -1; + auto cleanup = qScopeGuard([&id, &ledId, &limitedId, &fallbackId] { if (id >= 0) QFontDatabase::removeApplicationFont(id); if (ledId >= 0) QFontDatabase::removeApplicationFont(ledId); + if (limitedId >= 0) + QFontDatabase::removeApplicationFont(limitedId); + if (fallbackId >= 0) + QFontDatabase::removeApplicationFont(fallbackId); }); const QChar hebrewChar(0x05D0); // Hebrew 'aleph' @@ -633,6 +657,107 @@ void tst_QFontDatabase::addApplicationFontFallback() QCOMPARE(hebrewFontNow, defaultHebrewFont); } + limitedId = QFontDatabase::addApplicationFont(m_limitedFont); + QVERIFY(limitedId >= 0); + + fallbackId = QFontDatabase::addApplicationFont(m_fallbackFont); + QVERIFY(fallbackId >= 0); + + QFontDatabase::addApplicationFallbackFontFamily(QChar::Script_Common, u"QtTestFallbackFont"_s); + + // The fallback for Common will be used also for Latin, because Latin and Common are + // considered the same script by the font matching engine. + { + QTextLayout layout; + layout.setText(u"A'B,"_s); + layout.setFont(QFont(u"QtTestLimitedFont"_s)); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList<QGlyphRun> glyphRuns = layout.glyphRuns(); + QVERIFY(glyphRuns.size() > 1); + for (int i = 0; i < glyphRuns.size(); ++i) { + QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s + || glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s); + } + } + + // When the text only consists of common script characters, the fallback font will also be used. + { + QTextLayout layout; + layout.setText(u"',"_s); + layout.setFont(QFont(u"QtTestLimitedFont"_s)); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList<QGlyphRun> glyphRuns = layout.glyphRuns(); + QCOMPARE(glyphRuns.size(), 2); + for (int i = 0; i < glyphRuns.size(); ++i) { + QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s + || glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s); + } + } + + QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Common, u"QtTestFallbackFont"_s)); + QFontDatabase::addApplicationFallbackFontFamily(QChar::Script_Latin, u"QtTestFallbackFont"_s); + + // Latin fallback works just the same as Common fallback + { + QTextLayout layout; + layout.setText(u"A'B,"_s); + layout.setFont(QFont(u"QtTestLimitedFont"_s)); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList<QGlyphRun> glyphRuns = layout.glyphRuns(); + QCOMPARE(glyphRuns.size(), 2); + for (int i = 0; i < glyphRuns.size(); ++i) { + QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s + || glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s); + } + } + + // When the common character is placed next to a Cyrillic characters, it gets adapted to this, + // so the fallback font will not be selected, even if it supports the character in question + { + QTextLayout layout; + layout.setText(u"A'Б,"_s); + layout.setFont(QFont(u"QtTestLimitedFont"_s)); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList<QGlyphRun> glyphRuns = layout.glyphRuns(); + QCOMPARE(glyphRuns.size(), 2); + for (int i = 0; i < glyphRuns.size(); ++i) { + QVERIFY(glyphRuns.at(i).rawFont().familyName() != u"QtTestFallbackFont"_s); + } + } + + QFontDatabase::addApplicationFallbackFontFamily(QChar::Script_Cyrillic, u"QtTestFallbackFont"_s); + + // When we set the fallback font for Cyrillic as well, it gets selected + { + QTextLayout layout; + layout.setText(u"A'Б,"_s); + layout.setFont(QFont(u"QtTestLimitedFont"_s)); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList<QGlyphRun> glyphRuns = layout.glyphRuns(); + QCOMPARE(glyphRuns.size(), 2); + for (int i = 0; i < glyphRuns.size(); ++i) { + QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s + || glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s); + } + } + + QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Cyrillic, u"QtTestFallbackFont"_s)); + QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Latin, u"QtTestFallbackFont"_s)); } QTEST_MAIN(tst_QFontDatabase) diff --git a/tests/auto/gui/text/qfontmetrics/CMakeLists.txt b/tests/auto/gui/text/qfontmetrics/CMakeLists.txt index d014d27d46..ee2f76ef76 100644 --- a/tests/auto/gui/text/qfontmetrics/CMakeLists.txt +++ b/tests/auto/gui/text/qfontmetrics/CMakeLists.txt @@ -24,8 +24,12 @@ qt_internal_add_test(tst_qfontmetrics set_source_files_properties("../../../shared/resources/testfont.ttf" PROPERTIES QT_RESOURCE_ALIAS "testfont.ttf" ) +set_source_files_properties("../../../shared/resources/testfont_linemetrics.otf" + PROPERTIES QT_RESOURCE_ALIAS "testfont_linemetrics.otf" +) set(testfont_resource_files "../../../shared/resources/testfont.ttf" + "../../../shared/resources/testfont_linemetrics.otf" "ucs4font.ttf" ) diff --git a/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp b/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp index 9471c1d93f..bad33ab0a4 100644 --- a/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp +++ b/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp @@ -36,6 +36,7 @@ private slots: void verticalMetrics(); void largeText_data(); void largeText(); // QTBUG-123339 + void typoLineMetrics(); }; void tst_QFontMetrics::same() @@ -410,5 +411,48 @@ void tst_QFontMetrics::largeText() QVERIFY(boundingRect.isValid()); } +void tst_QFontMetrics::typoLineMetrics() +{ + QString testFont = QFINDTESTDATA("fonts/testfont_linemetrics.otf"); + QVERIFY(!testFont.isEmpty()); + + int id = QFontDatabase::addApplicationFont(testFont); + QVERIFY(id >= 0); + + { + auto cleanup = qScopeGuard([&id] { + if (id >= 0) + QFontDatabase::removeApplicationFont(id); + }); + + QImage img(100, 100, QImage::Format_ARGB32); + img.setDevicePixelRatio(1.0); + QFont font(QFontDatabase::applicationFontFamilies(id).at(0), &img); + font.setPixelSize(18); + + const qreal unitsPerEm = 1000.0; + + QFontMetrics defaultFm(font); + const int defaultAscent = defaultFm.ascent(); + const int defaultDescent = defaultFm.descent(); + const int defaultLeading = defaultFm.leading(); + + QCOMPARE(defaultAscent, qRound(1234.0 / unitsPerEm * font.pixelSize())); + QCOMPARE(defaultDescent, qRound(5678.0 / unitsPerEm * font.pixelSize())); + QCOMPARE(defaultLeading, 0.0); + + font.setStyleStrategy(QFont::PreferTypoLineMetrics); + const QFontMetrics typoFm(font); + + const int typoAscent = typoFm.ascent(); + const int typoDescent = typoFm.descent(); + const int typoLeading = typoFm.leading(); + + QCOMPARE(typoAscent, qRound(2000.0 / unitsPerEm * font.pixelSize())); + QCOMPARE(typoDescent, qRound(3000.0 / unitsPerEm * font.pixelSize())); + QCOMPARE(typoLeading, qRound(1000.0 / unitsPerEm * font.pixelSize())); + } +} + QTEST_MAIN(tst_QFontMetrics) #include "tst_qfontmetrics.moc" diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index 335ee06e2f..600b45575f 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -4053,20 +4053,107 @@ void tst_QTextDocument::restoreStrokeFromHtml() QTextDocument document; QTextCursor textCursor(&document); QTextCharFormat textOutline; - textOutline.setTextOutline(QPen(Qt::red, 2.3)); - textCursor.insertText("Outlined text", textOutline); + + // Set stroke color and width + { + QPen pen(Qt::red, 2.3, Qt::SolidLine); + textOutline.setTextOutline(pen); + textCursor.insertText("Outlined text", textOutline); + } + + // Set Cap and Join styles + { + QPen pen; + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::RoundJoin); + textOutline.setTextOutline(pen); + textCursor.insertBlock(); + textCursor.insertText("Cap and Join Style", textOutline); + } + + // Set Miter limit + { + QPen pen; + pen.setJoinStyle(Qt::MiterJoin); + pen.setMiterLimit(4); + textOutline.setTextOutline(pen); + textCursor.insertBlock(); + textCursor.insertText("Miter Limit", textOutline); + } + + // Set Dash Array and Dash Offset + { + QPen pen; + QList<qreal> pattern; + const int dash = 2; + const int gap = 4; + pattern << dash << gap << dash << gap << dash << gap; + pen.setDashPattern(pattern); + pen.setDashOffset(3); + textOutline.setTextOutline(pen); + textCursor.insertBlock(); + textCursor.insertText("Dash Pattern", textOutline); + } + { QTextDocument otherDocument; otherDocument.setHtml(document.toHtml()); - QCOMPARE(otherDocument.blockCount(), 1); - QTextBlock block = otherDocument.firstBlock(); - QTextFragment fragment = block.begin().fragment(); - QCOMPARE(fragment.text(), QStringLiteral("Outlined text")); - QTextCharFormat fmt = fragment.charFormat(); - QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline)); - QPen pen = fmt.textOutline(); - QCOMPARE(pen.color(), QColor(Qt::red)); - QCOMPARE(pen.widthF(), 2.3); + QCOMPARE(otherDocument.blockCount(), document.blockCount()); + + QTextBlock block; + QTextFragment fragment; + QTextCharFormat fmt; + QPen pen; + + { + block = otherDocument.findBlockByNumber(0); + fragment = block.begin().fragment(); + QCOMPARE(fragment.text(), QStringLiteral("Outlined text")); + fmt = fragment.charFormat(); + QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline)); + pen = fmt.textOutline(); + QCOMPARE(pen.color(), QColor(Qt::red)); + QCOMPARE(pen.widthF(), 2.3); + } + + { + block = otherDocument.findBlockByNumber(1); + qDebug() << block.text(); + fragment = block.begin().fragment(); + QCOMPARE(fragment.text(), QStringLiteral("Cap and Join Style")); + fmt = fragment.charFormat(); + QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline)); + pen = fmt.textOutline(); + QCOMPARE(pen.capStyle(), Qt::FlatCap); + QCOMPARE(pen.joinStyle(), Qt::RoundJoin); + } + + { + block = otherDocument.findBlockByNumber(2); + fragment = block.begin().fragment(); + QCOMPARE(fragment.text(), QStringLiteral("Miter Limit")); + fmt = fragment.charFormat(); + QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline)); + pen = fmt.textOutline(); + QCOMPARE(pen.joinStyle(), Qt::MiterJoin); + QCOMPARE(pen.miterLimit(), 4); + } + + + { + block = otherDocument.findBlockByNumber(3); + fragment = block.begin().fragment(); + QCOMPARE(fragment.text(), QStringLiteral("Dash Pattern")); + fmt = fragment.charFormat(); + QVERIFY(fmt.hasProperty(QTextCharFormat::TextOutline)); + pen = fmt.textOutline(); + QCOMPARE(pen.dashOffset(), 3); + QList<qreal> pattern; + const int dash = 2; + const int gap = 4; + pattern << dash << gap << dash << gap << dash << gap; + QCOMPARE(pen.dashPattern(), pattern); + } } } diff --git a/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp b/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp index 8327a5d187..0048623d0e 100644 --- a/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp +++ b/tests/auto/gui/text/qtextimagehandler/tst_qtextimagehandler.cpp @@ -6,6 +6,10 @@ #include <QPainter> #include <private/qtextimagehandler_p.h> +using namespace Qt::StringLiterals; + +// #define DEBUG_WRITE_HTML + class tst_QTextImageHandler : public QObject { Q_OBJECT @@ -20,6 +24,8 @@ private slots: void loadAtNImages_data(); #ifndef QT_NO_TEXTHTMLPARSER void loadAtNImages(); + void maxWidth_data(); + void maxWidth(); #endif }; @@ -61,7 +67,7 @@ void tst_QTextImageHandler::loadAtNImages() const auto it = std::find_if(formats.begin(), formats.end(), [](const auto &format){ return format.objectType() == QTextFormat::ImageObject; }); - QVERIFY(it != formats.end()); + QCOMPARE_NE(it, formats.end()); const QTextImageFormat format = (*it).toImageFormat(); QTextImageHandler handler; @@ -77,6 +83,64 @@ void tst_QTextImageHandler::loadAtNImages() QCOMPARE(img.pixelColor(0, 0), expectedColor); } } + +void tst_QTextImageHandler::maxWidth_data() +{ + QTest::addColumn<QString>("imageFile"); + QTest::addColumn<QSizeF>("pageSize"); + QTest::addColumn<QTextLength>("maxWidth"); + QTest::addColumn<QSizeF>("expectedSize"); + + QTest::addRow("constrained-percentage") << QFINDTESTDATA("data/image.png") << QSizeF(16, 16) << QTextLength(QTextLength::PercentageLength, 100) << QSizeF(12, 12); + QTest::addRow("not-constrained-percentage") << QFINDTESTDATA("data/image.png") << QSizeF(200, 200) << QTextLength(QTextLength::PercentageLength, 100) << QSizeF(16, 16); + QTest::addRow("constrained-fixed") << QFINDTESTDATA("data/image.png") << QSizeF(16, 16) << QTextLength(QTextLength::FixedLength, 5) << QSizeF(5, 5); + QTest::addRow("not-constrained-fixed") << QFINDTESTDATA("data/image.png") << QSizeF(200, 200) << QTextLength(QTextLength::FixedLength, 5) << QSizeF(5, 5); + QTest::addRow("not-constrained-default") << QFINDTESTDATA("data/image.png") << QSizeF(200, 200) << QTextLength(QTextLength::VariableLength, 5) << QSizeF(16, 16); +} + +void tst_QTextImageHandler::maxWidth() +{ + QFETCH(QString, imageFile); + QFETCH(QSizeF, pageSize); + QFETCH(QTextLength, maxWidth); + QFETCH(QSizeF, expectedSize); + + QTextDocument doc; + doc.setPageSize(pageSize); + doc.setDocumentMargin(2); + QTextCursor c(&doc); + QString style; + if (maxWidth.type() == QTextLength::PercentageLength) + style = " style=\"max-width:"_L1 + QString::number(maxWidth.rawValue()) + "%;\""_L1; + else if (maxWidth.type() == QTextLength::FixedLength) + style = " style=\"max-width:"_L1 + QString::number(maxWidth.rawValue()) + "px;\""_L1; + const QString html = "<img src=\"" + imageFile + u'\"' + style + "\">"; + c.insertHtml(html); + +#ifdef DEBUG_WRITE_HTML + { + QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html"); + out.open(QFile::WriteOnly); + out.write(html.toLatin1()); + out.close(); + } + { + QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + "_rewrite.html"); + out.open(QFile::WriteOnly); + out.write(doc.toHtml().toLatin1()); + out.close(); + } +#endif + const auto formats = doc.allFormats(); + const auto it = std::find_if(formats.begin(), formats.end(), [](const auto &format){ + return format.objectType() == QTextFormat::ImageObject; + }); + QCOMPARE_NE(it, formats.end()); + const QTextImageFormat format = (*it).toImageFormat(); + QTextImageHandler handler; + + QCOMPARE(handler.intrinsicSize(&doc, 0, format), expectedSize); +} #endif QTEST_MAIN(tst_QTextImageHandler) diff --git a/tests/auto/gui/text/qtextmarkdownwriter/data/example.md b/tests/auto/gui/text/qtextmarkdownwriter/data/example.md index 15b30598e6..8fdad207ae 100644 --- a/tests/auto/gui/text/qtextmarkdownwriter/data/example.md +++ b/tests/auto/gui/text/qtextmarkdownwriter/data/example.md @@ -40,7 +40,7 @@ numerals in the same list structure: 1. Introduction 2. Qt Tools 1) Qt Assistant - 2) Qt Designer + 2) Qt Widgets Designer 1. Form Editor 2. Component Architecture 3) Qt Linguist @@ -70,7 +70,7 @@ column spans, text formatting within cells, and size constraints for columns. |-------------|------------------------------------|---------------------------|-------------------------| |9:00 - 11:00 |Introduction to Qt ||| |11:00 - 13:00|Using qmake |Object-oriented Programming|Layouts in Qt | -|13:00 - 15:00|Qt Designer Tutorial |Extreme Programming |Writing Custom Styles | +|13:00 - 15:00|Qt Widgets Designer Tutorial |Extreme Programming |Writing Custom Styles | |15:00 - 17:00|Qt Linguist and Internationalization|Test-Driven Development | | *Try adding text to the cells in the table and experiment with the alignment of diff --git a/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp b/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp index e08b299209..e75626eda7 100644 --- a/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp +++ b/tests/auto/gui/util/qdesktopservices/tst_qdesktopservices.cpp @@ -42,10 +42,6 @@ public slots: } }; -#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) -# define CAN_IMPLICITLY_UNSET -#endif - void tst_qdesktopservices::handlers() { MyUrlHandler fooHandler; @@ -53,12 +49,10 @@ void tst_qdesktopservices::handlers() QDesktopServices::setUrlHandler(QString("foo"), &fooHandler, "handle"); QDesktopServices::setUrlHandler(QString("bar"), &barHandler, "handle"); -#ifndef CAN_IMPLICITLY_UNSET const auto unsetHandlers = qScopeGuard([] { QDesktopServices::unsetUrlHandler(u"bar"_s); QDesktopServices::unsetUrlHandler(u"foo"_s); }); -#endif QUrl fooUrl("foo://blub/meh"); QUrl barUrl("bar://hmm/hmmmm"); @@ -68,15 +62,6 @@ void tst_qdesktopservices::handlers() QCOMPARE(fooHandler.lastHandledUrl.toString(), fooUrl.toString()); QCOMPARE(barHandler.lastHandledUrl.toString(), barUrl.toString()); - -#ifdef CAN_IMPLICITLY_UNSET - for (int i = 0; i < 2; ++i) - QTest::ignoreMessage(QtWarningMsg, - "Please call QDesktopServices::unsetUrlHandler() before destroying a " - "registered URL handler object.\n" - "Support for destroying a registered URL handler object is deprecated, " - "and will be removed in Qt 6.6."); -#endif } QTEST_MAIN(tst_qdesktopservices) diff --git a/tests/auto/network/access/CMakeLists.txt b/tests/auto/network/access/CMakeLists.txt index b9015642b7..ed99aa8746 100644 --- a/tests/auto/network/access/CMakeLists.txt +++ b/tests/auto/network/access/CMakeLists.txt @@ -9,12 +9,12 @@ add_subdirectory(qnetworkcookiejar) add_subdirectory(qnetworkaccessmanager) add_subdirectory(qnetworkcookie) add_subdirectory(qnetworkrequest) -if(QT_FEATURE_localserver) - add_subdirectory(qnetworkreply) -endif() +add_subdirectory(qnetworkreply) add_subdirectory(qnetworkcachemetadata) add_subdirectory(qabstractnetworkcache) if(QT_FEATURE_http) + add_subdirectory(qnetworkreply_local) + add_subdirectory(qformdatabuilder) add_subdirectory(qnetworkrequestfactory) add_subdirectory(qrestaccessmanager) endif() diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index d9e82330b2..396a6f2fda 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -1277,7 +1277,7 @@ void tst_Http2::unsupportedAuthenticateChallenge() bool authenticationRequested = false; connect(manager.get(), &QNetworkAccessManager::authenticationRequired, reply.get(), - [&](QNetworkReply *, QAuthenticator *auth) { + [&](QNetworkReply *, QAuthenticator *) { authenticationRequested = true; }); diff --git a/tests/auto/network/access/qformdatabuilder/CMakeLists.txt b/tests/auto/network/access/qformdatabuilder/CMakeLists.txt new file mode 100644 index 0000000000..dde2dc10e0 --- /dev/null +++ b/tests/auto/network/access/qformdatabuilder/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qformdatabuilder LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qformdatabuilder + SOURCES + tst_qformdatabuilder.cpp + LIBRARIES + Qt::Core + Qt::Network + TESTDATA + rfc3252.txt + image1.jpg + document.docx + sheet.xlsx +) + diff --git a/tests/auto/network/access/qformdatabuilder/document.docx b/tests/auto/network/access/qformdatabuilder/document.docx Binary files differnew file mode 100644 index 0000000000..49005f14b6 --- /dev/null +++ b/tests/auto/network/access/qformdatabuilder/document.docx diff --git a/tests/auto/network/access/qformdatabuilder/image1.jpg b/tests/auto/network/access/qformdatabuilder/image1.jpg Binary files differnew file mode 100644 index 0000000000..d1d27bf7cf --- /dev/null +++ b/tests/auto/network/access/qformdatabuilder/image1.jpg diff --git a/tests/auto/network/access/qformdatabuilder/rfc3252.txt b/tests/auto/network/access/qformdatabuilder/rfc3252.txt new file mode 100644 index 0000000000..5436ce5b26 --- /dev/null +++ b/tests/auto/network/access/qformdatabuilder/rfc3252.txt @@ -0,0 +1 @@ +some text for reference diff --git a/tests/auto/network/access/qformdatabuilder/sheet.xlsx b/tests/auto/network/access/qformdatabuilder/sheet.xlsx Binary files differnew file mode 100644 index 0000000000..2cb1ec7361 --- /dev/null +++ b/tests/auto/network/access/qformdatabuilder/sheet.xlsx diff --git a/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp b/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp new file mode 100644 index 0000000000..ad40c79bf9 --- /dev/null +++ b/tests/auto/network/access/qformdatabuilder/tst_qformdatabuilder.cpp @@ -0,0 +1,203 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtNetwork/qformdatabuilder.h> + +#include <QtCore/qbuffer.h> +#include <QtCore/qfile.h> + +#include <QtTest/qtest.h> + +using namespace Qt::StringLiterals; + +class tst_QFormDataBuilder : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void generateQHttpPartWithDevice_data(); + void generateQHttpPartWithDevice(); + + void escapesBackslashAndQuotesInFilenameAndName_data(); + void escapesBackslashAndQuotesInFilenameAndName(); + + void picksUtf8EncodingOnlyIfL1OrAsciiDontSuffice_data(); + void picksUtf8EncodingOnlyIfL1OrAsciiDontSuffice(); +}; + +void tst_QFormDataBuilder::generateQHttpPartWithDevice_data() +{ + QTest::addColumn<QLatin1StringView>("name_data"); + QTest::addColumn<QString>("real_file_name"); + QTest::addColumn<QString>("body_name_data"); + QTest::addColumn<QByteArray>("expected_content_type_data"); + QTest::addColumn<QByteArray>("expected_content_disposition_data"); + + QTest::newRow("txt-ascii") << "text"_L1 << "rfc3252.txt" << "rfc3252.txt" << "text/plain"_ba + << "form-data; name=\"text\"; filename=rfc3252.txt"_ba; + QTest::newRow("txt-latin") << "text"_L1 << "rfc3252.txt" << "szöveg.txt" << "text/plain"_ba + << "form-data; name=\"text\"; filename*=ISO-8859-1''sz%F6veg.txt"_ba; + QTest::newRow("txt-unicode") << "text"_L1 << "rfc3252.txt" << "テキスト.txt" << "text/plain"_ba + << "form-data; name=\"text\"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.txt"_ba; + + QTest::newRow("jpg-ascii") << "image"_L1 << "image1.jpg" << "image1.jpg" << "image/jpeg"_ba + << "form-data; name=\"image\"; filename=image1.jpg"_ba; + QTest::newRow("jpg-latin") << "image"_L1 << "image1.jpg" << "kép.jpg" << "image/jpeg"_ba + << "form-data; name=\"image\"; filename*=ISO-8859-1''k%E9p.jpg"_ba; + QTest::newRow("jpg-unicode") << "image"_L1 << "image1.jpg" << "絵.jpg" << "image/jpeg"_ba + << "form-data; name=\"image\"; filename*=UTF-8''%E7%B5%B5"_ba; + + QTest::newRow("doc-ascii") << "text"_L1 << "document.docx" << "word.docx" + << "application/vnd.openxmlformats-officedocument.wordprocessingml.document"_ba + << "form-data; name=\"text\"; filename=word.docx"_ba; + QTest::newRow("doc-latin") << "text"_L1 << "document.docx" << "szöveg.docx" + << "application/vnd.openxmlformats-officedocument.wordprocessingml.document"_ba + << "form-data; name=\"text\"; filename*=ISO-8859-1''sz%F6veg.docx"_ba; + QTest::newRow("doc-unicode") << "text"_L1 << "document.docx" << "テキスト.docx" + << "application/vnd.openxmlformats-officedocument.wordprocessingml.document"_ba + << "form-data; name=\"text\"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.docx"_ba; + + QTest::newRow("xls-ascii") << "spreadsheet"_L1 << "sheet.xlsx" << "sheet.xlsx" + << "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"_ba + << "form-data; name=\"spreadsheet\"; filename=sheet.xlsx"_ba; + QTest::newRow("xls-latin") << "spreadsheet"_L1 << "sheet.xlsx" << "szöveg.xlsx" + << "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"_ba + << "form-data; name=\"spreadsheet\"; filename*=ISO-8859-1''sz%F6veg.xlsx"_ba; + QTest::newRow("xls-unicode") << "spreadsheet"_L1 << "sheet.xlsx" << "テキスト.xlsx" + << "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"_ba + << "form-data; name=\"spreadsheet\"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.xlsx"_ba; + +} + +void tst_QFormDataBuilder::generateQHttpPartWithDevice() +{ + QFETCH(const QLatin1StringView, name_data); + QFETCH(const QString, real_file_name); + QFETCH(const QString, body_name_data); + QFETCH(const QByteArray, expected_content_type_data); + QFETCH(const QByteArray, expected_content_disposition_data); + + QString testData = QFileInfo(QFINDTESTDATA(real_file_name)).absoluteFilePath(); + QFile data_file(testData); + + QHttpPart httpPart = QFormDataPartBuilder(name_data, QFormDataPartBuilder::PrivateConstructor()) + .setBodyDevice(&data_file, body_name_data) + .build(); + + QByteArray msg; + { + QBuffer buf(&msg); + QVERIFY(buf.open(QIODevice::WriteOnly)); + QDebug debug(&buf); + debug << httpPart; + } + + QVERIFY(msg.contains(expected_content_type_data)); + QVERIFY(msg.contains(expected_content_disposition_data)); +} + +void tst_QFormDataBuilder::escapesBackslashAndQuotesInFilenameAndName_data() +{ + QTest::addColumn<QLatin1StringView>("name_data"); + QTest::addColumn<QString>("body_name_data"); + QTest::addColumn<QByteArray>("expected_content_type_data"); + QTest::addColumn<QByteArray>("expected_content_disposition_data"); + + QTest::newRow("quote") << "t\"ext"_L1 << "rfc3252.txt" << "text/plain"_ba + << R"(form-data; name="t\"ext"; filename=rfc3252.txt)"_ba; + + QTest::newRow("slash") << "t\\ext"_L1 << "rfc3252.txt" << "text/plain"_ba + << R"(form-data; name="t\\ext"; filename=rfc3252.txt)"_ba; + + QTest::newRow("quotes") << "t\"e\"xt"_L1 << "rfc3252.txt" << "text/plain"_ba + << R"(form-data; name="t\"e\"xt"; filename=rfc3252.txt)"_ba; + + QTest::newRow("slashes") << "t\\\\ext"_L1 << "rfc3252.txt" << "text/plain"_ba + << R"(form-data; name="t\\\\ext"; filename=rfc3252.txt)"_ba; + + QTest::newRow("quote-slash") << "t\"ex\\t"_L1 << "rfc3252.txt" << "text/plain"_ba + << R"(form-data; name="t\"ex\\t"; filename=rfc3252.txt)"_ba; + + QTest::newRow("quotes-slashes") << "t\"e\"x\\t\\"_L1 << "rfc3252.txt" << "text/plain"_ba + << R"(form-data; name="t\"e\"x\\t\\"; filename=rfc3252.txt)"_ba; +} + +void tst_QFormDataBuilder::escapesBackslashAndQuotesInFilenameAndName() +{ + QFETCH(const QLatin1StringView, name_data); + QFETCH(const QString, body_name_data); + QFETCH(const QByteArray, expected_content_type_data); + QFETCH(const QByteArray, expected_content_disposition_data); + + QFile dummy_file(body_name_data); + + QHttpPart httpPart = QFormDataPartBuilder(name_data, QFormDataPartBuilder::PrivateConstructor()) + .setBodyDevice(&dummy_file, body_name_data) + .build(); + + QByteArray msg; + { + QBuffer buf(&msg); + QVERIFY(buf.open(QIODevice::WriteOnly)); + QDebug debug(&buf); + debug << httpPart; + } + + QVERIFY(msg.contains(expected_content_type_data)); + QVERIFY(msg.contains(expected_content_disposition_data)); +} + +void tst_QFormDataBuilder::picksUtf8EncodingOnlyIfL1OrAsciiDontSuffice_data() +{ + QTest::addColumn<QLatin1StringView>("name_data"); + QTest::addColumn<QAnyStringView>("body_name_data"); + QTest::addColumn<QByteArray>("expected_content_type_data"); + QTest::addColumn<QByteArray>("expected_content_disposition_data"); + + QTest::newRow("latin1-ascii") << "text"_L1 << QAnyStringView("rfc3252.txt"_L1) << "text/plain"_ba + << "form-data; name=\"text\"; filename=rfc3252.txt"_ba; + QTest::newRow("u8-ascii") << "text"_L1 << QAnyStringView(u8"rfc3252.txt") << "text/plain"_ba + << "form-data; name=\"text\"; filename=rfc3252.txt"_ba; + QTest::newRow("u-ascii") << "text"_L1 << QAnyStringView(u"rfc3252.txt") << "text/plain"_ba + << "form-data; name=\"text\"; filename=rfc3252.txt"_ba; + + + QTest::newRow("latin1-latin") << "text"_L1 << QAnyStringView("sz\366veg.txt"_L1) << "text/plain"_ba + << "form-data; name=\"text\"; filename*=ISO-8859-1''sz%F6veg.txt"_ba; + QTest::newRow("u8-latin") << "text"_L1 << QAnyStringView(u8"szöveg.txt") << "text/plain"_ba + << "form-data; name=\"text\"; filename*=ISO-8859-1''sz%F6veg.txt"_ba; + QTest::newRow("u-latin") << "text"_L1 << QAnyStringView(u"szöveg.txt") << "text/plain"_ba + << "form-data; name=\"text\"; filename*=ISO-8859-1''sz%F6veg.txt"_ba; + + QTest::newRow("u8-u8") << "text"_L1 << QAnyStringView(u8"テキスト.txt") << "text/plain"_ba + << "form-data; name=\"text\"; filename*=UTF-8''%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88.txt"_ba; +} + +void tst_QFormDataBuilder::picksUtf8EncodingOnlyIfL1OrAsciiDontSuffice() +{ + QFETCH(const QLatin1StringView, name_data); + QFETCH(const QAnyStringView, body_name_data); + QFETCH(const QByteArray, expected_content_type_data); + QFETCH(const QByteArray, expected_content_disposition_data); + + QBuffer buff; + + QHttpPart httpPart = QFormDataPartBuilder(name_data, QFormDataPartBuilder::PrivateConstructor()) + .setBodyDevice(&buff, body_name_data) + .build(); + + QByteArray msg; + { + QBuffer buf(&msg); + QVERIFY(buf.open(QIODevice::WriteOnly)); + QDebug debug(&buf); + debug << httpPart; + } + + QVERIFY(msg.contains(expected_content_type_data)); + QVERIFY(msg.contains(expected_content_disposition_data)); +} + + +QTEST_MAIN(tst_QFormDataBuilder) +#include "tst_qformdatabuilder.moc" diff --git a/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa b/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa Binary files differindex 2bd4ca05f3..f891b16963 100644 --- a/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa +++ b/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 66cbeaac9b..64e7716e0c 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -35,8 +35,10 @@ #include <QtNetwork/QTcpServer> #include <QtNetwork/QTcpSocket> +#if QT_CONFIG(localserver) #include <QtNetwork/QLocalSocket> #include <QtNetwork/QLocalServer> +#endif #include <QtNetwork/QHostInfo> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkRequest> @@ -44,14 +46,18 @@ #include <QtNetwork/QAbstractNetworkCache> #include <QtNetwork/qauthenticator.h> #include <QtNetwork/qnetworkaccessmanager.h> +#if QT_CONFIG(networkdiskcache) #include <QtNetwork/qnetworkdiskcache.h> +#endif #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> -#include <QtNetwork/QHttp1Configuration> #include <QtNetwork/qnetworkcookie.h> #include <QtNetwork/QNetworkCookieJar> +#if QT_CONFIG(http) #include <QtNetwork/QHttpPart> #include <QtNetwork/QHttpMultiPart> +#include <QtNetwork/QHttp1Configuration> +#endif #include <QtNetwork/QNetworkProxyQuery> #if QT_CONFIG(ssl) #include <QtNetwork/qsslerror.h> @@ -129,14 +135,14 @@ class tst_QNetworkReply: public QObject static QString tempRedirectReplyStr() { QString s = "HTTP/1.1 307 Temporary Redirect\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %1\r\n" "\r\n"; return s; } static QString movedReplyStr() { QString s = "HTTP/1.1 301 Moved Permanently\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %1\r\n" "\r\n"; return s; @@ -144,7 +150,7 @@ class tst_QNetworkReply: public QObject static QString foundReplyStr() { QString s = "HTTP/1.1 302 Found\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %1\r\n" "\r\n"; return s; @@ -152,7 +158,7 @@ class tst_QNetworkReply: public QObject static QString permRedirectReplyStr() { QString s = "HTTP/1.1 308 Permanent Redirect\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %1\r\n" "\r\n"; return s; @@ -194,8 +200,10 @@ public: ~tst_QNetworkReply(); QString runSimpleRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QNetworkReplyPtr &reply, const QByteArray &data = QByteArray()); +#if QT_CONFIG(http) QString runMultipartRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply, QHttpMultiPart *multiPart, const QByteArray &verb); +#endif QString runCustomRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply, const QByteArray &verb, QIODevice *data); @@ -261,17 +269,21 @@ private Q_SLOTS: void putToHttp(); void putToHttpSynchronous_data(); void putToHttpSynchronous(); +#if QT_CONFIG(http) void putToHttpMultipart_data(); void putToHttpMultipart(); +#endif void putWithoutBody(); void putWithoutBody_data(); void postToHttp_data(); void postToHttp(); void postToHttpSynchronous_data(); void postToHttpSynchronous(); +#if QT_CONFIG(http) void postToHttpMultipart_data(); void postToHttpMultipart(); void multipartSkipIndices(); // QTBUG-32534 +#endif void postWithoutBody_data(); void postWithoutBody(); #if QT_CONFIG(ssl) @@ -342,8 +354,10 @@ private Q_SLOTS: void ioPutToFileFromFile(); void ioPutToFileFromSocket_data(); void ioPutToFileFromSocket(); +#if QT_CONFIG(localserver) void ioPutToFileFromLocalSocket_data(); void ioPutToFileFromLocalSocket(); +#endif void ioPutToFileFromProcess_data(); void ioPutToFileFromProcess(); void ioPutToFtpFromFile_data(); @@ -449,7 +463,9 @@ private Q_SLOTS: void ioGetFromHttpWithoutContentLength(); void ioGetFromHttpBrokenChunkedEncoding(); +#if QT_CONFIG(http) void qtbug12908compressedHttpReply(); +#endif void compressedHttpReplyBrokenGzip(); void getFromUnreachableIp(); @@ -468,7 +484,9 @@ private Q_SLOTS: void qtbug27161httpHeaderMayBeDamaged_data(); void qtbug27161httpHeaderMayBeDamaged(); +#if QT_CONFIG(networkdiskcache) void qtbug28035browserDoesNotLoadQtProjectOrgCorrectly(); +#endif void qtbug45581WrongReplyStatusCode(); @@ -484,8 +502,10 @@ private Q_SLOTS: void varyingCacheExpiry_data(); void varyingCacheExpiry(); +#if QT_CONFIG(http) void amountOfHttp1ConnectionsQtbug25280_data(); void amountOfHttp1ConnectionsQtbug25280(); +#endif void dontInsertPartialContentIntoTheCache(); @@ -521,12 +541,16 @@ private Q_SLOTS: void ioHttpCookiesDuringRedirect(); void ioHttpRedirect_data(); void ioHttpRedirect(); +#if QT_CONFIG(networkdiskcache) void ioHttpRedirectWithCache(); +#endif void ioHttpRedirectFromLocalToRemote(); void ioHttpRedirectPostPut_data(); void ioHttpRedirectPostPut(); +#if QT_CONFIG(http) void ioHttpRedirectMultipartPost_data(); void ioHttpRedirectMultipartPost(); +#endif void ioHttpRedirectDelete(); void ioHttpRedirectCustom(); void ioHttpRedirectWithUploadDevice_data(); @@ -540,16 +564,20 @@ private Q_SLOTS: void autoDeleteReplies_data(); void autoDeleteReplies(); +#if QT_CONFIG(http) || defined (Q_OS_WASM) void requestWithTimeout_data(); void requestWithTimeout(); +#endif void moreActivitySignals_data(); void moreActivitySignals(); void contentEncoding_data(); void contentEncoding(); +#if QT_CONFIG(http) void contentEncodingBigPayload_data(); void contentEncodingBigPayload(); +#endif void cacheWithContentEncoding_data(); void cacheWithContentEncoding(); void downloadProgressWithContentEncoding_data(); @@ -560,11 +588,13 @@ private Q_SLOTS: void notFoundWithCompression_data(); void notFoundWithCompression(); +#if QT_CONFIG(http) void qhttpPartDebug_data(); void qhttpPartDebug(); void qtbug68821proxyError_data(); void qtbug68821proxyError(); +#endif void abortAndError(); @@ -1440,6 +1470,7 @@ void tst_QNetworkReply::storeSslConfiguration() } #endif +#if QT_CONFIG(http) QString tst_QNetworkReply::runMultipartRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply, QHttpMultiPart *multiPart, @@ -1471,6 +1502,7 @@ QString tst_QNetworkReply::runMultipartRequest(const QNetworkRequest &request, } return QString(); } +#endif QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, @@ -2756,7 +2788,7 @@ void tst_QNetworkReply::postToHttp() QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi"); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply; QFETCH(QByteArray, data); @@ -2783,7 +2815,7 @@ void tst_QNetworkReply::postToHttpSynchronous() QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi"); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); request.setAttribute( QNetworkRequest::SynchronousRequestAttribute, @@ -2805,6 +2837,7 @@ void tst_QNetworkReply::postToHttpSynchronous() QCOMPARE(uploadedData, md5sum.toHex()); } +#if QT_CONFIG(http) void tst_QNetworkReply::postToHttpMultipart_data() { QTest::addColumn<QUrl>("url"); @@ -2844,8 +2877,8 @@ void tst_QNetworkReply::postToHttpMultipart_data() QHttpMultiPart *customMultiPart = new QHttpMultiPart; customMultiPart->append(textPart); - expectedData = "header: Content-Type, value: 'text/plain'\n" - "header: Content-Disposition, value: 'form-data; name=\"text\"'\n" + expectedData = "header: content-type, value: 'text/plain'\n" + "header: content-disposition, value: 'form-data; name=\"text\"'\n" "content: 7 bytes\n" "\n"; QTest::newRow("text-custom") << url << customMultiPart << expectedData << QByteArray("custom"); @@ -2881,18 +2914,18 @@ void tst_QNetworkReply::postToHttpMultipart_data() multiPart3->append(textPart); multiPart3->append(textPart2); multiPart3->append(textPart3); - expectedData = "header: Content-Type, value: 'text/plain'\n" - "header: Content-Disposition, value: 'form-data; name=\"text\"'\n" + expectedData = "header: content-type, value: 'text/plain'\n" + "header: content-disposition, value: 'form-data; name=\"text\"'\n" "content: 7 bytes\n" "\n" - "header: Content-Type, value: 'text/plain'\n" - "header: myRawHeader, value: 'myValue'\n" - "header: Content-Disposition, value: 'form-data; name=\"text2\"'\n" + "header: content-type, value: 'text/plain'\n" + "header: myrawheader, value: 'myValue'\n" + "header: content-disposition, value: 'form-data; name=\"text2\"'\n" "content: some more bytes\n" "\n" - "header: Content-Type, value: 'text/plain'\n" - "header: Content-Disposition, value: 'form-data; name=\"text3\"'\n" - "header: Content-Location, value: 'http://my.test.location.tld'\n" + "header: content-type, value: 'text/plain'\n" + "header: content-disposition, value: 'form-data; name=\"text3\"'\n" + "header: content-location, value: 'http://my.test.location.tld'\n" "content: even more bytes\n\n"; QTest::newRow("text-text-text") << url << multiPart3 << expectedData << QByteArray("alternative"); @@ -3114,6 +3147,7 @@ void tst_QNetworkReply::multipartSkipIndices() // QTBUG-32534 } multiPart->deleteLater(); } +#endif void tst_QNetworkReply::postWithoutBody_data() { @@ -3156,6 +3190,7 @@ void tst_QNetworkReply::postWithoutBody() QCOMPARE(server.foundContentLength, client_data); } +#if QT_CONFIG(http) void tst_QNetworkReply::putToHttpMultipart_data() { postToHttpMultipart_data(); @@ -3200,6 +3235,7 @@ void tst_QNetworkReply::putToHttpMultipart() // QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above QCOMPARE(replyData, expectedReplyData); } +#endif #if QT_CONFIG(ssl) void tst_QNetworkReply::putToHttps_data() @@ -3311,7 +3347,7 @@ void tst_QNetworkReply::postToHttps() QSslConfiguration conf; conf.setCaCertificates(certs); request.setSslConfiguration(conf); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply; QFETCH(QByteArray, data); @@ -3345,7 +3381,7 @@ void tst_QNetworkReply::postToHttpsSynchronous() QSslConfiguration conf; conf.setCaCertificates(certs); request.setSslConfiguration(conf); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); request.setAttribute( QNetworkRequest::SynchronousRequestAttribute, @@ -3367,6 +3403,7 @@ void tst_QNetworkReply::postToHttpsSynchronous() QCOMPARE(uploadedData, md5sum.toHex()); } +#if QT_CONFIG(http) void tst_QNetworkReply::postToHttpsMultipart_data() { if (isSecureTransport) @@ -3417,7 +3454,7 @@ void tst_QNetworkReply::postToHttpsMultipart() expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n"); QCOMPARE(replyData, expectedReplyData); } - +#endif #endif // QT_CONFIG(ssl) void tst_QNetworkReply::deleteFromHttp_data() @@ -4461,7 +4498,7 @@ void tst_QNetworkReply::ioGetFromHttpWithCache_data() QByteArray reply200 = "HTTP/1.0 200\r\n" "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-control: no-store\r\n" "Content-length: 8\r\n" "\r\n" @@ -4596,7 +4633,7 @@ void tst_QNetworkReply::ioGetFromHttpWithCache_data() QByteArray reply206 = "HTTP/1.0 206\r\n" "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-control: no-cache\r\n" "Content-Range: bytes 2-6/8\r\n" "Content-length: 4\r\n" @@ -4999,6 +5036,7 @@ void tst_QNetworkReply::ioPutToFileFromSocket() QCOMPARE(contents, data); } +#if QT_CONFIG(localserver) void tst_QNetworkReply::ioPutToFileFromLocalSocket_data() { putToFile_data(); @@ -5042,6 +5080,7 @@ void tst_QNetworkReply::ioPutToFileFromLocalSocket() QByteArray contents = file.readAll(); QCOMPARE(contents, data); } +#endif // Currently no stdin/out supported for Windows CE. void tst_QNetworkReply::ioPutToFileFromProcess_data() @@ -5205,7 +5244,7 @@ void tst_QNetworkReply::ioPostToHttpFromFile() QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi"); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply(manager.post(request, &sourceFile)); @@ -5282,7 +5321,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocket() socketpair.endPoints[0]->write(data); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); manager.setProxy(proxy); QNetworkReplyPtr reply(manager.post(request, socketpair.endPoints[1])); @@ -5356,7 +5395,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous() QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi"); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); request.setAttribute( QNetworkRequest::SynchronousRequestAttribute, true); @@ -5387,7 +5426,7 @@ void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileToEnd() QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"; QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply(manager.post(request, &sourceFile)); connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), @@ -5413,7 +5452,7 @@ void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileFiveBytes() QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"; QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); // only send 5 bytes request.setHeader(QNetworkRequest::ContentLengthHeader, 5); QVERIFY(request.header(QNetworkRequest::ContentLengthHeader).isValid()); @@ -5444,7 +5483,7 @@ void tst_QNetworkReply::ioPostToHttpFromMiddleOfQBufferFiveBytes() QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"; QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply(manager.post(request, &uploadBuffer)); connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), @@ -5472,7 +5511,7 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag() QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"; QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); // disallow buffering request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, true); request.setHeader(QNetworkRequest::ContentLengthHeader, data.size()); @@ -5557,7 +5596,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() QUrl url = QUrl(QLatin1String("https://127.0.0.1:") + QString::number(server.serverPort()) + QLatin1Char('/')); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply(manager.post(request, sourceFile)); QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); @@ -5711,7 +5750,7 @@ void tst_QNetworkReply::ioPostToHttpUploadProgress() // create the request QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort())); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply(manager.post(request, &sourceFile)); QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); @@ -5779,7 +5818,7 @@ void tst_QNetworkReply::emitAllUploadProgressSignals() QUrl url = QUrl(QLatin1String("http://127.0.0.1:") + QString::number(server.serverPort()) + QLatin1Char('/')); QNetworkRequest normalRequest(url); - normalRequest.setRawHeader("Content-Type", "application/octet-stream"); + normalRequest.setRawHeader("content-type", "application/octet-stream"); QNetworkRequest catchAllSignalsRequest(normalRequest); catchAllSignalsRequest.setAttribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute, true); @@ -5830,7 +5869,7 @@ void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress() // create the request QUrl url = QUrl(QLatin1String("http://127.0.0.1:") + QString::number(server.serverPort()) + QLatin1Char('/')); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply(manager.post(request, &buffer)); QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); @@ -5876,7 +5915,11 @@ void tst_QNetworkReply::lastModifiedHeaderForFile() QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime(); - QCOMPARE(header, fileInfo.lastModified()); + QDateTime expected = fileInfo.lastModified(); + // remove msecs, HTTP dates don't support it + expected = expected.addMSecs(-expected.time().msec()); + + QCOMPARE(header.toUTC(), expected.toUTC()); } void tst_QNetworkReply::lastModifiedHeaderForHttp() @@ -6182,7 +6225,7 @@ void tst_QNetworkReply::receiveCookiesFromHttp() QByteArray data = cookieString.toLatin1() + '\n'; QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/set-cookie.cgi"); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); QNetworkReplyPtr reply; RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); @@ -6210,7 +6253,7 @@ void tst_QNetworkReply::receiveCookiesFromHttpSynchronous() QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/set-cookie.cgi"); QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); request.setAttribute( QNetworkRequest::SynchronousRequestAttribute, true); @@ -6639,7 +6682,6 @@ void tst_QNetworkReply::httpConnectionCount() int pendingConnectionCount = 0; - using namespace std::chrono_literals; const auto newPendingConnection = [&server]() { return server->hasPendingConnections(); }; // If we have http2 enabled then the second connection will take a little // longer to be established because we will wait for the first one to finish @@ -7488,6 +7530,7 @@ void tst_QNetworkReply::ioGetFromHttpBrokenChunkedEncoding() QCOMPARE(reply->error(), QNetworkReply::NoError); } +#if QT_CONFIG(http) // TODO: // Prepare a gzip that has one chunk that expands to the size mentioned in the bugreport. // Then have a custom HTTP server that waits after this chunk so the returning gets @@ -7516,6 +7559,7 @@ void tst_QNetworkReply::qtbug12908compressedHttpReply() QCOMPARE(reply->size(), qint64(16384)); QCOMPARE(reply->readAll(), QByteArray(16384, '\0')); } +#endif void tst_QNetworkReply::compressedHttpReplyBrokenGzip() { @@ -8042,11 +8086,12 @@ void tst_QNetworkReply::qtbug27161httpHeaderMayBeDamaged(){ QCOMPARE(reply->readAll(), QByteArray("ABC")); } +#if QT_CONFIG(networkdiskcache) void tst_QNetworkReply::qtbug28035browserDoesNotLoadQtProjectOrgCorrectly() { QByteArray getReply = "HTTP/1.1 200\r\n" "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-control: max-age = 6000\r\n" "\r\n" "GET"; @@ -8054,7 +8099,7 @@ void tst_QNetworkReply::qtbug28035browserDoesNotLoadQtProjectOrgCorrectly() { QByteArray postReply = "HTTP/1.1 200\r\n" "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-control: max-age = 6000\r\n" "Content-length: 4\r\n" "\r\n" @@ -8063,7 +8108,7 @@ void tst_QNetworkReply::qtbug28035browserDoesNotLoadQtProjectOrgCorrectly() { QByteArray putReply = "HTTP/1.1 201\r\n" "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-control: max-age = 6000\r\n" "\r\n"; @@ -8101,7 +8146,7 @@ void tst_QNetworkReply::qtbug28035browserDoesNotLoadQtProjectOrgCorrectly() { server.clearHeaderParserState(); server.setDataToTransmit(postReply); - request.setRawHeader("Content-Type", "text/plain"); + request.setRawHeader("content-type", "text/plain"); reply.reset(manager.post(request, postData)); QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); @@ -8166,6 +8211,7 @@ void tst_QNetworkReply::qtbug28035browserDoesNotLoadQtProjectOrgCorrectly() { QCOMPARE(reply->readAll(), QByteArray("GET")); QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), true); } +#endif void tst_QNetworkReply::qtbug45581WrongReplyStatusCode() { @@ -8551,6 +8597,7 @@ public: } }; +#if QT_CONFIG(http) void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280_data() { QTest::addColumn<int>("amount"); @@ -8591,13 +8638,14 @@ void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280() } QCOMPARE(server.receivedSockets.size(), amount); } +#endif void tst_QNetworkReply::dontInsertPartialContentIntoTheCache() { QByteArray reply206 = "HTTP/1.0 206\r\n" "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-control: no-cache\r\n" "Content-Range: bytes 2-6/8\r\n" "Content-length: 4\r\n" @@ -8654,7 +8702,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache() "WWW-Authenticate: Basic realm=\"QNetworkAccessManager Test Realm\"\r\n" "Content-Length: 4\r\n" "Connection: close\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "\r\n" "auth"; QRegularExpression rx("authorization: Basic ([^\r\n]*)\r\n"); @@ -8663,7 +8711,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache() if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") { dataToTransmit = "HTTP/1.0 200 OK\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Content-Length: 2\r\n" "\r\n" "OK"; @@ -9033,7 +9081,7 @@ void tst_QNetworkReply::ioHttpRedirectErrors_data() QTest::addColumn<QNetworkReply::NetworkError>("error"); QString tempRedirectReply = QString("HTTP/1.1 307 Temporary Redirect\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: http://localhost:%1\r\n\r\n"); QTest::newRow("too-many-redirects") << "http://localhost" << tempRedirectReply << QNetworkReply::TooManyRedirectsError; @@ -9341,7 +9389,7 @@ void tst_QNetworkReply::ioHttpRedirect() targetUrl.setPort(target.serverPort()); QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %2\r\n" "\r\n").arg(status, targetUrl.toString()); MiniHttpServer redirectServer(redirectReply.toLatin1(), false); @@ -9359,6 +9407,7 @@ void tst_QNetworkReply::ioHttpRedirect() QVERIFY(validateRedirectedResponseHeaders(reply)); } +#if QT_CONFIG(networkdiskcache) /* Test that, if we load a redirect from cache, we don't treat the request to the destination of the redirect as a redirect. @@ -9369,7 +9418,7 @@ void tst_QNetworkReply::ioHttpRedirectWithCache() { // Disallow caching the result so that the second request must also send the request QByteArray http200ResponseNoCache = "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "Cache-Control: no-cache\r\n" "\r\nHello"; @@ -9379,7 +9428,7 @@ void tst_QNetworkReply::ioHttpRedirectWithCache() // A cache-able redirect reply QString redirectReply = QStringLiteral("HTTP/1.1 308\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %1\r\n" "Cache-Control: max-age=3600\r\n" "\r\nYou're being redirected").arg(targetUrl.toString()); @@ -9416,6 +9465,7 @@ void tst_QNetworkReply::ioHttpRedirectWithCache() QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QVERIFY(validateRedirectedResponseHeaders(reply)); } +#endif void tst_QNetworkReply::ioHttpRedirectFromLocalToRemote() { @@ -9491,7 +9541,7 @@ void tst_QNetworkReply::ioHttpRedirectPostPut() QUrl targetUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi"); QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %2\r\n" "\r\n").arg(status, targetUrl.toString()); MiniHttpServer redirectServer(redirectReply.toLatin1()); @@ -9510,6 +9560,7 @@ void tst_QNetworkReply::ioHttpRedirectPostPut() QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); } +#if QT_CONFIG(http) void tst_QNetworkReply::ioHttpRedirectMultipartPost_data() { postToHttpMultipart_data(); @@ -9570,6 +9621,7 @@ void tst_QNetworkReply::ioHttpRedirectMultipartPost() // QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above QCOMPARE(replyData, expectedReplyData); } +#endif void tst_QNetworkReply::ioHttpRedirectDelete() { @@ -9645,7 +9697,7 @@ void tst_QNetworkReply::ioHttpRedirectWithUploadDevice() targetUrl.setPort(target.serverPort()); QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n" - "Content-Type: text/plain\r\n" + "content-type: text/plain\r\n" "location: %2\r\n" "\r\n").arg(status, targetUrl.toString()); MiniHttpServer redirectServer(redirectReply.toLatin1()); @@ -9679,8 +9731,8 @@ void tst_QNetworkReply::ioHttpRedirectWithUploadDevice() // we shouldn't send Content-Length with not content (esp. for GET) QVERIFY2(!target.receivedData.contains("Content-Length"), "Target server should not have received a Content-Length header"); - QVERIFY2(!target.receivedData.contains("Content-Type"), - "Target server should not have received a Content-Type header"); + QVERIFY2(!target.receivedData.contains("content-type"), + "Target server should not have received a content-type header"); } } @@ -10011,6 +10063,7 @@ void tst_QNetworkReply::autoDeleteReplies() } } +#if QT_CONFIG(http) || defined (Q_OS_WASM) void tst_QNetworkReply::requestWithTimeout_data() { using Operation = QNetworkAccessManager::Operation; @@ -10048,7 +10101,7 @@ void tst_QNetworkReply::requestWithTimeout() server.stopTransfer = true; QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); - request.setRawHeader("Content-Type", "application/octet-stream"); + request.setRawHeader("content-type", "application/octet-stream"); if (reqInt > 0) request.setTransferTimeout(reqInt); if (reqChrono > 0ms) @@ -10070,6 +10123,7 @@ void tst_QNetworkReply::requestWithTimeout() QCOMPARE(spy.size(), 1); QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError); } +#endif void tst_QNetworkReply::moreActivitySignals_data() { @@ -10100,7 +10154,7 @@ void tst_QNetworkReply::moreActivitySignals() QNetworkRequest request(url); QNetworkReplyPtr reply; if (postWithData) { - request.setRawHeader("Content-Type", "text/plain"); + request.setRawHeader("content-type", "text/plain"); reply.reset(manager.post(request, "Hello, world!")); } else { reply.reset(manager.get(request)); @@ -10121,7 +10175,7 @@ void tst_QNetworkReply::moreActivitySignals() // Second request will not send socketStartedConnecting because of keep-alive, so don't check it. QNetworkReplyPtr secondreply; if (postWithData) { - request.setRawHeader("Content-Type", "text/plain"); + request.setRawHeader("content-type", "text/plain"); secondreply.reset(manager.post(request, "Hello, world!")); } else { secondreply.reset(manager.get(request)); @@ -10242,6 +10296,7 @@ void tst_QNetworkReply::contentEncoding() } } +#if QT_CONFIG(http) void tst_QNetworkReply::contentEncodingBigPayload_data() { QTest::addColumn<QByteArray>("encoding"); @@ -10300,6 +10355,7 @@ void tst_QNetworkReply::contentEncodingBigPayload() } QCOMPARE(total, expectedSize); } +#endif void tst_QNetworkReply::cacheWithContentEncoding_data() { @@ -10482,6 +10538,7 @@ void tst_QNetworkReply::notFoundWithCompression() QCOMPARE(reply->readAll(), expected); } +#if QT_CONFIG(http) void tst_QNetworkReply::qhttpPartDebug_data() { QTest::addColumn<QByteArray>("header_data"); @@ -10583,6 +10640,7 @@ void tst_QNetworkReply::qtbug68821proxyError() QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).at(0), error); } +#endif void tst_QNetworkReply::abortAndError() { diff --git a/tests/auto/network/access/qnetworkreply_local/CMakeLists.txt b/tests/auto/network/access/qnetworkreply_local/CMakeLists.txt new file mode 100644 index 0000000000..13a60afb13 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply_local/CMakeLists.txt @@ -0,0 +1,9 @@ +qt_internal_add_test(tst_qnetworkreply_local + SOURCES + minihttpserver.h + tst_qnetworkreply_local.cpp + LIBRARIES + Qt::CorePrivate + Qt::NetworkPrivate + BUNDLE_ANDROID_OPENSSL_LIBS +) diff --git a/tests/auto/network/access/qnetworkreply_local/minihttpserver.h b/tests/auto/network/access/qnetworkreply_local/minihttpserver.h new file mode 100644 index 0000000000..eb0697a6f8 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply_local/minihttpserver.h @@ -0,0 +1,246 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MINIHTTPSERVER_H +#define MINIHTTPSERVER_H + +#include <QtNetwork/qtnetworkglobal.h> + +#include <QtNetwork/qtcpserver.h> +#include <QtNetwork/qtcpsocket.h> +#include <QtNetwork/qlocalsocket.h> +#if QT_CONFIG(ssl) +# include <QtNetwork/qsslsocket.h> +#endif +#if QT_CONFIG(localserver) +# include <QtNetwork/qlocalserver.h> +#endif + +#include <QtCore/qpointer.h> +#include <QtCore/qhash.h> + +#include <utility> + +static inline QByteArray default200Response() +{ + return QByteArrayLiteral("HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 12\r\n" + "\r\n" + "Hello World!"); +} +class MiniHttpServerV2 : public QObject +{ + Q_OBJECT + +public: + struct State; + +#if QT_CONFIG(localserver) + void bind(QLocalServer *server) + { + Q_ASSERT(!localServer); + localServer = server; + connect(server, &QLocalServer::newConnection, this, + &MiniHttpServerV2::incomingLocalConnection); + } +#endif + + void bind(QTcpServer *server) + { + Q_ASSERT(!tcpServer); + tcpServer = server; + connect(server, &QTcpServer::pendingConnectionAvailable, this, + &MiniHttpServerV2::incomingConnection); + } + + void setDataToTransmit(QByteArray data) { dataToTransmit = std::move(data); } + + void clearServerState() + { + auto copy = std::exchange(clientStates, {}); + for (auto [socket, _] : copy.asKeyValueRange()) { + if (auto *tcpSocket = qobject_cast<QTcpSocket *>(socket)) + tcpSocket->disconnectFromHost(); + else if (auto *localSocket = qobject_cast<QLocalSocket *>(socket)) + localSocket->disconnectFromServer(); + else + Q_UNREACHABLE_RETURN(); + socket->deleteLater(); + } + } + + bool hasPendingConnections() const + { + return +#if QT_CONFIG(localserver) + (localServer && localServer->hasPendingConnections()) || +#endif + (tcpServer && tcpServer->hasPendingConnections()); + } + + QString addressForScheme(QStringView scheme) const + { + using namespace Qt::StringLiterals; + if (scheme.startsWith("unix"_L1) || scheme.startsWith("local"_L1)) { +#if QT_CONFIG(localserver) + if (localServer) + return localServer->serverName(); +#endif + } else if (scheme == "http"_L1) { + if (tcpServer) + return "%1:%2"_L1.arg(tcpServer->serverAddress().toString(), + QString::number(tcpServer->serverPort())); + } + return {}; + } + + QList<State> peerStates() const { return clientStates.values(); } + +protected: +#if QT_CONFIG(localserver) + void incomingLocalConnection() + { + auto *socket = localServer->nextPendingConnection(); + connectSocketSignals(socket); + } +#endif + + void incomingConnection() + { + auto *socket = tcpServer->nextPendingConnection(); + connectSocketSignals(socket); + } + + void reply(QIODevice *socket) + { + Q_ASSERT(socket); + if (dataToTransmit.isEmpty()) { + emit socket->bytesWritten(0); // emulate having written the data + return; + } + if (!stopTransfer) + socket->write(dataToTransmit); + } + +private: + void connectSocketSignals(QIODevice *socket) + { + connect(socket, &QIODevice::readyRead, this, [this, socket]() { readyReadSlot(socket); }); + connect(socket, &QIODevice::bytesWritten, this, + [this, socket]() { bytesWrittenSlot(socket); }); +#if QT_CONFIG(ssl) + if (auto *sslSocket = qobject_cast<QSslSocket *>(socket)) + connect(sslSocket, &QSslSocket::sslErrors, this, &MiniHttpServerV2::slotSslErrors); +#endif + + if (auto *tcpSocket = qobject_cast<QTcpSocket *>(socket)) { + connect(tcpSocket, &QAbstractSocket::errorOccurred, this, &MiniHttpServerV2::slotError); + } else if (auto *localSocket = qobject_cast<QLocalSocket *>(socket)) { + connect(localSocket, &QLocalSocket::errorOccurred, this, + [this](QLocalSocket::LocalSocketError error) { + slotError(QAbstractSocket::SocketError(error)); + }); + } else { + Q_UNREACHABLE_RETURN(); + } + } + + void parseContentLength(State &st, QByteArrayView header) + { + qsizetype index = header.indexOf("\r\ncontent-length:"); + if (index == -1) + return; + st.foundContentLength = true; + + index += sizeof("\r\ncontent-length:") - 1; + const auto *end = std::find(header.cbegin() + index, header.cend(), '\r'); + QByteArrayView num = header.mid(index, std::distance(header.cbegin() + index, end)); + bool ok = false; + st.contentLength = num.toInt(&ok); + if (!ok) + st.contentLength = -1; + } + +private slots: +#if QT_CONFIG(ssl) + void slotSslErrors(const QList<QSslError> &errors) + { + QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(sender()); + Q_ASSERT(currentClient); + qDebug() << "slotSslErrors" << currentClient->errorString() << errors; + } +#endif + void slotError(QAbstractSocket::SocketError err) + { + QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(sender()); + Q_ASSERT(currentClient); + qDebug() << "slotError" << err << currentClient->errorString(); + } + +public slots: + + void readyReadSlot(QIODevice *socket) + { + if (stopTransfer) + return; + State &st = clientStates[socket]; + st.receivedData += socket->readAll(); + const qsizetype doubleEndlPos = st.receivedData.indexOf("\r\n\r\n"); + + if (doubleEndlPos != -1) { + const qsizetype endOfHeader = doubleEndlPos + 4; + st.contentRead = st.receivedData.size() - endOfHeader; + + if (!st.checkedContentLength) { + parseContentLength(st, QByteArrayView(st.receivedData).first(endOfHeader)); + st.checkedContentLength = true; + } + + if (st.contentRead < st.contentLength) + return; + + // multiple requests incoming, remove the bytes of the current one + if (multiple) + st.receivedData.remove(0, endOfHeader); + + reply(socket); + } + } + + void bytesWrittenSlot(QIODevice *socket) + { + // Disconnect and delete in next cycle (else Windows clients will fail with + // RemoteHostClosedError). + if (doClose && socket->bytesToWrite() == 0) { + disconnect(socket, nullptr, this, nullptr); + socket->deleteLater(); + } + } + +private: + QByteArray dataToTransmit = default200Response(); + + QTcpServer *tcpServer = nullptr; +#if QT_CONFIG(localserver) + QLocalServer *localServer = nullptr; +#endif + + QHash<QIODevice *, State> clientStates; + +public: + struct State + { + QByteArray receivedData; + qsizetype contentLength = 0; + qsizetype contentRead = 0; + bool checkedContentLength = false; + bool foundContentLength = false; + }; + + bool doClose = true; + bool multiple = false; + bool stopTransfer = false; +}; + +#endif // MINIHTTPSERVER_H diff --git a/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp new file mode 100644 index 0000000000..6d78c81593 --- /dev/null +++ b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtNetwork/qtnetworkglobal.h> + +#include <QtTest/qtest.h> + +#include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/qnetworkaccessmanager.h> + +#include "minihttpserver.h" + +using namespace Qt::StringLiterals; + +/* + The tests here are meant to be self-contained, using servers in the same + process if needed. This enables externals to more easily run the tests too. +*/ +class tst_QNetworkReply_local : public QObject +{ + Q_OBJECT +private slots: + void initTestCase_data(); + + void get(); + void post(); + +#if QT_CONFIG(localserver) + void fullServerName_data(); + void fullServerName(); +#endif +}; + +void tst_QNetworkReply_local::initTestCase_data() +{ + QTest::addColumn<QString>("scheme"); + + QTest::newRow("http") << "http"; +#if QT_CONFIG(localserver) + QTest::newRow("unix") << "unix+http"; + QTest::newRow("local") << "local+http"; // equivalent to unix, but test that it works +#endif +} + +static std::unique_ptr<MiniHttpServerV2> getServerForCurrentScheme() +{ + auto server = std::make_unique<MiniHttpServerV2>(); + QFETCH_GLOBAL(QString, scheme); + if (scheme.startsWith("unix"_L1) || scheme.startsWith("local"_L1)) { +#if QT_CONFIG(localserver) + QLocalServer *localServer = new QLocalServer(server.get()); + localServer->listen(u"qt_networkreply_test_"_s + % QLatin1StringView(QTest::currentTestFunction()) + % QString::number(QCoreApplication::applicationPid())); + server->bind(localServer); +#endif + } else if (scheme == "http") { + QTcpServer *tcpServer = new QTcpServer(server.get()); + tcpServer->listen(QHostAddress::LocalHost, 0); + server->bind(tcpServer); + } + return server; +} + +static QUrl getUrlForCurrentScheme(MiniHttpServerV2 *server) +{ + QFETCH_GLOBAL(QString, scheme); + const QString address = server->addressForScheme(scheme); + const QString urlString = QLatin1StringView("%1://%2").arg(scheme, address); + return { urlString }; +} + +void tst_QNetworkReply_local::get() +{ + std::unique_ptr<MiniHttpServerV2> server = getServerForCurrentScheme(); + const QUrl url = getUrlForCurrentScheme(server.get()); + + QNetworkAccessManager manager; + std::unique_ptr<QNetworkReply> reply(manager.get(QNetworkRequest(url))); + + const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); }); + QVERIFY(res); + + QCOMPARE(reply->readAll(), QByteArray("Hello World!")); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); +} + +void tst_QNetworkReply_local::post() +{ + std::unique_ptr<MiniHttpServerV2> server = getServerForCurrentScheme(); + const QUrl url = getUrlForCurrentScheme(server.get()); + + QNetworkAccessManager manager; + const QByteArray payload = "Hello from the other side!"_ba; + QNetworkRequest req(url); + req.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); + std::unique_ptr<QNetworkReply> reply(manager.post(req, payload)); + + const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); }); + QVERIFY(res); + + QCOMPARE(reply->readAll(), QByteArray("Hello World!")); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + auto states = server->peerStates(); + QCOMPARE(states.size(), 1); + + const auto &firstRequest = states.at(0); + + QVERIFY(firstRequest.checkedContentLength); + QCOMPARE(firstRequest.contentLength, payload.size()); + QCOMPARE_GT(firstRequest.receivedData.size(), payload.size() + 4); + QCOMPARE(firstRequest.receivedData.last(payload.size() + 4), "\r\n\r\n" + payload); +} + +#if QT_CONFIG(localserver) +void tst_QNetworkReply_local::fullServerName_data() +{ +#if defined(Q_OS_ANDROID) || defined(QT_PLATFORM_UIKIT) + QSKIP("While partially supported, the test as-is doesn't make sense on this platform."); +#else + + QTest::addColumn<QString>("hostAndPath"); + + QTest::newRow("dummy-host") << u"://irrelevant/test"_s; + QTest::newRow("no-host") << u":///test"_s; +#endif +} + +void tst_QNetworkReply_local::fullServerName() +{ + QFETCH_GLOBAL(QString, scheme); + if (!scheme.startsWith("unix"_L1) && !scheme.startsWith("local"_L1)) + return; // only relevant for local sockets + + MiniHttpServerV2 server; + QLocalServer localServer; + + QString path; +#ifdef Q_OS_WIN + path = uR"(\\.\pipe\qt_networkreply_test_fullServerName)"_s + % QString::number(QCoreApplication::applicationPid()); +#else + path = u"/tmp/qt_networkreply_test_fullServerName"_s + % QString::number(QCoreApplication::applicationPid()) % u".sock"_s; +#endif + + QVERIFY(localServer.listen(path)); + server.bind(&localServer); + + QFETCH(QString, hostAndPath); + QUrl url(scheme % hostAndPath); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::FullLocalServerNameAttribute, path); + + QNetworkAccessManager manager; + std::unique_ptr<QNetworkReply> reply(manager.get(req)); + + const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); }); + QVERIFY(res); + + QCOMPARE(reply->readAll(), QByteArray("Hello World!")); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + const QByteArray receivedData = server.peerStates().at(0).receivedData; + const QByteArray expectedGet = "GET " % url.path().toUtf8() % " HTTP/1.1\r\n"; + QVERIFY(receivedData.startsWith(expectedGet)); + + const QByteArray expectedHost = "host: " % url.host().toUtf8() % "\r\n"; + QVERIFY(receivedData.contains(expectedHost)); +} +#endif + +QTEST_MAIN(tst_QNetworkReply_local) + +#include "tst_qnetworkreply_local.moc" +#include "moc_minihttpserver.cpp" diff --git a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp index ed978e6b0f..f0b02ae91d 100644 --- a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp +++ b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp @@ -3,6 +3,10 @@ #include <QTest> +#if QT_CONFIG(http) +#include <QtNetwork/QHttp1Configuration> +#include <QtNetwork/QHttp2Configuration> +#endif #include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkCookie> @@ -33,6 +37,8 @@ private slots: void originatingObject(); void setHeaders_data(); void setHeaders(); + void operatorEqual_data(); + void operatorEqual(); void removeHeader(); }; @@ -349,7 +355,7 @@ void tst_QNetworkRequest::rawHeaderParsing_data() << true << "Content-Type" << "text/html"; QTest::newRow("Content-Length") << QNetworkRequest::ContentLengthHeader << QVariant(qint64(1)) - << true << "Content-Length" << " 1 "; + << true << "Content-Length" << "1"; QTest::newRow("Location") << QNetworkRequest::LocationHeader << QVariant(QUrl("http://foo/with space")) << true << "Location" << "http://foo/with%20space"; @@ -581,5 +587,128 @@ void tst_QNetworkRequest::setHeaders() QCOMPARE(r2.headers().toListOfPairs(), h.toListOfPairs()); } +void tst_QNetworkRequest::operatorEqual_data() +{ + QTest::addColumn<QNetworkRequest>("a"); + QTest::addColumn<QNetworkRequest>("b"); + QTest::addColumn<bool>("expectedToMatch"); + QTest::newRow("null") << QNetworkRequest() << QNetworkRequest() << true; + + QNetworkRequest data1; + data1.setUrl(QUrl("http://qt-project.org")); + QTest::newRow("url-1-1") << data1 << QNetworkRequest() << false; + QTest::newRow("url-1-2") << data1 << data1 << true; + + QNetworkRequest data2; + QHttpHeaders headers; + headers.append("name1", "value1"); + data2.setHeaders(headers); + QTest::newRow("headers-2-1") << data2 << QNetworkRequest() << false; + QTest::newRow("headers-2-2") << data2 << data2 << true; + QTest::newRow("headers-2-3") << data2 << data1 << false; + + QNetworkRequest data3; + data3.setPeerVerifyName("peerName"); + QTest::newRow("peerName-3-1") << data3 << QNetworkRequest() << false; + QTest::newRow("peerName-3-2") << data3 << data3 << true; + QTest::newRow("peerName-3-3") << data3 << data1 << false; + QTest::newRow("peerName-3-4") << data3 << data2 << false; + + QNetworkRequest data4; + data4.setAttribute(QNetworkRequest::Http2AllowedAttribute, true); + QTest::newRow("attribute-4-1") << data4 << QNetworkRequest() << false; + QTest::newRow("attribute-4-2") << data4 << data4 << true; + QTest::newRow("attribute-4-3") << data4 << data1 << false; + QTest::newRow("attribute-4-4") << data4 << data2 << false; + QTest::newRow("attribute-4-5") << data4 << data3 << false; + + QNetworkRequest data5; + data5.setPriority(QNetworkRequest::Priority::HighPriority); + QTest::newRow("priority-5-1") << data5 << QNetworkRequest() << false; + QTest::newRow("priority-5-2") << data5 << data5 << true; + QTest::newRow("priority-5-3") << data5 << data1 << false; + QTest::newRow("priority-5-4") << data5 << data2 << false; + QTest::newRow("priority-5-5") << data5 << data3 << false; + QTest::newRow("priority-5-6") << data5 << data4 << false; + + QNetworkRequest data6; + data6.setMaximumRedirectsAllowed(3); + QTest::newRow("maxRedirects-6-1") << data6 << QNetworkRequest() << false; + QTest::newRow("maxRedirects-6-2") << data6 << data6 << true; + QTest::newRow("maxRedirects-6-3") << data6 << data1 << false; + QTest::newRow("maxRedirects-6-4") << data6 << data2 << false; + QTest::newRow("maxRedirects-6-5") << data6 << data3 << false; + QTest::newRow("maxRedirects-6-6") << data6 << data4 << false; + QTest::newRow("maxRedirects-6-7") << data6 << data5 << false; + +#if QT_CONFIG(http) + QNetworkRequest data7; + QHttp1Configuration http1Configuration; + http1Configuration.setNumberOfConnectionsPerHost(5); + data7.setHttp1Configuration(http1Configuration); + QTest::newRow("http1Config-7-1") << data7 << QNetworkRequest() << false; + QTest::newRow("http1Config-7-2") << data7 << data7 << true; + QTest::newRow("http1Config-7-3") << data7 << data1 << false; + QTest::newRow("http1Config-7-4") << data7 << data2 << false; + QTest::newRow("http1Config-7-5") << data7 << data3 << false; + QTest::newRow("http1Config-7-6") << data7 << data4 << false; + QTest::newRow("http1Config-7-7") << data7 << data5 << false; + QTest::newRow("http1Config-7-8") << data7 << data6 << false; + + QNetworkRequest data8; + QHttp2Configuration http2Configuration; + http2Configuration.setMaxFrameSize(16386); + data8.setHttp2Configuration(http2Configuration); + QTest::newRow("http2Config-8-1") << data8 << QNetworkRequest() << false; + QTest::newRow("http2Config-8-2") << data8 << data8 << true; + QTest::newRow("http2Config-8-3") << data8 << data1 << false; + QTest::newRow("http2Config-8-4") << data8 << data2 << false; + QTest::newRow("http2Config-8-5") << data8 << data3 << false; + QTest::newRow("http2Config-8-6") << data8 << data4 << false; + QTest::newRow("http2Config-8-7") << data8 << data5 << false; + QTest::newRow("http2Config-8-8") << data8 << data6 << false; + QTest::newRow("http2Config-8-9") << data8 << data7 << false; + + QNetworkRequest data9; + data9.setDecompressedSafetyCheckThreshold(-1); + QTest::newRow("threshold-9-1") << data9 << QNetworkRequest() << false; + QTest::newRow("threshold-9-2") << data9 << data9 << true; + QTest::newRow("threshold-9-3") << data9 << data1 << false; + QTest::newRow("threshold-9-4") << data9 << data2 << false; + QTest::newRow("threshold-9-5") << data9 << data3 << false; + QTest::newRow("threshold-9-6") << data9 << data4 << false; + QTest::newRow("threshold-9-7") << data9 << data5 << false; + QTest::newRow("threshold-9-8") << data9 << data6 << false; + QTest::newRow("threshold-9-9") << data9 << data7 << false; + QTest::newRow("threshold-9-10") << data9 << data8 << false; +#endif + +#if QT_CONFIG(http) || defined (Q_OS_WASM) + QNetworkRequest data10; + data10.setTransferTimeout(50000); + QTest::newRow("timeout-10-1") << data10 << QNetworkRequest() << false; + QTest::newRow("timeout-10-2") << data10 << data10 << true; + QTest::newRow("timeout-10-3") << data10 << data1 << false; + QTest::newRow("timeout-10-4") << data10 << data2 << false; + QTest::newRow("timeout-10-5") << data10 << data3 << false; + QTest::newRow("timeout-10-6") << data10 << data4 << false; + QTest::newRow("timeout-10-7") << data10 << data5 << false; + QTest::newRow("timeout-10-8") << data10 << data6 << false; + QTest::newRow("timeout-10-9") << data10 << data7 << false; + QTest::newRow("timeout-10-10") << data10 << data8 << false; + QTest::newRow("timeout-10-11") << data10 << data9 << false; +#endif +} + +// public bool operator==(const QNetworkRequest &other) const +void tst_QNetworkRequest::operatorEqual() +{ + QFETCH(QNetworkRequest, a); + QFETCH(QNetworkRequest, b); + QFETCH(bool, expectedToMatch); + + QCOMPARE(a == b, expectedToMatch); +} + QTEST_MAIN(tst_QNetworkRequest) #include "tst_qnetworkrequest.moc" diff --git a/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp b/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp index f71e94862c..af3a74a498 100644 --- a/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp +++ b/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp @@ -13,6 +13,13 @@ #include <QtNetwork/QNetworkDatagram> #include <QtNetwork/QUdpSocket> +#if QT_CONFIG(networkproxy) +# include <QtNetwork/QNetworkProxyFactory> +#endif +#if QT_CONFIG(ssl) +# include <QtNetwork/QSslSocket> +#endif + #ifdef Q_OS_UNIX # include <QtCore/QFile> #else @@ -37,8 +44,11 @@ class tst_QDnsLookup: public QObject QStringList domainNameListAlternatives(const QString &input); std::unique_ptr<QDnsLookup> lookupCommon(QDnsLookup::Type type, const QString &domain, - const QHostAddress &server = {}, quint16 port = 53); + const QHostAddress &server = {}, quint16 port = 0, + QDnsLookup::Protocol protocol = QDnsLookup::Standard); QStringList formatReply(const QDnsLookup *lookup) const; + + void setNameserver_helper(QDnsLookup::Protocol protocol); public slots: void initTestCase(); @@ -57,6 +67,8 @@ private slots: void setNameserverLoopback(); void setNameserver_data(); void setNameserver(); + void dnsOverTls_data(); + void dnsOverTls(); void bindingsAndProperties(); void automatedBindings(); }; @@ -74,9 +86,11 @@ static const char preparedDnsQuery[] = "\x00\x00\x06\x00\x01" // <root domain> IN SOA ; -static QList<QHostAddress> systemNameservers() +static QList<QHostAddress> systemNameservers(QDnsLookup::Protocol protocol) { QList<QHostAddress> result; + if (protocol != QDnsLookup::Standard) + return result; #ifdef Q_OS_WIN ULONG infosize = 0; @@ -113,7 +127,7 @@ static QList<QHostAddress> systemNameservers() return result; } -static QList<QHostAddress> globalPublicNameservers() +static QList<QHostAddress> globalPublicNameservers(QDnsLookup::Protocol proto) { const char *const candidates[] = { // Google's dns.google @@ -128,6 +142,56 @@ static QList<QHostAddress> globalPublicNameservers() //"9.9.9.9", "2620:fe::9", }; + auto udpSendAndReceive = [](const QHostAddress &addr, QByteArray &data) { + QUdpSocket socket; + socket.connectToHost(addr, 53); + if (socket.waitForConnected(1)) + socket.write(data); + + if (!socket.waitForReadyRead(1000)) + return socket.errorString(); + + QNetworkDatagram dgram = socket.receiveDatagram(); + if (!dgram.isValid()) + return socket.errorString(); + + data = dgram.data(); + return QString(); + }; + + auto tlsSendAndReceive = [](const QHostAddress &addr, QByteArray &data) { +#if QT_CONFIG(ssl) + QSslSocket socket; + QDeadlineTimer timeout(2000); + socket.connectToHostEncrypted(addr.toString(), 853); + if (!socket.waitForEncrypted(2000)) + return socket.errorString(); + + quint16 size = qToBigEndian<quint16>(data.size()); + socket.write(reinterpret_cast<char *>(&size), sizeof(size)); + socket.write(data); + + if (!socket.waitForReadyRead(timeout.remainingTime())) + return socket.errorString(); + if (socket.bytesAvailable() < 2) + return u"protocol error"_s; + + socket.read(reinterpret_cast<char *>(&size), sizeof(size)); + size = qFromBigEndian(size); + + while (socket.bytesAvailable() < size) { + int remaining = timeout.remainingTime(); + if (remaining < 0 || !socket.waitForReadyRead(remaining)) + return socket.errorString(); + } + + data = socket.readAll(); + return QString(); +#else + return u"SSL/TLS support not compiled in"_s; +#endif + }; + QList<QHostAddress> result; QRandomGenerator &rng = *QRandomGenerator::system(); for (auto name : candidates) { @@ -138,23 +202,18 @@ static QList<QHostAddress> globalPublicNameservers() char *ptr = data.data(); qToBigEndian(id, ptr); - QUdpSocket socket; - socket.connectToHost(addr, 53); - if (socket.waitForConnected(1)) - socket.write(data); - - if (!socket.waitForReadyRead(1000)) { - qDebug() << addr << "discarded:" << socket.errorString(); - continue; - } - - QNetworkDatagram dgram = socket.receiveDatagram(); - if (!dgram.isValid()) { - qDebug() << addr << "discarded:" << socket.errorString(); + QString errorString = [&] { + switch (proto) { + case QDnsLookup::Standard: return udpSendAndReceive(addr, data); + case QDnsLookup::DnsOverTls: return tlsSendAndReceive(addr, data); + } + Q_UNREACHABLE(); + }(); + if (!errorString.isEmpty()) { + qDebug() << addr << "discarded:" << errorString; continue; } - data = dgram.data(); ptr = data.data(); if (data.size() < HeaderSize) { qDebug() << addr << "discarded: reply too small"; @@ -181,6 +240,11 @@ void tst_QDnsLookup::initTestCase() { if (qgetenv("QTEST_ENVIRONMENT") == "ci") dnsServersMustWork = true; + +#if QT_CONFIG(networkproxy) + // for DNS-over-TLS + QNetworkProxyFactory::setUseSystemConfiguration(true); +#endif } QString tst_QDnsLookup::domainName(const QString &input) @@ -221,9 +285,10 @@ QStringList tst_QDnsLookup::domainNameListAlternatives(const QString &input) std::unique_ptr<QDnsLookup> tst_QDnsLookup::lookupCommon(QDnsLookup::Type type, const QString &domain, - const QHostAddress &server, quint16 port) + const QHostAddress &server, quint16 port, + QDnsLookup::Protocol protocol) { - auto lookup = std::make_unique<QDnsLookup>(type, domainName(domain), server, port); + auto lookup = std::make_unique<QDnsLookup>(type, domainName(domain), protocol, server, port); QObject::connect(lookup.get(), &QDnsLookup::finished, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); lookup->lookup(); @@ -309,6 +374,14 @@ QStringList tst_QDnsLookup::formatReply(const QDnsLookup *lookup) const result.append(std::move(entry)); } + for (const QDnsTlsAssociationRecord &rr : lookup->tlsAssociationRecords()) { + QString entry = u"TLSA %1 %2 %3 %4"_s.arg(int(rr.usage())).arg(int(rr.selector())) + .arg(int(rr.matchType())).arg(rr.value().toHex().toUpper()); + if (rr.name() != domain) + entry = "TLSA unexpected label to "_L1 + rr.name(); + result.append(std::move(entry)); + } + result.sort(); return result; } @@ -439,6 +512,10 @@ void tst_QDnsLookup::lookup_data() "SRV 2 50 7 aaaa-single;" "SRV 3 50 7 a-multi"; + QTest::newRow("tlsa") << QDnsLookup::Type::TLSA << "_25._tcp.multi" + << "TLSA 3 1 1 0123456789ABCDEFFEDCBA9876543210" + "0123456789ABCDEFFEDCBA9876543210"; + QTest::newRow("txt-single") << QDnsLookup::TXT << "txt-single" << "TXT \"Hello\""; QTest::newRow("txt-multi-onerr") << QDnsLookup::TXT << "txt-multi-onerr" @@ -457,8 +534,12 @@ void tst_QDnsLookup::lookup() if (!lookup) return; - QCOMPARE(lookup->error(), QDnsLookup::NoError); +#ifdef Q_OS_WIN + if (QTest::currentDataTag() == "tlsa"_L1) + QSKIP("WinDNS doesn't work properly with TLSA records and we don't know why"); +#endif QCOMPARE(lookup->errorString(), QString()); + QCOMPARE(lookup->error(), QDnsLookup::NoError); QCOMPARE(lookup->type(), type); QCOMPARE(lookup->name(), domainName(domain)); @@ -591,31 +672,59 @@ void tst_QDnsLookup::setNameserverLoopback() QCOMPARE(lookup.error(), QDnsLookup::NotFoundError); } -void tst_QDnsLookup::setNameserver_data() +template <QDnsLookup::Protocol Protocol> +static void setNameserver_data_helper(const QByteArray &protoName) { - static QList<QHostAddress> servers = systemNameservers() + globalPublicNameservers(); + if (!QDnsLookup::isProtocolSupported(Protocol)) + QSKIP(protoName + " not supported"); + + static QList<QHostAddress> servers = systemNameservers(Protocol) + + globalPublicNameservers(Protocol); QTest::addColumn<QHostAddress>("server"); if (servers.isEmpty()) { - QSKIP("No reachable DNS servers were found"); + QSKIP("No reachable " + protoName + " servers were found"); } else { for (const QHostAddress &h : std::as_const(servers)) QTest::addRow("%s", qUtf8Printable(h.toString())) << h; } } -void tst_QDnsLookup::setNameserver() +void tst_QDnsLookup::setNameserver_data() +{ + setNameserver_data_helper<QDnsLookup::Standard>("DNS"); +} + +void tst_QDnsLookup::setNameserver_helper(QDnsLookup::Protocol protocol) { QFETCH(QHostAddress, server); + QElapsedTimer timer; + timer.start(); std::unique_ptr<QDnsLookup> lookup = - lookupCommon(QDnsLookup::Type::A, "a-single", server); + lookupCommon(QDnsLookup::Type::A, "a-single", server, 0, protocol); if (!lookup) return; + qDebug() << "Lookup took" << timer.elapsed() << "ms"; QCOMPARE(lookup->error(), QDnsLookup::NoError); QString result = formatReply(lookup.get()).join(';'); QCOMPARE(result, "A 192.0.2.1"); } +void tst_QDnsLookup::setNameserver() +{ + setNameserver_helper(QDnsLookup::Standard); +} + +void tst_QDnsLookup::dnsOverTls_data() +{ + setNameserver_data_helper<QDnsLookup::DnsOverTls>("DNS-over-TLS"); +} + +void tst_QDnsLookup::dnsOverTls() +{ + setNameserver_helper(QDnsLookup::DnsOverTls); +} + void tst_QDnsLookup::bindingsAndProperties() { QDnsLookup lookup; diff --git a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp index 141ca25021..e5bbf3467c 100644 --- a/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp +++ b/tests/auto/network/kernel/qnetworkinterface/tst_qnetworkinterface.cpp @@ -197,6 +197,14 @@ void tst_QNetworkInterface::localAddress_data() } else if (!ipv6 || entry.prefixLength() != 64) { continue; } else { +#ifdef Q_OS_ANDROID + // Android seem to not allow IPv6 connection from interfaces other than wlan, + // if it's connected, and wlan is connected by default on Android emulators, + // so prefer selecting wlan in this test. + const QString scopeId = addr.scopeId(); + if (!scopeId.isEmpty() && !scopeId.startsWith("wlan")) + continue; +#endif // add a random node in this IPv6 network quint64 randomid = qFromBigEndian(Q_UINT64_C(0x8f41f072e5733caa)); QIPv6Address ip6 = addr.toIPv6Address(); diff --git a/tests/auto/network/socket/CMakeLists.txt b/tests/auto/network/socket/CMakeLists.txt index 06c356c08f..7136017f39 100644 --- a/tests/auto/network/socket/CMakeLists.txt +++ b/tests/auto/network/socket/CMakeLists.txt @@ -14,10 +14,13 @@ add_subdirectory(qabstractsocket) if(QT_FEATURE_localserver AND NOT ANDROID) # QTBUG-87387 add_subdirectory(qlocalsocket) +endif() +if(QT_FEATURE_networkinterface AND NOT ANDROID) # QTBUG-87388 add_subdirectory(qtcpserver) endif() + if(QT_FEATURE_sctp) add_subdirectory(qsctpsocket) endif() diff --git a/tests/auto/other/CMakeLists.txt b/tests/auto/other/CMakeLists.txt index 16f96cd5f4..e261ea234d 100644 --- a/tests/auto/other/CMakeLists.txt +++ b/tests/auto/other/CMakeLists.txt @@ -1,9 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -if(NOT CMAKE_CROSSCOMPILING) - # add_subdirectory(atwrapper) <- does not exist -endif() if(TARGET Qt::Widgets) add_subdirectory(gestures) add_subdirectory(languagechange) diff --git a/tests/auto/other/android_deployment_settings/CMakeLists.txt b/tests/auto/other/android_deployment_settings/CMakeLists.txt index f2ba135328..9ef457189a 100644 --- a/tests/auto/other/android_deployment_settings/CMakeLists.txt +++ b/tests/auto/other/android_deployment_settings/CMakeLists.txt @@ -30,6 +30,7 @@ set_target_properties(${target} PROPERTIES QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2" QT_ANDROID_MIN_SDK_VERSION "1" QT_ANDROID_TARGET_SDK_VERSION "2" + QT_ANDROID_PACKAGE_NAME "org.qtproject.android_deployment_settings_test" QT_ANDROID_DEPLOYMENT_DEPENDENCIES "dep1.so;dep2.so;dep3.so" QT_ANDROID_DEPLOYMENT_SETTINGS_FILE "attempt_to_rewrite.json" QT_ANDROID_EXTRA_LIBS @@ -53,6 +54,7 @@ set_target_properties(${target} PROPERTIES QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2" QT_ANDROID_MIN_SDK_VERSION "1" QT_ANDROID_TARGET_SDK_VERSION "2" + QT_ANDROID_PACKAGE_NAME "org.qtproject.android_deployment_settings_test" QT_ANDROID_DEPLOYMENT_DEPENDENCIES "dep1.so;dep2.so;dep3.so" QT_ANDROID_EXTRA_LIBS "some/path/to/lib1.so;some/path\\to/lib2.so;some\\path\\to\\lib3.so;some/path/to/lib4.so" diff --git a/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp b/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp index d68d08b58e..f8428aaf43 100644 --- a/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp +++ b/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp @@ -76,6 +76,8 @@ void tst_android_deployment_settings::DeploymentSettings_data() << "1"; QTest::newRow("android-target-sdk-version") << "android-target-sdk-version" << "2"; + QTest::newRow("android-package-name") << "android-package-name" + << "org.qtproject.android_deployment_settings_test"; } void tst_android_deployment_settings::DeploymentSettings() diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp index 5fd695e2e6..cab88e2fc0 100644 --- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp +++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp @@ -966,6 +966,13 @@ void tst_QAccessibility::mainWindowTest() QCOMPARE(iface->text(QAccessible::Name), name); QCOMPARE(iface->role(), QAccessible::Window); QVERIFY(iface->state().active); + + QTestAccessibility::clearEvents(); + QLatin1String newName = QLatin1String("Main window with updated title"); + mw->setWindowTitle(newName); + QCOMPARE(iface->text(QAccessible::Name), QLatin1String(newName)); + QAccessibleEvent event(mw, QAccessible::NameChanged); + QVERIFY(QTestAccessibility::containsEvent(&event)); } QTestAccessibility::clearEvents(); diff --git a/tests/auto/shared/resources/testfont_linemetrics.otf b/tests/auto/shared/resources/testfont_linemetrics.otf Binary files differnew file mode 100644 index 0000000000..11368f7d9e --- /dev/null +++ b/tests/auto/shared/resources/testfont_linemetrics.otf diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h index f81fe5548b..5a10bde814 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h +++ b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h @@ -518,7 +518,10 @@ protected: void cleanup() { QSqlQuery q(m_db); - q.exec("DROP PROCEDURE IF EXISTS " + m_procName); + if (m_db.driverName() == "QIBASE") + q.exec("DROP PROCEDURE " + m_procName); + else + q.exec("DROP PROCEDURE IF EXISTS " + m_procName); } private: QSqlDatabase m_db; diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp index 19afacf6f9..79fe9d6333 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp @@ -2344,20 +2344,20 @@ void tst_QSqlDatabase::moveToThread() auto clonedDb = QSqlDatabase::cloneDatabase(db, "clonedDb"); auto mainThread = QThread::currentThread(); CHECK_DATABASE(db); - QCOMPARE(db.currentThread(), mainThread); - QCOMPARE(clonedDb.currentThread(), mainThread); + QCOMPARE(db.thread(), mainThread); + QCOMPARE(clonedDb.thread(), mainThread); std::unique_ptr<QThread> t(QThread::create([&] { db.moveToThread(mainThread); QThread::currentThread()->exit(); })); db.moveToThread(t.get()); - QCOMPARE(db.currentThread(), t.get()); - QCOMPARE(clonedDb.currentThread(), mainThread); + QCOMPARE(db.thread(), t.get()); + QCOMPARE(clonedDb.thread(), mainThread); t->start(); QTRY_VERIFY(t->isRunning()); QTRY_VERIFY(t->wait(30000)); - QCOMPARE(db.currentThread(), mainThread); - QCOMPARE(clonedDb.currentThread(), mainThread); + QCOMPARE(db.thread(), mainThread); + QCOMPARE(clonedDb.thread(), mainThread); db = QSqlDatabase(); clonedDb = QSqlDatabase(); QSqlDatabase::removeDatabase("clonedDb"); diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp index 56fb5cd05f..89033c30e0 100644 --- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp @@ -256,6 +256,9 @@ private slots: void ibaseDateTimeWithTZ(); void ibaseTimeStampTzArray_data() { generic_data("QIBASE"); } void ibaseTimeStampTzArray(); + void ibaseInt128_data() { generic_data("QIBASE"); } + void ibaseInt128(); + void psqlJsonOperator_data() { generic_data("QPSQL"); } void psqlJsonOperator(); @@ -1875,52 +1878,54 @@ void tst_QSqlQuery::oci_rawField() // Test whether we can fetch values with more than DOUBLE precision // note that SQLite highest precision is that of a double, although // you can define field with higher precision: +// Test whether we can fetch values with more than DOUBLE precision +// note that SQLite highest precision is that of a double, although +// you can define field with higher precision: void tst_QSqlQuery::precision() { QFETCH(QString, dbName); QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db); - if (dbType == QSqlDriver::Interbase) - QSKIP("DB unable to store high precision"); const auto tidier = qScopeGuard([db, oldPrecision = db.driver()->numericalPrecisionPolicy()]() { db.driver()->setNumericalPrecisionPolicy(oldPrecision); }); + int digits = 21; + int decimals = 20; + std::array<QLatin1String, 2> precStrings = { "1.2345678901234567891"_L1, + "-1.2345678901234567891"_L1 }; + if (dbType == QSqlDriver::SQLite) { + // SQLite 3.45 does not return more, even when speicfied + digits = 17; + decimals = 16; + precStrings = { "1.2345678901234567"_L1, "-1.2345678901234567"_L1 }; + } else if (dbType == QSqlDriver::Sybase) + decimals = 18; db.driver()->setNumericalPrecisionPolicy(QSql::HighPrecision); TableScope ts(db, "qtest_precision", __FILE__); - static const QLatin1String precStr("1.2345678901234567891"); - - { - // need a new scope for SQLITE - QSqlQuery q(db); - QVERIFY_SQL(q, exec(QLatin1String(tst_Databases::isMSAccess(db) - ? "CREATE TABLE %1 (col1 number)" - : "CREATE TABLE %1 (col1 numeric(21, 20))") - .arg(ts.tableName()))); - - QVERIFY_SQL(q, exec(QLatin1String("INSERT INTO %1 (col1) VALUES (%2)") - .arg(ts.tableName(), precStr))); + QSqlQuery q(db); + QString stmt = "CREATE TABLE %1 (col1 numeric("_L1 + QString::number(digits) + ", "_L1 + + QString::number(decimals) + "))"_L1; + if (tst_Databases::isMSAccess(db)) + stmt = "CREATE TABLE %1 (col1 number)"_L1; + QVERIFY_SQL(q, exec(stmt.arg(ts.tableName()))); + for (const auto &precStr : precStrings) { + QVERIFY_SQL(q, exec("DELETE FROM %1"_L1.arg(ts.tableName()))); + QVERIFY_SQL(q, exec("INSERT INTO %1 (col1) VALUES (%2)"_L1.arg(ts.tableName(), precStr))); QVERIFY_SQL(q, exec("SELECT * FROM " + ts.tableName())); QVERIFY(q.next()); const QString val = q.value(0).toString(); if (!val.startsWith(precStr)) { int i = 0; - while (i < val.size() && precStr[i] != 0 && precStr[i] == val[i].toLatin1()) + while (i < val.size() && precStr[i] != 0 && precStr[i] == val[i]) ++i; - - // TDS has crappy precisions by default - if (dbType == QSqlDriver::Sybase) { - if (i < 18) - qWarning("TDS didn't return the right precision"); - } else { - qWarning() << tst_Databases::dbToString(db) << "didn't return the right precision (" - << i << "out of 21)," << val; - } + qWarning() << tst_Databases::dbToString(db) << "didn't return the right precision (" + << i << "out of " << digits << ")," << val; } - } // SQLITE scope + } } void tst_QSqlQuery::nullResult() @@ -5034,6 +5039,54 @@ void tst_QSqlQuery::ibaseTimeStampTzArray() #endif // QT_CONFIG(timezone) } +void tst_QSqlQuery::ibaseInt128() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + TableScope ts(db, "int128test", __FILE__); + db.setNumericalPrecisionPolicy(QSql::HighPrecision); + QSqlQuery q(db); + if (!q.exec("CREATE TABLE " + ts.tableName() + " (id INT PRIMARY KEY, price NUMERIC(20, 4))")) + QSKIP("Need at least Firebird 4 for this test - skipping"); + + QVERIFY_SQL(q, exec("INSERT INTO " + ts.tableName() + "(id,price) values(1,40001.1234)")); + QVERIFY_SQL(q, prepare("INSERT INTO " + ts.tableName() + "(id,price) values(2,:amount)")); + q.bindValue(":amount", 12345.67890); + QVERIFY_SQL(q, exec()); + { + QSqlQuery q2(db); + q2.setNumericalPrecisionPolicy(QSql::LowPrecisionDouble); + QVERIFY_SQL(q2, exec("SELECT price FROM " + ts.tableName() + " ORDER BY id")); + QVERIFY_SQL(q2, next()); + QCOMPARE(q2.value(0).metaType().id(), QMetaType::Double); + QCOMPARE(q2.value(0).toDouble(), 40001.1234); + QVERIFY_SQL(q2, next()); + QCOMPARE(q2.value("price").metaType().id(), QMetaType::Double); + QCOMPARE(q2.value("price").toDouble(), 12345.6789); + QVERIFY_SQL(q2, exec("SELECT sum(price) FROM " + ts.tableName())); + QVERIFY_SQL(q2, next()); + QCOMPARE(q2.value(0).metaType().id(), QMetaType::Double); + QCOMPARE(q2.value(0).toDouble(), 52346.8023); + } + { + QSqlQuery q2(db); + q2.setNumericalPrecisionPolicy(QSql::HighPrecision); + QVERIFY_SQL(q2, exec("SELECT price FROM " + ts.tableName() + " ORDER BY id")); + QVERIFY_SQL(q2, next()); + QCOMPARE(q2.value(0).metaType().id(), QMetaType::QString); + QCOMPARE(q2.value(0).toString(), "40001.1234"); + QVERIFY_SQL(q2, next()); + QCOMPARE(q2.value("price").metaType().id(), QMetaType::QString); + QCOMPARE(q2.value("price").toString(), "12345.6789"); + QVERIFY_SQL(q2, exec("SELECT sum(price) FROM " + ts.tableName())); + QVERIFY_SQL(q2, next()); + QCOMPARE(q2.value(0).metaType().id(), QMetaType::QString); + QCOMPARE(q2.value(0).toString(), "52346.8023"); + } +} + void tst_QSqlQuery::ibase_executeBlock() { QFETCH(QString, dbName); diff --git a/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp b/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp index 6aeae86d7d..8e7d5b502e 100644 --- a/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp +++ b/tests/auto/sql/kernel/qsqlrecord/tst_qsqlrecord.cpp @@ -10,7 +10,7 @@ #include <qsqlrecord.h> -#define NUM_FIELDS 4 +#define NUM_FIELDS 5 class tst_QSqlRecord : public QObject { @@ -67,6 +67,7 @@ void tst_QSqlRecord::createTestRecord() fields[1] = std::make_unique<QSqlField>(QStringLiteral("int"), QMetaType(QMetaType::Int), QStringLiteral("inttable")); fields[2] = std::make_unique<QSqlField>(QStringLiteral("double"), QMetaType(QMetaType::Double), QStringLiteral("doubletable")); fields[3] = std::make_unique<QSqlField>(QStringLiteral("bool"), QMetaType(QMetaType::Bool)); + fields[4] = std::make_unique<QSqlField>(QStringLiteral("öäü@€"), QMetaType(QMetaType::Int)); for (const auto &field : fields) rec->append(*field); } @@ -173,9 +174,22 @@ void tst_QSqlRecord::clearValues() void tst_QSqlRecord::contains() { createTestRecord(); - for (const auto &field : fields) - QVERIFY(rec->contains(field->name())); - QVERIFY( !rec->contains( "__Harry__" ) ); + QStringList fieldNames; + for (const auto &field : fields) { + fieldNames.append(field->name()); + if (!field->tableName().isEmpty()) + fieldNames.append(field->tableName() + u'.' + field->name()); + } + for (const auto &name : std::as_const(fieldNames)) { + QVERIFY(rec->contains(name)); + const QByteArray nameBa = name.toUtf8(); + QVERIFY(rec->contains(nameBa)); + const char *nameStr = nameBa.constData(); + QVERIFY(rec->contains(nameStr)); + QVERIFY(!rec->contains(name.left(name.size() - 1))); + QVERIFY(!rec->contains(name + u'.' + name)); + } + QVERIFY(!rec->contains("__Harry__")); } void tst_QSqlRecord::count() diff --git a/tests/auto/testlib/selftests/eventloop/CMakeLists.txt b/tests/auto/testlib/selftests/eventloop/CMakeLists.txt index 46b794f895..afaf766f69 100644 --- a/tests/auto/testlib/selftests/eventloop/CMakeLists.txt +++ b/tests/auto/testlib/selftests/eventloop/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## eventloop Binary: ##################################################################### diff --git a/tests/auto/testlib/selftests/failcleanup/CMakeLists.txt b/tests/auto/testlib/selftests/failcleanup/CMakeLists.txt index 0b582ffdc3..10805ba6f7 100644 --- a/tests/auto/testlib/selftests/failcleanup/CMakeLists.txt +++ b/tests/auto/testlib/selftests/failcleanup/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2024 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause ##################################################################### diff --git a/tests/auto/testlib/selftests/failcleanuptestcase/CMakeLists.txt b/tests/auto/testlib/selftests/failcleanuptestcase/CMakeLists.txt index 1a1417f76c..cb11a577f6 100644 --- a/tests/auto/testlib/selftests/failcleanuptestcase/CMakeLists.txt +++ b/tests/auto/testlib/selftests/failcleanuptestcase/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## failcleanuptestcase Binary: ##################################################################### diff --git a/tests/auto/testlib/selftests/skipblacklisted/CMakeLists.txt b/tests/auto/testlib/selftests/skipblacklisted/CMakeLists.txt index cc1a2ff469..0cdcf2c063 100644 --- a/tests/auto/testlib/selftests/skipblacklisted/CMakeLists.txt +++ b/tests/auto/testlib/selftests/skipblacklisted/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## skipblacklisted Binary: ##################################################################### diff --git a/tests/auto/testlib/selftests/skipcleanuptestcase/CMakeLists.txt b/tests/auto/testlib/selftests/skipcleanuptestcase/CMakeLists.txt index 88aae42acd..5c531b46b5 100644 --- a/tests/auto/testlib/selftests/skipcleanuptestcase/CMakeLists.txt +++ b/tests/auto/testlib/selftests/skipcleanuptestcase/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## skipcleanuptestcase Binary: ##################################################################### diff --git a/tests/auto/tools/moc/enum_inc.h b/tests/auto/tools/moc/enum_inc.h index 7d7b6ae9f3..2b355b7a19 100644 --- a/tests/auto/tools/moc/enum_inc.h +++ b/tests/auto/tools/moc/enum_inc.h @@ -1,2 +1,4 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only parcel = 42, part = 12, diff --git a/tests/auto/tools/moc/forwarddeclaredparam.h b/tests/auto/tools/moc/forwarddeclaredparam.h index 176f0c3356..94caae8a5b 100644 --- a/tests/auto/tools/moc/forwarddeclaredparam.h +++ b/tests/auto/tools/moc/forwarddeclaredparam.h @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef FORWARDDECLAREDPARAM_H #define FORWARDDECLAREDPARAM_H struct ForwardDeclaredParam {}; diff --git a/tests/auto/tools/moc/os9-newlines.h b/tests/auto/tools/moc/os9-newlines.h index 82bddd117d..2da70f6a89 100644 --- a/tests/auto/tools/moc/os9-newlines.h +++ b/tests/auto/tools/moc/os9-newlines.h @@ -1 +1 @@ -// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QObject>
class Os9Newlines : public QObject
{
Q_OBJECT
public Q_SLOTS:
inline void testSlot() {}
};
\ No newline at end of file +// REUSE-IgnoreStart
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// REUSE-IgnoreEnd
#include <QObject>
class Os9Newlines : public QObject
{
Q_OBJECT
public Q_SLOTS:
inline void testSlot() {}
};
\ No newline at end of file diff --git a/tests/auto/tools/moc/subdir/extradefines.h b/tests/auto/tools/moc/subdir/extradefines.h index e7888ce80d..efad23be6f 100644 --- a/tests/auto/tools/moc/subdir/extradefines.h +++ b/tests/auto/tools/moc/subdir/extradefines.h @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #define FOO 1 diff --git a/tests/auto/tools/qmake/testdata/conflicting_targets/main.cpp b/tests/auto/tools/qmake/testdata/conflicting_targets/main.cpp index 39de28135a..866a063753 100644 --- a/tests/auto/tools/qmake/testdata/conflicting_targets/main.cpp +++ b/tests/auto/tools/qmake/testdata/conflicting_targets/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only int main(int, char **) { return 0; diff --git a/tests/auto/tools/qmake/testdata/findDeps/needed.cpp b/tests/auto/tools/qmake/testdata/findDeps/needed.cpp index 698d0aaa12..726382cf10 100644 --- a/tests/auto/tools/qmake/testdata/findDeps/needed.cpp +++ b/tests/auto/tools/qmake/testdata/findDeps/needed.cpp @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only extern int needed(void) { return 1; } diff --git a/tests/auto/tools/qmake/testdata/findMocs/digitseparated.h b/tests/auto/tools/qmake/testdata/findMocs/digitseparated.h index ddedac26e3..10cf31a8a5 100644 --- a/tests/auto/tools/qmake/testdata/findMocs/digitseparated.h +++ b/tests/auto/tools/qmake/testdata/findMocs/digitseparated.h @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QObject> class AfterDigitSeparator : public QObject diff --git a/tests/auto/tools/qmake/testdata/include_pwd/anotherheader.h b/tests/auto/tools/qmake/testdata/include_pwd/anotherheader.h index a3a63e26bf..228a8861b7 100644 --- a/tests/auto/tools/qmake/testdata/include_pwd/anotherheader.h +++ b/tests/auto/tools/qmake/testdata/include_pwd/anotherheader.h @@ -1,2 +1,4 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only /* This file should be included indirectly through main.cpp */ diff --git a/tests/auto/tools/qmake/testdata/include_pwd/main.cpp b/tests/auto/tools/qmake/testdata/include_pwd/main.cpp index c98935f87d..b55f0525ea 100644 --- a/tests/auto/tools/qmake/testdata/include_pwd/main.cpp +++ b/tests/auto/tools/qmake/testdata/include_pwd/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "someheader.h" int main() diff --git a/tests/auto/tools/qmake/testdata/include_pwd/somedirectory/someheader.h b/tests/auto/tools/qmake/testdata/include_pwd/somedirectory/someheader.h index f977346945..9a26fb7c1e 100644 --- a/tests/auto/tools/qmake/testdata/include_pwd/somedirectory/someheader.h +++ b/tests/auto/tools/qmake/testdata/include_pwd/somedirectory/someheader.h @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "anotherheader.h" diff --git a/tests/auto/tools/qmake/testdata/quotedfilenames/include folder/header.h b/tests/auto/tools/qmake/testdata/quotedfilenames/include folder/header.h index 595c0cac3c..b65da1d85c 100644 --- a/tests/auto/tools/qmake/testdata/quotedfilenames/include folder/header.h +++ b/tests/auto/tools/qmake/testdata/quotedfilenames/include folder/header.h @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only /* a random header file */ diff --git a/tests/auto/tools/qmake/testdata/windows_resources/main.cpp b/tests/auto/tools/qmake/testdata/windows_resources/main.cpp index 237c8ce181..6c1d24751e 100644 --- a/tests/auto/tools/qmake/testdata/windows_resources/main.cpp +++ b/tests/auto/tools/qmake/testdata/windows_resources/main.cpp @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only int main() {} diff --git a/tests/auto/tools/qt_cmake_create/testdata/cpp_project/main.cpp b/tests/auto/tools/qt_cmake_create/testdata/cpp_project/main.cpp index ae659d5ed4..3526286e5e 100644 --- a/tests/auto/tools/qt_cmake_create/testdata/cpp_project/main.cpp +++ b/tests/auto/tools/qt_cmake_create/testdata/cpp_project/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <iostream> int main(int, char *[]) diff --git a/tests/auto/tools/qt_cmake_create/testdata/qml_project/main.cpp b/tests/auto/tools/qt_cmake_create/testdata/qml_project/main.cpp index 13c7014d8c..844e67e2c4 100644 --- a/tests/auto/tools/qt_cmake_create/testdata/qml_project/main.cpp +++ b/tests/auto/tools/qt_cmake_create/testdata/qml_project/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QGuiApplication> #include <QQmlApplicationEngine> diff --git a/tests/auto/tools/qt_cmake_create/testdata/qrc_project/main.cpp b/tests/auto/tools/qt_cmake_create/testdata/qrc_project/main.cpp index cd8ed8f57b..72f63e3077 100644 --- a/tests/auto/tools/qt_cmake_create/testdata/qrc_project/main.cpp +++ b/tests/auto/tools/qt_cmake_create/testdata/qrc_project/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QFile> #include <QDebug> diff --git a/tests/auto/tools/qt_cmake_create/testdata/ui_project/main.cpp b/tests/auto/tools/qt_cmake_create/testdata/ui_project/main.cpp index b0a4ec2647..2e74e1c140 100644 --- a/tests/auto/tools/qt_cmake_create/testdata/ui_project/main.cpp +++ b/tests/auto/tools/qt_cmake_create/testdata/ui_project/main.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "widget.h" #include <QApplication> diff --git a/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.cpp b/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.cpp index 815d5f8c4b..65c5eae0c9 100644 --- a/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.cpp +++ b/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.cpp @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "widget.h" #include "ui_widget.h" @@ -12,4 +14,3 @@ Widget::~Widget() { delete ui; } - diff --git a/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.h b/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.h index 1fe2322b13..9110f2a20b 100644 --- a/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.h +++ b/tests/auto/tools/qt_cmake_create/testdata/ui_project/widget.h @@ -1,3 +1,5 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WIDGET_H #define WIDGET_H diff --git a/tests/auto/tools/rcc/data/.gitattributes b/tests/auto/tools/rcc/data/.gitattributes new file mode 100644 index 0000000000..89d6e8fe1c --- /dev/null +++ b/tests/auto/tools/rcc/data/.gitattributes @@ -0,0 +1,2 @@ +# Keep parentdir.txt with LF line endings +parentdir.txt -text diff --git a/tests/auto/tools/rcc/data/legal/legal.qrc b/tests/auto/tools/rcc/data/legal/legal.qrc new file mode 100644 index 0000000000..5eb7f58416 --- /dev/null +++ b/tests/auto/tools/rcc/data/legal/legal.qrc @@ -0,0 +1,10 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <legal> + Copyright (C) 2024 Intel Corporation. + SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + </legal> + <qresource> + <file>../parentdir.txt</file> + </qresource> +</RCC> diff --git a/tests/auto/tools/rcc/data/legal/rcc_legal.cpp b/tests/auto/tools/rcc/data/legal/rcc_legal.cpp new file mode 100644 index 0000000000..deec3380aa --- /dev/null +++ b/tests/auto/tools/rcc/data/legal/rcc_legal.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** Resource object code +** Copyright (C) 2024 Intel Corporation. +** SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +** +** Created by: The Resource Compiler for Qt version 6.9.0 +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#ifdef _MSC_VER +// disable informational message "function ... selected for automatic inline expansion" +#pragma warning (disable: 4711) +#endif + +static const unsigned char qt_resource_data[] = { + // parentdir.txt + 0x0,0x0,0x0,0x1a, + 0x61, + 0x62,0x63,0x64,0x65,0x66,0x67,0x69,0x68,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72, + 0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0xa, + +}; + +static const unsigned char qt_resource_name[] = { + // parentdir.txt + 0x0,0xd, + 0x6,0x14,0xd1,0x74, + 0x0,0x70, + 0x0,0x61,0x0,0x72,0x0,0x65,0x0,0x6e,0x0,0x74,0x0,0x64,0x0,0x69,0x0,0x72,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74, + +}; + +static const unsigned char qt_resource_struct[] = { + // : + 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + // :/parentdir.txt + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0, +TIMESTAMP:../parentdir.txt + +}; + +#ifdef QT_NAMESPACE +# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name +# define QT_RCC_MANGLE_NAMESPACE0(x) x +# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b +# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b) +# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \ + QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE)) +#else +# define QT_RCC_PREPEND_NAMESPACE(name) name +# define QT_RCC_MANGLE_NAMESPACE(name) name +#endif + +#if defined(QT_INLINE_NAMESPACE) +inline namespace QT_NAMESPACE { +#elif defined(QT_NAMESPACE) +namespace QT_NAMESPACE { +#endif + +bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); +bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); + +#ifdef QT_NAMESPACE +} +#endif + +int QT_RCC_MANGLE_NAMESPACE(qInitResources)(); +int QT_RCC_MANGLE_NAMESPACE(qInitResources)() +{ + int version = 3; + QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData) + (version, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); +int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)() +{ + int version = 3; + QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData) + (version, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace { + struct initializer { + initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources)(); } + ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); } + } dummy; +} + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/tests/auto/tools/rcc/tst_rcc.cpp b/tests/auto/tools/rcc/tst_rcc.cpp index 88a6e17e2f..af4a992d5c 100644 --- a/tests/auto/tools/rcc/tst_rcc.cpp +++ b/tests/auto/tools/rcc/tst_rcc.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com> // Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2024 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -148,6 +149,9 @@ void tst_rcc::rcc_data() QTest::newRow("size-1") << sizesPath << "size-1.qrc" << "size-1.expected"; QTest::newRow("size-2-0-35-1") << sizesPath << "size-2-0-35-1.qrc" << (sizeof(size_t) == 8 ? "size-2-0-35-1.expected" : "size-2-0-35-1.expected32"); + + QTest::newRow("legal") << m_dataPath + QLatin1StringView("/legal") + << "legal.qrc" << "rcc_legal.cpp"; } static QStringList readLinesFromFile(const QString &fileName, diff --git a/tests/auto/tools/uic/baseline/Widget.ui b/tests/auto/tools/uic/baseline/Widget.ui index ab94733dcc..285d95eb88 100644 --- a/tests/auto/tools/uic/baseline/Widget.ui +++ b/tests/auto/tools/uic/baseline/Widget.ui @@ -1,4 +1,6 @@ <ui version="4.0" > + <legal>Copyright (C) 2016 The Qt Company Ltd. +SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only</legal> <class>Form</class> <widget class="QWidget" name="Form" > <property name="geometry" > diff --git a/tests/auto/tools/uic/baseline/Widget.ui.h b/tests/auto/tools/uic/baseline/Widget.ui.h index 4d4846e6c5..a7fa74c447 100644 --- a/tests/auto/tools/uic/baseline/Widget.ui.h +++ b/tests/auto/tools/uic/baseline/Widget.ui.h @@ -1,3 +1,8 @@ +/* +Copyright (C) 2016 The Qt Company Ltd. +SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +*/ + /******************************************************************************** ** Form generated from reading UI file 'Widget.ui' ** diff --git a/tests/auto/wasm/fetchapi/tst_fetchapi.cpp b/tests/auto/wasm/fetchapi/tst_fetchapi.cpp index 3dcd8dd916..e37316b2db 100644 --- a/tests/auto/wasm/fetchapi/tst_fetchapi.cpp +++ b/tests/auto/wasm/fetchapi/tst_fetchapi.cpp @@ -69,11 +69,9 @@ void tst_FetchApi::sendRequestOnMainThread() void tst_FetchApi::sendRequestOnBackgroundThread() { - QSKIP("Skip this test until we fix fetching from background threads."); QEventLoop mainEventLoop; BackgroundThread *backgroundThread = new BackgroundThread(); connect(backgroundThread, &BackgroundThread::finished, &mainEventLoop, &QEventLoop::quit); - connect(backgroundThread, &BackgroundThread::finished, backgroundThread, &QObject::deleteLater); backgroundThread->start(); mainEventLoop.exec(); diff --git a/tests/auto/wasm/selenium/CMakeLists.txt b/tests/auto/wasm/selenium/CMakeLists.txt index 335b6d23d9..445030878e 100644 --- a/tests/auto/wasm/selenium/CMakeLists.txt +++ b/tests/auto/wasm/selenium/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) cmake_minimum_required(VERSION 3.16) project(tst_qwasmwindow_harness LANGUAGES CXX) diff --git a/tests/auto/wasm/selenium/run.bat b/tests/auto/wasm/selenium/run.bat index 031e0b13ab..56305c72c3 100644 --- a/tests/auto/wasm/selenium/run.bat +++ b/tests/auto/wasm/selenium/run.bat @@ -1,3 +1,5 @@ +:: Copyright (C) 2024 The Qt Company Ltd. +:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 :: :: The highest version of python that can be used is 3.11 :: Download from here: https://www.python.org/downloads/release/python-3118/ diff --git a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp index 94afff6e40..26a6245164 100644 --- a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp +++ b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp @@ -63,6 +63,7 @@ private slots: void hideNativeByDestruction(); void explicitDoneAfterButtonClicked(); + void legacyApiReturnValue(); void cleanup(); }; @@ -511,7 +512,7 @@ QT_WARNING_DISABLE_DEPRECATED // the button text versions closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 1); - COMPARE(ret, 3); // Custom button opaque result + COMPARE(ret, 1); QVERIFY(closeHelper.done()); #endif // QT_DEPRECATED_SINCE(6, 2) #undef COMPARE @@ -863,5 +864,21 @@ void tst_QMessageBox::explicitDoneAfterButtonClicked() QCOMPARE(rejectedSpy.size(), 3); } +void tst_QMessageBox::legacyApiReturnValue() +{ + ExecCloseHelper closeHelper; + for (int i = 0; i < 3; ++i) { + closeHelper.start(Qt::Key_Enter); +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + QCOMPARE(QMessageBox::warning(nullptr, "Title", "Text", + "Button 0", "Button 1", "Button 2", i), i); + closeHelper.start(Qt::Key_Escape); + QCOMPARE(QMessageBox::warning(nullptr, "Title", "Text", + "Button 0", "Button 1", "Button 2", 0, i), i); +QT_WARNING_POP + } +} + QTEST_MAIN(tst_QMessageBox) #include "tst_qmessagebox.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp index c91a0803ee..fad75ec045 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp @@ -2976,11 +2976,6 @@ static QSizeF wfh(Qt::SizeHint /*which*/, const QSizeF &constraint) return result; } -bool qFuzzyCompare(const QSizeF &a, const QSizeF &b) -{ - return qFuzzyCompare(a.width(), b.width()) && qFuzzyCompare(a.height(), b.height()); -} - void tst_QGraphicsGridLayout::heightForWidth() { QGraphicsWidget *widget = new QGraphicsWidget; diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index 41f3da1553..ee1a8c530a 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -2062,7 +2062,6 @@ void tst_QGraphicsProxyWidget::setFocus_simpleWidget() window.setLayout(layout); window.show(); - QApplicationPrivate::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2456,7 +2455,6 @@ void tst_QGraphicsProxyWidget::tooltip_basic() QGraphicsView view(&scene); view.setFixedSize(200, 200); view.show(); - QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); { QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index 2dec3f30e9..2eb2d84504 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -23,6 +23,7 @@ #include <QtGui/QFontDatabase> #include <QtGui/QClipboard> +#include <QtGui/QStyleHints> #include <QtWidgets/QApplication> #include <QtWidgets/QMessageBox> @@ -119,6 +120,7 @@ private slots: void style(); void applicationPalettePolish(); + void setColorScheme(); void allWidgets(); void topLevelWidgets(); @@ -1622,7 +1624,6 @@ void tst_QApplication::focusWidget() QTextEdit te; te.show(); - QApplicationPrivate::setActiveWindow(&te); QVERIFY(QTest::qWaitForWindowActive(&te)); const auto focusWidget = QApplication::focusWidget(); @@ -1638,7 +1639,6 @@ void tst_QApplication::focusWidget() QTextEdit te(&w); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); const auto focusWidget = QApplication::focusWidget(); @@ -2053,6 +2053,122 @@ void tst_QApplication::applicationPalettePolish() } } +void tst_QApplication::setColorScheme() +{ + int argc = 1; + QApplication app(argc, &argv0); + + if (QStringList{"minimal", "offscreen", "wayland", "xcb", "wasm", "webassembly"} + .contains(QGuiApplication::platformName(), Qt::CaseInsensitive)) { + QSKIP("Setting the colorScheme is not implemented on this platform."); + } + qDebug() << "Testing setColorScheme on platform" << QGuiApplication::platformName(); + + if (QByteArrayView(app.style()->metaObject()->className()) == "QWindowsVistaStyle") + QSKIP("Setting the colorScheme is not supported with the Windows Vista style."); + + const Qt::ColorScheme defaultColorScheme = QApplication::styleHints()->colorScheme(); + // if we implement setColorScheme, then we must be able to read it + QVERIFY(defaultColorScheme != Qt::ColorScheme::Unknown); + const Qt::ColorScheme newColorScheme = defaultColorScheme == Qt::ColorScheme::Light + ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; + + class TopLevelWidget : public QWidget + { + QList<QEvent::Type> events; + public: + TopLevelWidget() + { + setObjectName("colorScheme TopLevelWidget"); + } + + void clearEvents() + { + events.clear(); + } + qsizetype eventCount(QEvent::Type type) const + { + return events.count(type); + } + protected: + bool event(QEvent *event) override + { + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + case QEvent::PaletteChange: + case QEvent::ThemeChange: + events << event->type(); + break; + default: + break; + } + + return QWidget::event(event); + } + } topLevelWidget; + topLevelWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevelWidget)); + + QSignalSpy colorSchemeChangedSpy(app.styleHints(), &QStyleHints::colorSchemeChanged); + + // always start with a clean list + topLevelWidget.clearEvents(); + const QPalette defaultPalette = topLevelWidget.palette(); + + bool oldPaletteWhenSchemeChanged = false; + connect(app.styleHints(), &QStyleHints::colorSchemeChanged, this, + [defaultPalette, &topLevelWidget, &oldPaletteWhenSchemeChanged]{ + oldPaletteWhenSchemeChanged = defaultPalette == topLevelWidget.palette(); + }); + + app.styleHints()->setColorScheme(newColorScheme); + QTRY_COMPARE(colorSchemeChangedSpy.count(), 1); + // We have not yet updated the palette when we emit the colorSchemeChanged + // signal, so the toplevel widget should still use the previous palette + QVERIFY(oldPaletteWhenSchemeChanged); + QCOMPARE(topLevelWidget.eventCount(QEvent::ThemeChange), 1); + // We can't guarantee that there is only one ApplicationPaletteChange, + // and they might arrive asynchronously in response to ThemeChange + QTRY_COMPARE_GE(topLevelWidget.eventCount(QEvent::ApplicationPaletteChange), 1); + // But we can guarantee a single PaletteChange event for the widget + QCOMPARE(topLevelWidget.eventCount(QEvent::PaletteChange), 1); + // The palette should have changed + QCOMPARE_NE(topLevelWidget.palette(), defaultPalette); + + topLevelWidget.clearEvents(); + colorSchemeChangedSpy.clear(); + + // verify that a widget shown with a color scheme override in place respect that + QWidget newWidget; + newWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&newWidget)); + QCOMPARE(newWidget.palette(), topLevelWidget.palette()); + + // Setting to Unknown should follow the system preference again + app.styleHints()->setColorScheme(Qt::ColorScheme::Unknown); + QTRY_COMPARE(colorSchemeChangedSpy.count(), 1); + QCOMPARE(app.styleHints()->colorScheme(), defaultColorScheme); + QTRY_COMPARE(topLevelWidget.eventCount(QEvent::PaletteChange), 1); + + auto debugPalette = qScopeGuard([defaultPalette, &topLevelWidget]{ + qDebug() << "Inspecting palettes for differences"; + const QPalette palette = topLevelWidget.palette(); + for (int g = 0; g < QPalette::NColorGroups; ++g) { + for (int r = 0; r < QPalette::NColorRoles; ++r) { + const auto group = static_cast<QPalette::ColorGroup>(g); + const auto role = static_cast<QPalette::ColorRole>(r); + qDebug() << "...Checking" << group << role; + const auto actualBrush = palette.brush(group, role); + const auto expectedBrush = defaultPalette.brush(group, role); + if (palette.brush(group, role) != defaultPalette.brush(group, role)) + qWarning() << "...Difference in" << group << role << actualBrush << expectedBrush; + } + } + }); + QCOMPARE(topLevelWidget.palette(), defaultPalette); + debugPalette.dismiss(); +} + void tst_QApplication::allWidgets() { int argc = 1; diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 94bf9fff5c..666cb70da9 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -319,6 +319,7 @@ private slots: void renderTargetOffset(); void renderInvisible(); void renderWithPainter(); + void renderRTL(); void render_task188133(); void render_task211796(); void render_task217815(); @@ -476,6 +477,10 @@ private slots: void reparentWindowHandles_data(); void reparentWindowHandles(); +#ifndef QT_NO_CONTEXTMENU + void contextMenuTrigger(); +#endif + private: const QString m_platform; QSize m_testWidgetSize; @@ -1897,6 +1902,7 @@ void tst_QWidget::focusChainOnReparent() } QWidget window2; + child22->setParent(child21); child2->setParent(&window2); QWidget *expectedNewChain[5] = {&window2, child2, child21, child22, &window2}; @@ -8474,6 +8480,47 @@ void tst_QWidget::renderWithPainter() QCOMPARE(painter.renderHints(), oldRenderHints); } +void tst_QWidget::renderRTL() +{ + QFont f; + f.setStyleStrategy(QFont::NoAntialias); + const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows"))); + + QMenu menu; + menu.setMinimumWidth(200); + menu.setFont(f); + menu.setStyle(style.data()); + menu.addAction("I"); + menu.show(); + menu.setLayoutDirection(Qt::LeftToRight); + QVERIFY(QTest::qWaitForWindowExposed(&menu)); + + QImage imageLTR(menu.size(), QImage::Format_ARGB32); + menu.render(&imageLTR); + //imageLTR.save("/tmp/rendered_1.png"); + + menu.setLayoutDirection(Qt::RightToLeft); + QImage imageRTL(menu.size(), QImage::Format_ARGB32); + menu.render(&imageRTL); + imageRTL = imageRTL.mirrored(true, false); + //imageRTL.save("/tmp/rendered_2.png"); + + QCOMPARE(imageLTR.height(), imageRTL.height()); + QCOMPARE(imageLTR.width(), imageRTL.width()); + static constexpr auto border = 4; + for (int h = border; h < imageRTL.height() - border; ++h) { + // there should be no difference on the right (aka no text) + for (int w = imageRTL.width() / 2; w < imageRTL.width() - border; ++w) { + auto pixLTR = imageLTR.pixel(w, h); + auto pixRTL = imageRTL.pixel(w, h); + if (pixLTR != pixRTL) + qDebug() << "Pixel do not match at" << w << h << ":" + << Qt::hex << pixLTR << "<->" << pixRTL; + QCOMPARE(pixLTR, pixRTL); + } + } +} + void tst_QWidget::render_task188133() { QMainWindow mainWindow; @@ -11107,6 +11154,10 @@ void tst_QWidget::hoverPosition() QVERIFY(QTest::qWaitForWindowExposed(&root)); const QPoint middle(50, 50); + // wait until we got the correct global pos + QPoint expectedGlobalPos = root.geometry().topLeft() + QPoint(100, 100) + middle; + if (!QTest::qWaitFor([&]{ return expectedGlobalPos == h.mapToGlobal(middle); })) + QSKIP("Can't move cursor"); QPoint curpos = h.mapToGlobal(middle); QCursor::setPos(curpos); if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; })) @@ -13998,5 +14049,41 @@ void tst_QWidget::reparentWindowHandles() } } +#ifndef QT_NO_CONTEXTMENU +void tst_QWidget::contextMenuTrigger() +{ + class ContextMenuWidget : public QWidget + { + public: + int events = 0; + + protected: + void contextMenuEvent(QContextMenuEvent *) override { ++events; } + }; + + const Qt::ContextMenuTrigger wasTrigger = QGuiApplication::styleHints()->contextMenuTrigger(); + auto restoreTriggerGuard = qScopeGuard([wasTrigger]{ + QGuiApplication::styleHints()->setContextMenuTrigger(wasTrigger); + }); + + ContextMenuWidget widget; + widget.show(); + QVERIFY(!qApp->topLevelWindows().empty()); + auto *window = qApp->topLevelWindows()[0]; + QVERIFY(window); + QCOMPARE(widget.events, 0); + QGuiApplication::styleHints()->setContextMenuTrigger(Qt::ContextMenuTrigger::Press); + QTest::mousePress(window, Qt::RightButton); + QCOMPARE(widget.events, 1); + QTest::mouseRelease(window, Qt::RightButton); + QCOMPARE(widget.events, 1); + QGuiApplication::styleHints()->setContextMenuTrigger(Qt::ContextMenuTrigger::Release); + QTest::mousePress(window, Qt::RightButton); + QCOMPARE(widget.events, 1); + QTest::mouseRelease(window, Qt::RightButton); + QCOMPARE(widget.events, 2); +} +#endif + QTEST_MAIN(tst_QWidget) #include "tst_qwidget.moc" diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index e771737ae0..1fab69fdcc 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -1634,8 +1634,7 @@ void tst_QWidget_window::mouseMoveWithPopup() // but the release event will still be delivered to the first popup - dialogs might not get it QCOMPARE(mouseAction(Qt::LeftButton), QEvent::MouseButtonRelease); - if (topLevel.popup->mouseReleaseCount != 1 - && !QGuiApplication::platformName().startsWith(QLatin1String("windows"), Qt::CaseInsensitive)) + if (topLevel.popup->mouseReleaseCount != 1) QEXPECT_FAIL("Dialog", "Platform specific behavior", Continue); QCOMPARE(topLevel.popup->mouseReleaseCount, 1); } diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp index 9059a9262e..64ebeb08b0 100644 --- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp @@ -260,6 +260,7 @@ private slots: void staticContents(); void scroll(); void paintOnScreenUpdates(); + void evaluateRhi(); #if defined(QT_BUILD_INTERNAL) void scrollWithOverlap(); @@ -581,6 +582,222 @@ void tst_QWidgetRepaintManager::paintOnScreenUpdates() } } +class RhiWidgetPrivate : public QWidgetPrivate +{ +public: + RhiWidgetPrivate(const QPlatformBackingStoreRhiConfig &config) + : config(config) + { + } + + QPlatformBackingStoreRhiConfig rhiConfig() const override + { + return config; + } + + QPlatformBackingStoreRhiConfig config = QPlatformBackingStoreRhiConfig::Null; +}; + +class RhiWidget : public QWidget +{ +public: + RhiWidget(const QPlatformBackingStoreRhiConfig &config = QPlatformBackingStoreRhiConfig::Null, QWidget *parent = nullptr) + : QWidget(*new RhiWidgetPrivate(config), parent, {}) + { + } +}; + +void tst_QWidgetRepaintManager::evaluateRhi() +{ + const auto *integration = QGuiApplicationPrivate::platformIntegration(); + if (!integration->hasCapability(QPlatformIntegration::RhiBasedRendering)) + QSKIP("Platform does not support RHI based rendering"); + + // We need full control over whether widgets are native or not + const bool nativeSiblingsOriginal = qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); + auto nativeSiblingGuard = qScopeGuard([&]{ + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, nativeSiblingsOriginal); + }); + + auto defaultSurfaceType = QSurface::RasterSurface; + bool usesRhiBackingStore = false; + + { + // Plain QWidget doesn't enable RHI + QWidget regularWidget; + regularWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(®ularWidget)); + QVERIFY(!QWidgetPrivate::get(®ularWidget)->usesRhiFlush); + + // The platform might use a non-raster surface type if it uses + // an RHI backingstore by default (e.g. Android, iOS, QNX). + defaultSurfaceType = regularWidget.windowHandle()->surfaceType(); + + // Record whether the platform uses an RHI backingstore, + // so we can opt out of some tests further down. + if (defaultSurfaceType != QSurface::RasterSurface) + usesRhiBackingStore = QWidgetPrivate::get(®ularWidget)->rhi(); + else + QVERIFY(!QWidgetPrivate::get(®ularWidget)->rhi()); + } + + { + // But a top level RHI widget does + RhiWidget rhiWidget; + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + } + +#if QT_CONFIG(opengl) + { + // Non-native child RHI widget enables RHI for top level regular widget + QWidget topLevel; + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + // Only the native widget that actually flushes will report usesRhiFlush + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + // But it should have an RHI it can use + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + } + + { + // Native child RHI widget does not enable RHI for top level + QWidget topLevel; + RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + nativeRhiWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Non-native RHI child of native child enables RHI for native child, + // but not top level. + QWidget topLevel; + QWidget nativeChild(&topLevel); + nativeChild.setAttribute(Qt::WA_NativeWindow); + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Native child RHI widget does not prevent RHI for top level + // if non-native RHI child widget is also present. + QWidget topLevel; + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + nativeRhiWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Reparenting into a window that already matches the required + // surface type should still mark the parent as flushing with RHI. + QWidget topLevel; + + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::Null); + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + rhiWidget.setParent(&topLevel); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Non-native RHI child of native child enables RHI for native child, + // but does not prevent top level from flushing with RHI. + QWidget topLevel; + QWidget nativeChild(&topLevel); + nativeChild.setAttribute(Qt::WA_NativeWindow); + RhiWidget rhiGranchild(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild); + RhiWidget rhiChild(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiGranchild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiGranchild)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiChild)->rhi()); + } + +#if QT_CONFIG(metal) + QRhiMetalInitParams metalParams; + if (QRhi::probe(QRhi::Metal, &metalParams)) { + // Native RHI childen allows mixing RHI backends + QWidget topLevel; + RhiWidget openglWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + openglWidget.setAttribute(Qt::WA_NativeWindow); + RhiWidget metalWidget(QPlatformBackingStoreRhiConfig::Metal, &topLevel); + metalWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + + QCOMPARE(openglWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&openglWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&openglWidget)->rhi()); + + QCOMPARE(metalWidget.windowHandle()->surfaceType(), QSurface::MetalSurface); + QVERIFY(QWidgetPrivate::get(&metalWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&metalWidget)->rhi()); + + QVERIFY(QWidgetPrivate::get(&openglWidget)->rhi() != QWidgetPrivate::get(&metalWidget)->rhi()); + } +#endif // QT_CONFIG(metal) + +#endif // QT_CONFIG(opengl) +} + #if defined(QT_BUILD_INTERNAL) /*! diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp index af62d0d089..88a7057d2e 100644 --- a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp +++ b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp @@ -96,6 +96,20 @@ private: QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, QList<int> &path1, QList<int> &path2) const; +#if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || defined(Q_OS_QNX) +#define qCreateFloatingTabs(mainWindow, centralWidget, d1, d2, path1, path2)\ + mainWindow = nullptr;\ + Q_UNUSED(path1);\ + Q_UNUSED(path2);\ + QSKIP("Platform not supported"); +#else +#define qCreateFloatingTabs(mainWindow, centralWidget, d1, d2, path1, path2)\ + createFloatingTabs(mainWindow, centralWidget, d1, d2, path1, path2);\ + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);\ + if (!platformSupportingRaise)\ + QSKIP("Platform not supporting raise(). Floating tab based tests will fail.") +#endif + static inline QPoint dragPoint(QDockWidget* dockWidget); static inline QPoint home1(QMainWindow* MainWindow) { return MainWindow->mapToGlobal(MainWindow->rect().topLeft() + QPoint(0.1 * MainWindow->width(), 0.1 * MainWindow->height())); } @@ -447,6 +461,23 @@ void tst_QDockWidget::setFloating() dw.setFloating(dw.isFloating()); QCOMPARE(spy.size(), 0); spy.clear(); + +#if defined(QT_BUILD_INTERNAL) && !defined(Q_OS_WIN) + // Check that setFloating() reparents the dock widget to the main window, + // in case it has a QDockWidgetGroupWindow parent + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + QList<int> path1; + QList<int> path2; + qCreateFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + QVERIFY(qobject_cast<QDockWidgetGroupWindow *>(d1->parentWidget())); + QVERIFY(qobject_cast<QDockWidgetGroupWindow *>(d2->parentWidget())); + d1->setFloating(true); + QTRY_COMPARE(mainWindow, d1->parentWidget()); + QTRY_COMPARE(mainWindow, d2->parentWidget()); +#endif // defined(QT_BUILD_INTERNAL) && !defined(Q_OS_WIN) } void tst_QDockWidget::allowedAreas() @@ -1399,7 +1430,7 @@ void tst_QDockWidget::createFloatingTabs(QMainWindow* &mainWindow, QPointer<QWid // Test will fail if platform doesn't support raise. mainWindow->windowHandle()->handle()->raise(); if (!platformSupportingRaise) - QSKIP("Platform not supporting raise(). Floating tab based tests will fail."); + return; // remember paths to d1 and d2 QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); @@ -1443,8 +1474,7 @@ void tst_QDockWidget::floatingTabs() QMainWindow* mainWindow; QList<int> path1; QList<int> path2; - createFloatingTabs(mainWindow, cent, d1, d2, path1, path2); - std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + qCreateFloatingTabs(mainWindow, cent, d1, d2, path1, path2); QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {d2}); QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {d1}); @@ -1497,9 +1527,13 @@ void tst_QDockWidget::floatingTabs() QTest::mouseClick(floatButton, Qt::LeftButton, Qt::KeyboardModifiers(), pos1); QTest::qWait(waitingTime); - // d1 must be floating again, while d2 is still in its GroupWindow + // d1 and d2 must be floating again QTRY_VERIFY(d1->isFloating()); - QTRY_VERIFY(!d2->isFloating()); + QTRY_VERIFY(d2->isFloating()); + + // d2 was the active tab, so d1 was not visible + QTRY_VERIFY(d1->isVisible()); + QTRY_VERIFY(d2->isVisible()); // Plug back into dock areas qCDebug(lcTestDockWidget) << "*** test plugging back to dock areas ***"; @@ -1562,8 +1596,7 @@ void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget() QMainWindow* mainWindow; QList<int> path1; QList<int> path2; - createFloatingTabs(mainWindow, cent, d1, d2, path1, path2); - std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + qCreateFloatingTabs(mainWindow, cent, d1, d2, path1, path2); switch (removalReason) { case ChildRemovalReason::Destroyed: @@ -1698,6 +1731,9 @@ void tst_QDockWidget::closeAndDelete() if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Test skipped on Wayland."); #ifdef QT_BUILD_INTERNAL + if (QSysInfo::productType() == "rhel") + QSKIP("Memory leak on RHEL 9.2 QTBUG-124559", TestFailMode::Abort); + // Create a mainwindow with a central widget and two dock widgets QPointer<QDockWidget> d1; QPointer<QDockWidget> d2; @@ -1992,6 +2028,7 @@ void tst_QDockWidget::saveAndRestore() QCOMPARE(d1->isFloating(), isFloating1); QCOMPARE(d2->isFloating(), isFloating2); +#undef qCreateFloatingTabs #endif // QT_BUILD_INTERNAL } diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp index 7c1fb97b97..06750b099b 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp @@ -124,6 +124,9 @@ private slots: void platformMenu(); void addActionQt5connect(); void QTBUG_65488_hiddenActionTriggered(); + void pressDragRelease_data(); + void pressDragRelease(); + protected slots: void onSimpleActivated( QAction*); void onComplexActionTriggered(); @@ -141,6 +144,7 @@ private: int m_simpleActivatedCount; int m_complexTriggerCount[int('k')]; QMenuBar* taskQTBUG53205MenuBar; + QScopedPointer<QPointingDevice> m_touchScreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); }; // Testing get/set functions @@ -1420,6 +1424,70 @@ void tst_QMenuBar::QTBUG_65488_hiddenActionTriggered() QCOMPARE(spy.size(), 0); } +void tst_QMenuBar::pressDragRelease_data() +{ + QTest::addColumn<QPointingDevice *>("device"); + + QTest::newRow("mouse") << const_cast<QPointingDevice *>(QPointingDevice::primaryPointingDevice()); + QTest::newRow("touchscreen") << m_touchScreen.get(); +} + +void tst_QMenuBar::pressDragRelease() +{ + QFETCH(QPointingDevice *, device); + + QMainWindow w; + const TestMenu menu = initWindowWithComplexMenuBar(w); + const QMenu *firstMenu = menu.menus.first(); + QAction *hoveredAction = nullptr; + connect(firstMenu, &QMenu::hovered, firstMenu, [&hoveredAction](QAction *hov) { hoveredAction = hov; }); + w.show(); + QVERIFY(QTest::qWaitForWindowActive(&w)); + QWindow *win = w.windowHandle(); + const QPoint p1(50, w.menuBar()->geometry().height() / 2); + switch (device->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + QTest::mousePress(win, Qt::LeftButton, {}, p1); + break; + case QInputDevice::DeviceType::TouchScreen: + QTest::touchEvent(win, device).press(0, p1); + break; + default: + break; + } + + QTRY_VERIFY(firstMenu->isVisible()); + const QPoint firstMenuItemPos = firstMenu->geometry().center() - QPoint(0, 2); + const QPoint firstMenuItemPosInWin = w.mapFromGlobal(firstMenuItemPos); + switch (device->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + QTest::mouseMove(win, firstMenuItemPosInWin); + break; + case QInputDevice::DeviceType::TouchScreen: + QTest::touchEvent(win, device).move(0, firstMenuItemPosInWin); + break; + default: + break; + } + QVERIFY(hoveredAction); + QCOMPARE(hoveredAction, firstMenu->actionAt(firstMenu->mapFromGlobal(firstMenuItemPos))); + QSignalSpy triggeredSpy(hoveredAction, &QAction::triggered); + switch (device->type()) { + case QInputDevice::DeviceType::Mouse: + case QInputDevice::DeviceType::TouchPad: + QTest::mouseRelease(win, Qt::LeftButton, {}, firstMenuItemPosInWin); + break; + case QInputDevice::DeviceType::TouchScreen: + QTest::touchEvent(win, device).release(0, firstMenuItemPosInWin); + break; + default: + break; + } + QTRY_COMPARE(triggeredSpy.size(), 1); +} + // QTBUG-56526 void tst_QMenuBar::platformMenu() { diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 773ccd894c..51f898c953 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -203,7 +203,7 @@ void tst_QOpenGLWidget::deviceLoss() w->resize(640, 480); w->show(); - auto rhi = w->backingStore()->handle()->rhi(); + auto rhi = w->backingStore()->handle()->rhi(w->windowHandle()); QNativeInterface::QEGLContext *rhiContext = nullptr; if (rhi->backend() == QRhi::OpenGLES2) { auto rhiHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); diff --git a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp index 92ce1f5419..73360ae21d 100644 --- a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +++ b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp @@ -339,7 +339,6 @@ void tst_QPushButton::setAccel() // The shortcut will not be activated unless the button is in a active // window and has focus - QApplicationPrivate::setActiveWindow(testWidget); testWidget->setFocus(); QVERIFY(QTest::qWaitForWindowActive(testWidget)); QTest::keyClick(testWidget, 'A', Qt::AltModifier); diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp index 4ccecce990..27343edcde 100644 --- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp +++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp @@ -927,7 +927,6 @@ void tst_QSpinBox::editingFinished() layout->addWidget(box2); testFocusWidget.show(); - QApplicationPrivate::setActiveWindow(&testFocusWidget); QVERIFY(QTest::qWaitForWindowActive(&testFocusWidget)); box->activateWindow(); box->setFocus(); diff --git a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp index 5d66f5922a..6dfab07fed 100644 --- a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp +++ b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp @@ -19,7 +19,6 @@ class TestBrowser : public QTextBrowser public: inline TestBrowser() { show(); - QApplicationPrivate::setActiveWindow(this); activateWindow(); setFocus(); QVERIFY(QTest::qWaitForWindowActive(this)); diff --git a/tests/auto/xml/CMakeLists.txt b/tests/auto/xml/CMakeLists.txt index 4b8cc88d78..92ac1870f6 100644 --- a/tests/auto/xml/CMakeLists.txt +++ b/tests/auto/xml/CMakeLists.txt @@ -1 +1,3 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(dom) |