Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package qcoro for openSUSE:Factory checked in at 2023-05-10 16:16:20 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/qcoro (Old) and /work/SRC/openSUSE:Factory/.qcoro.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "qcoro" Wed May 10 16:16:20 2023 rev:8 rq:1085641 version:0.9.0 Changes: -------- --- /work/SRC/openSUSE:Factory/qcoro/qcoro.changes 2023-02-09 16:23:25.174778676 +0100 +++ /work/SRC/openSUSE:Factory/.qcoro.new.1533/qcoro.changes 2023-05-10 16:16:22.422211339 +0200 @@ -1,0 +2,17 @@ +Thu Apr 27 08:23:17 UTC 2023 - Christophe Marin <[email protected]> + +- Update to 0.9.0 + * Make QCoro::Generator properly move-constructible + * iOS support: handle QProcess being not available + * Fix QCoro::connect with QFutures + * Fix debug build against MSVC2022 + * Fix CheckAtomic failing on Windows with Clang + * Make how Qt packages are found more convenient + * Fix clang 16 builds + * Fix crash in QCoroSignal when signal is received after + * destruction + * Fix connecting to member function pointers + * QML: Add declarative API for awaiting a task + * Implement QCoroTest + +------------------------------------------------------------------- Old: ---- qcoro-0.8.0.tar.gz New: ---- qcoro-0.9.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ qcoro.spec ++++++ --- /var/tmp/diff_new_pack.BaOK8f/_old 2023-05-10 16:16:22.862213942 +0200 +++ /var/tmp/diff_new_pack.BaOK8f/_new 2023-05-10 16:16:22.866213965 +0200 @@ -36,7 +36,7 @@ %endif # Name: qcoro%{?_pkg_name_suffix} -Version: 0.8.0 +Version: 0.9.0 Release: 0 Summary: Coroutines for Qt License: MIT @@ -63,6 +63,7 @@ # C++-20 support is needed. Qt6 already requires gcc10 %if 0%{?qt5} && 0%{?suse_version} == 1500 BuildRequires: gcc10-c++ +BuildRequires: gcc10-PIE %endif %description @@ -166,18 +167,12 @@ # 20220713: tests still randomly fail, mostly on arm #%%ctest -%post -n libQCoro%{_qt_suffix}Core%{sonum} -p /sbin/ldconfig -%post -n libQCoro%{_qt_suffix}DBus%{sonum} -p /sbin/ldconfig -%post -n libQCoro%{_qt_suffix}Network%{sonum} -p /sbin/ldconfig -%post -n libQCoro%{_qt_suffix}Qml%{sonum} -p /sbin/ldconfig -%post -n libQCoro%{_qt_suffix}Quick%{sonum} -p /sbin/ldconfig -%post -n libQCoro%{_qt_suffix}WebSockets%{sonum} -p /sbin/ldconfig -%postun -n libQCoro%{_qt_suffix}Core%{sonum} -p /sbin/ldconfig -%postun -n libQCoro%{_qt_suffix}DBus%{sonum} -p /sbin/ldconfig -%postun -n libQCoro%{_qt_suffix}Network%{sonum} -p /sbin/ldconfig -%postun -n libQCoro%{_qt_suffix}Qml%{sonum} -p /sbin/ldconfig -%postun -n libQCoro%{_qt_suffix}Quick%{sonum} -p /sbin/ldconfig -%postun -n libQCoro%{_qt_suffix}WebSockets%{sonum} -p /sbin/ldconfig +%ldconfig_scriptlets -n libQCoro%{_qt_suffix}Core%{sonum} +%ldconfig_scriptlets -n libQCoro%{_qt_suffix}DBus%{sonum} +%ldconfig_scriptlets -n libQCoro%{_qt_suffix}Network%{sonum} +%ldconfig_scriptlets -n libQCoro%{_qt_suffix}Qml%{sonum} +%ldconfig_scriptlets -n libQCoro%{_qt_suffix}Quick%{sonum} +%ldconfig_scriptlets -n libQCoro%{_qt_suffix}WebSockets%{sonum} %files -n libQCoro%{_qt_suffix}Core%{sonum} %license LICENSES/* @@ -214,6 +209,7 @@ %{_libdir}/cmake/QCoro%{_qt_suffix}Network/ %{_libdir}/cmake/QCoro%{_qt_suffix}Qml/ %{_libdir}/cmake/QCoro%{_qt_suffix}Quick/ +%{_libdir}/cmake/QCoro%{_qt_suffix}Test/ %{_libdir}/cmake/QCoro%{_qt_suffix}WebSockets/ %{_libdir}/libQCoro%{_qt_suffix}Core.so %{_libdir}/libQCoro%{_qt_suffix}DBus.so ++++++ qcoro-0.8.0.tar.gz -> qcoro-0.9.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/.github/workflows/build-docker-images.yml new/qcoro-0.9.0/.github/workflows/build-docker-images.yml --- old/qcoro-0.8.0/.github/workflows/build-docker-images.yml 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/.github/workflows/build-docker-images.yml 2023-04-27 10:09:19.000000000 +0200 @@ -72,5 +72,5 @@ push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - file: docker/Dockerfile + file: docker/Dockerfile.${{ matrix.compiler }} context: docker diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/.github/workflows/build-linux.yml new/qcoro-0.9.0/.github/workflows/build-linux.yml --- old/qcoro-0.8.0/.github/workflows/build-linux.yml 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/.github/workflows/build-linux.yml 2023-04-27 10:09:19.000000000 +0200 @@ -9,13 +9,13 @@ pull_request: types: [opened, synchronize, reopened, edited] - env: BUILD_TYPE: Debug QTEST_FUNCTION_TIMEOUT: 60000 jobs: + generate-matrix: name: Generate build matrix runs-on: ubuntu-latest @@ -35,6 +35,10 @@ strategy: matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }} fail-fast: false + defaults: + run: + shell: bash -l {0} + runs-on: ${{ matrix.runs_on }} name: ${{ matrix.platform }}-${{ matrix.compiler_full }}-qt-${{ matrix.qt_version }} @@ -51,7 +55,6 @@ cmake -E make_directory build - name: Configure CMake - shell: bash run: | QT_VERSION_MAJOR=$(echo ${{ matrix.qt_version }} | cut -d'.' -f1) @@ -69,12 +72,10 @@ ${EXTRA_CMAKE_FLAGS} - name: Build - shell: bash run: | cmake --build build --config $BUILD_TYPE --parallel $(nproc) --verbose - name: Test - shell: bash run: | cd build QT_LOGGING_TO_CONSOLE=1 ctest -C $BUILD_TYPE \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/.github/workflows/generate-matrix.py new/qcoro-0.9.0/.github/workflows/generate-matrix.py --- old/qcoro-0.8.0/.github/workflows/generate-matrix.py 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/.github/workflows/generate-matrix.py 2023-04-27 10:09:19.000000000 +0200 @@ -36,7 +36,7 @@ }, { "name": "clang", - "versions": [ "11", "14", "dev" ] + "versions": [ "11", "14", "16", "dev" ] } ] } @@ -61,7 +61,7 @@ if compiler == "gcc": return "gcc" elif compiler == "clang": - return "silkeh/clang" + return "debian" else: return None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/CMakeLists.txt new/qcoro-0.9.0/CMakeLists.txt --- old/qcoro-0.8.0/CMakeLists.txt 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/CMakeLists.txt 2023-04-27 10:09:19.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.18.4) -set(qcoro_VERSION 0.8.0) +set(qcoro_VERSION 0.9.0) set(qcoro_SOVERSION 0) project(qcoro LANGUAGES CXX VERSION ${qcoro_VERSION}) @@ -33,6 +33,8 @@ add_feature_info(QtQuick QCORO_WITH_QTQUICK "Build QtQuick support") option(QCORO_WITH_QML "Build QML integration features" ON) add_feature_info(QtQml QCORO_WITH_QML "Build QML integration features") +option(QCORO_WITH_QTTEST "Build QtTest support" ON) +add_feature_info(QtTest QCORO_WITH_QTTEST "Build QtTest support") #-----------------------------------------------------------# # Dependencies @@ -63,6 +65,9 @@ # Qt6 needs access to private API list(APPEND REQUIRED_QT6_COMPONENTS QmlPrivate) endif() +if (QCORO_WITH_QTTEST) + list(APPEND REQUIRED_QT_COMPONENTS Test) +endif() if (QCORO_BUILD_EXAMPLES) list(APPEND REQUIRED_QT_COMPONENTS Widgets Concurrent) endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/cmake/CheckAtomic.cmake new/qcoro-0.9.0/cmake/CheckAtomic.cmake --- old/qcoro-0.8.0/cmake/CheckAtomic.cmake 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/cmake/CheckAtomic.cmake 2023-04-27 10:09:19.000000000 +0200 @@ -8,7 +8,7 @@ function(check_working_cxx_atomics varname) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++20") CHECK_CXX_SOURCE_COMPILES(" #include <atomic> std::atomic<int> x; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/cmake/QCoroFindQt.cmake new/qcoro-0.9.0/cmake/QCoroFindQt.cmake --- old/qcoro-0.8.0/cmake/QCoroFindQt.cmake 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/cmake/QCoroFindQt.cmake 2023-04-27 10:09:19.000000000 +0200 @@ -12,17 +12,11 @@ set(ARGS_QT_VERSION 5) endif() endif() + + list(APPEND REQUIRED_QT_COMPONENTS "${ARGS_QT${ARGS_QT_VERSION}_COMPONENTS}") + list(FILTER REQUIRED_QT_COMPONENTS EXCLUDE REGEX "Private$$") - foreach (component IN LISTS ARGS_COMPONENTS ARGS_QT${ARGS_QT_VERSION}_COMPONENTS) - message(STATUS "Qt component: ${component}") - if ("${component}" MATCHES "Private$$") - string(REPLACE "Private" "" base_component "${component}") - find_package(Qt${ARGS_QT_VERSION}${base_component} REQUIRED COMPONENTS Private) - else() - find_package(Qt${ARGS_QT_VERSION}${component} REQUIRED) - endif() - endforeach() + find_package(Qt${ARGS_QT_VERSION} REQUIRED COMPONENTS ${REQUIRED_QT_COMPONENTS}) set(${ARGS_FOUND_VER_VAR} ${ARGS_QT_VERSION}) endmacro() - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/docker/Dockerfile new/qcoro-0.9.0/docker/Dockerfile --- old/qcoro-0.8.0/docker/Dockerfile 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/docker/Dockerfile 1970-01-01 01:00:00.000000000 +0100 @@ -1,48 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Daniel Vrátil <[email protected]> -# SPDX-License-Identifier: MIT -# -# Docker image for specific versions of clang or gcc and specific version of Qt. - -ARG compiler_version -ARG compiler_image -FROM ${compiler_image}:${compiler_version} -ARG qt_version -ARG qt_modules -ARG qt_archives - -SHELL ["/bin/bash", "-c"] - -RUN source /etc/os-release && \ - if [[ "${VERSION_ID}" == "10" ]]; then \ - echo "deb http://deb.debian.org/debian buster-backports main" > /etc/apt/sources.list.d/backports.list; \ - fi - -RUN apt-get update \ - && apt-get upgrade --yes \ - && apt-get install --yes --no-install-recommends \ - cmake=3.18.4\* cmake-data=3.18.4\* \ - python3-pip python3-setuptools python3-wheel python3-dev \ - dbus dbus-x11 \ - libglib2.0-dev libxkbcommon-dev libfreetype6-dev libfontconfig1-dev \ - libssl-dev \ - libegl-dev libgl-dev libegl1=1.3.2\* libgl1=1.3.2\* libglx0=1.3.2\* libglvnd0=1.3.2\* \ - libvulkan-dev \ - && apt-get clean - -# Workaround a bug in CMake (?) which tries to look for OpenGL-related libraries -# in /usr/lib/x86_64-unknown-linux-gnu instead of /usr/lib/x86_64-linux-gnu -RUN ln -s x86_64-linux-gnu /usr/lib/x86_64-unknown-linux-gnu - -RUN pip3 install aqtinstall - -WORKDIR /root - -COPY install-qt.sh ./install-qt.sh - -RUN ./install-qt.sh "${qt_version}" "${qt_modules}" "${qt_archives}" - -ENV QT_BASE_DIR "/opt/qt/${qt_version}/gcc_64" -ENV PATH "${QT_BASE_DIR}/bin:${PATH}" -ENV CMAKE_PREFIX_PATH="${QT_BASE_DIR}/lib/cmake" -ENV LD_LIBRARY_PATH "${QT_BASE_DIR}/lib:${LD_LIBRARY_PATH}" -ENV XDG_DATA_DIRS "${QT_BASE_DIR}/share:${XDG_DATA_DIRS}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/docker/Dockerfile.clang new/qcoro-0.9.0/docker/Dockerfile.clang --- old/qcoro-0.8.0/docker/Dockerfile.clang 1970-01-01 01:00:00.000000000 +0100 +++ new/qcoro-0.9.0/docker/Dockerfile.clang 2023-04-27 10:09:19.000000000 +0200 @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2022 Daniel Vrátil <[email protected]> +# SPDX-License-Identifier: MIT +# +# Docker image for specific versions of clang and specific version of Qt. + +FROM debian:bullseye +ARG compiler_version +ARG qt_version +ARG qt_modules +ARG qt_archives + +SHELL ["/bin/bash", "-c"] + +# Install build & runtime dependencies +RUN apt-get update \ + && apt-get upgrade --yes \ + && apt-get install --yes --no-install-recommends \ + cmake=3.18.4\* cmake-data=3.18.4\* \ + make \ + python3-pip python3-setuptools python3-wheel python3-dev \ + dbus dbus-x11 \ + libglib2.0-dev libxkbcommon-dev libfreetype6-dev libfontconfig1-dev \ + libssl-dev \ + libegl-dev libgl-dev libegl1=1.3.2\* libgl1=1.3.2\* libglx0=1.3.2\* libglvnd0=1.3.2\* \ + libvulkan-dev + +# Install and set up clang +RUN \ + # Set up env \ + if [ "${compiler_version}" == "dev" ]; then \ + export CLANG_VERSION_SUFFIX=""; \ + else \ + export CLANG_VERSION_SUFFIX="-${compiler_version}"; \ + fi && \ + echo "export CC=\"/usr/bin/clang${CLANG_VERSION_SUFFIX}\"" >> /etc/profile.d/clang.sh && \ + echo "export CXX=\"/usr/bin/clang++${CLANG_VERSION_SUFFIX}\"" >> /etc/profile.d/clang.sh && \ + # Install tools needed to add the clang repository \ + apt-get install --yes --no-install-recommends ca-certificates wget gnupg && \ + # Add clang repository \ + echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye${CLANG_VERSION_SUFFIX} main" > /etc/apt/sources.list.d/llvm.list && \ + echo "deb-src http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye${CLANG_VERSION_SUFFIX} main" >> /etc/apt/sources.list.d/llvm.list && \ + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ + # Install clang \ + apt-get update && \ + apt-get install --yes --install-recommends clang${CLANG_VERSION_SUFFIX} && \ + clang_major_version=$(clang${CLANG_VERSION_SUFFIX} --version | grep -Eo "[0-9]+.[0-9]+.[0-9]+" | cut -d'.' -f1) && \ + if [ ${clang_major_version} -gt 11 ]; then \ + apt-get install --yes --no-install-recommends libclang-rt-${clang_major_version}-dev; \ + fi + +# Workaround a bug in CMake (?) which tries to look for OpenGL-related libraries +# in /usr/lib/x86_64-unknown-linux-gnu instead of /usr/lib/x86_64-linux-gnu +RUN ln -s x86_64-linux-gnu /usr/lib/x86_64-unknown-linux-gnu + +# Install Qt +WORKDIR /root +RUN pip3 install aqtinstall +COPY install-qt.sh ./install-qt.sh +RUN ./install-qt.sh "${qt_version}" "${qt_modules}" "${qt_archives}" + +# Set Qt up environment +ENV QT_BASE_DIR "/opt/qt/${qt_version}/gcc_64" +ENV PATH "${QT_BASE_DIR}/bin:${PATH}" +ENV CMAKE_PREFIX_PATH "${QT_BASE_DIR}/lib/cmake" +ENV LD_LIBRARY_PATH "${QT_BASE_DIR}/lib:${LD_LIBRARY_PATH}" +ENV XDG_DATA_DIRS "${QT_BASE_DIR}/share:${XDG_DATA_DIRS}" + +ENTRYPOINT [ "/bin/bash", "-l" ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/docker/Dockerfile.gcc new/qcoro-0.9.0/docker/Dockerfile.gcc --- old/qcoro-0.8.0/docker/Dockerfile.gcc 1970-01-01 01:00:00.000000000 +0100 +++ new/qcoro-0.9.0/docker/Dockerfile.gcc 2023-04-27 10:09:19.000000000 +0200 @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2022 Daniel Vrátil <[email protected]> +# SPDX-License-Identifier: MIT +# +# Docker image for specific versions of gcc and specific version of Qt. + +ARG compiler_version +FROM gcc:${compiler_version} +ARG qt_version +ARG qt_modules +ARG qt_archives + +SHELL ["/bin/bash", "-c"] + +# Enable backports in older GCC images to get access to newer cmake +RUN source /etc/os-release && \ + if [[ "${VERSION_ID}" == "10" ]]; then \ + echo "deb http://deb.debian.org/debian buster-backports main" > /etc/apt/sources.list.d/backports.list; \ + fi + +# Install build-time and runtime dependencies +RUN apt-get update \ + && apt-get upgrade --yes \ + && apt-get install --yes --no-install-recommends \ + cmake=3.18.4\* cmake-data=3.18.4\* \ + python3-pip python3-setuptools python3-wheel python3-dev \ + dbus dbus-x11 \ + libglib2.0-dev libxkbcommon-dev libfreetype6-dev libfontconfig1-dev \ + libssl-dev \ + libegl-dev libgl-dev libegl1=1.3.2\* libgl1=1.3.2\* libglx0=1.3.2\* libglvnd0=1.3.2\* \ + libvulkan-dev \ + && apt-get clean + +# Workaround a bug in CMake (?) which tries to look for OpenGL-related libraries +# in /usr/lib/x86_64-unknown-linux-gnu instead of /usr/lib/x86_64-linux-gnu +RUN ln -s x86_64-linux-gnu /usr/lib/x86_64-unknown-linux-gnu + +# Install Qt +WORKDIR /root +RUN pip3 install aqtinstall +COPY install-qt.sh ./install-qt.sh +RUN ./install-qt.sh "${qt_version}" "${qt_modules}" "${qt_archives}" + +# Set up environment +ENV QT_BASE_DIR "/opt/qt/${qt_version}/gcc_64" +ENV PATH "${QT_BASE_DIR}/bin:${PATH}" +ENV CMAKE_PREFIX_PATH="${QT_BASE_DIR}/lib/cmake" +ENV LD_LIBRARY_PATH "${QT_BASE_DIR}/lib:${LD_LIBRARY_PATH}" +ENV XDG_DATA_DIRS "${QT_BASE_DIR}/share:${XDG_DATA_DIRS}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/docs/news/2023/2023-04-27-qcoro-0.9.0-announcement.md new/qcoro-0.9.0/docs/news/2023/2023-04-27-qcoro-0.9.0-announcement.md --- old/qcoro-0.8.0/docs/news/2023/2023-04-27-qcoro-0.9.0-announcement.md 1970-01-01 01:00:00.000000000 +0100 +++ new/qcoro-0.9.0/docs/news/2023/2023-04-27-qcoro-0.9.0-announcement.md 2023-04-27 10:09:19.000000000 +0200 @@ -0,0 +1,77 @@ +--- +title: QCoro 0.9.0 Release Announcement +date: "2023-04-27" +description: > + Important bugfixes, huge improvement to QML support and a brand new Test module. +--- + +<!-- +SPDX-FileCopyrightText: 2023 Daniel Vrátil <[email protected]> + +SPDX-License-Identifier: GFDL-1.3-or-later +--> + +# QCoro 0.9.0 Release Announcement + +Another smallish release with just a few new features, but quite important bugfixes. + +As always, thank you to everyone who reported issues and contributed to QCoro. +Your help is much appreciated! + +## Enhanced QML Support + +Jonah Brüchert has improved the QML support by introducing a declarative API to await +a task result. + +Previously a task result could have only been obtained inside a JavaScript function: + +```qml +Label { + id: usernameLabel + Component.onCompleted: { + api.getUserName().then(function(result) { + usernameLabel.text = result + }) + } +} +``` + +With QCoro 0.9.0 you can use the new declarative API to use the result of an asynchronous +task in a property binding: + +```qml +Label { + id: usernameLabel + text: api.getUserName().await("Loading...").value +} +``` + +The `Label` will now show the string "Loading..." while the asynchronous task is running +and will automatically change to the result of the task once if finishes. + +You can check the [`QCoro::QmlTask` documentation][qcoro-qmltask-docs] for more details. + +## QCoroTest Module + +Yet another release with a new QCoro module! This time it's for tests! The QCoroTest +module contains macros (e.g., `QCORO_VERIFY`, `QCORO_COMPARE`, ...) that are basically +identical to their QtTest counteparts with the only difference being that they can be +used inside a coroutine. + +We already had this macros inside QCoro test suite almost since the very beginning +and realized they can be useful to our users as well, since they will likely want +to have unittests for their coroutine code. + +In some of the next releases we would like to add a little bit more infrastructure +to make writing unittests for coroutines with QtTest even easier. + +Check the [`QCoroTest` module documentation][qcoro-test-docs] with a full list of +the test macros. + +## Full changelog + +[See changelog on Github](https://github.com/danvratil/qcoro/releases/tag/v0.9.0) + +[qcoro-qmltask-docs]: https://qcoro.dvratil.cz/reference/qml/qmltask/ +[qcoro-test-docs]: https://qcoro.dvratil.cz/reference/test/ + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/docs/reference/qml/qmltask.md new/qcoro-0.9.0/docs/reference/qml/qmltask.md --- old/qcoro-0.8.0/docs/reference/qml/qmltask.md 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/docs/reference/qml/qmltask.md 2023-04-27 10:09:19.000000000 +0200 @@ -37,6 +37,7 @@ } ``` +QmlTasks can call a JavaScript function when they complete: ```QML Example { Component.onCompleted: { @@ -44,6 +45,20 @@ } } ``` + +They can also set properties when the value is available: +```QML +import QCoro 0 +import io.me.qmlmodule 1.0 + +Item { + Example { id: store } + + Label { + text: store.fetchValue("key").await().value + } +} +``` [qdoc-qml]: https://doc.qt.io/qt-5/qvariant.html diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/docs/reference/test/index.md new/qcoro-0.9.0/docs/reference/test/index.md --- old/qcoro-0.8.0/docs/reference/test/index.md 1970-01-01 01:00:00.000000000 +0100 +++ new/qcoro-0.9.0/docs/reference/test/index.md 2023-04-27 10:09:19.000000000 +0200 @@ -0,0 +1,66 @@ +<!-- +SPDX-FileCopyrightText: 2023 Daniel Vrátil <[email protected]> +SPDX-License-Identifier: GFDL-1.3-or-later +--> + +# Test Module + +The `Test` module contains coroutine-friendly versions of tests macros +from the [QtTest][qdoc-qtest] module. + +## CMake Usage + +```cmake +find_package(QCoro6 COMPONENTS Test) +... +target_link_libraries(my-target QCoro::Test) +``` + +## QMake Usage + +``` +QT += QCoroTest +``` + +## Test Macros + +The module contains a bunch of test macros that behave identically to their QtTest +counterparts, the only difference being that they can be used inside a coroutine. + +```cpp +#include <QCoroTest> +``` + +* [`QCORO_COMPARE(actual, expected)`][qdoc-qcompare] +* [`QCORO_EXPECT_FAIL(dataIndex, comment, mode)`][qdoc-qexpect-fail] +* [`QCORO_FAIL(message)`][qdoc-qfail] +* [`QCORO_SKIP(description)`][qdoc-qskip] +* [`QCORO_TEST(data, testElement)`][qdoc-qtest] +* [`QCORO_TRY_COMPARE(actual, expected)`][qdoc-qtry-compare] +* [`QCORO_TRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout)`][qdoc-qtry-compare-with-timeout] +* [`QCORO_TRY_VERIFY2(condition, message)`][qdoc-qtry-verify2] +* [`QCORO_TRY_VERIFY(condition)`][qdoc-qtry-verify] +* [`QCORO_TRY_VERIFY2_WITH_TIMEOUT(condition, message, timeout)`][qdoc-qtry-verify2-with-timeout] +* [`QCORO_TRY_VERIFY_WITH_TIMEOUT(condition, timeout)`][qdoc-qtry-verify-with-timeout] +* [`QCORO_VERIFY2(condition, message)`][qdoc-qverify2] +* [`QCORO_VERIFY(condition)`][qdoc-qverify] +* [`QCORO_VERIFY_EXCEPTION_THROWN(expression, exceptionType)`][qdoc-qverify-exception-thrown] + + +[qdoc-qtest]: https://doc.qt.io/qt-5/qttest-index.html +[qdoc-qcompare]: https://doc.qt.io/qt-5/qtest.html#QCOMPARE +[qdoc-qexpect-fail]: https://doc.qt.io/qt-5/qtest.html#QEXPECT_FAIL +[qdoc-qfail]: https://doc.qt.io/qt-5/qtest.html#QFAIL +[qdoc-qskip]: https://doc.qt.io/qt-5/qtest.html#QSKIP +[qdoc-qtest]: https://doc.qt.io/qt-5/qtest.html#QTEST +[qdoc-qtry-compare]: https://doc.qt.io/qt-5/qtest.html#QTRY_COMPARE +[qdoc-qtry-compare-with-timeout]: https://doc.qt.io/qt-5/qtest.html#QTRY_COMPARE_WITH_TIMEOUT +[qdoc-qtry-verify2]: https://doc.qt.io/qt-5/qtest.html#QTRY_VERIFY2 +[qdoc-qtry-verify]: https://doc.qt.io/qt-5/qtest.html#QTRY_VERIFY +[qdoc-qtry-verify2-with-timeout]: https://doc.qt.io/qt-5/qtest.html#QTRY_VERIFY2_WITH_TIMEOUT +[qdoc-qtry-verify-with-timeout]: https://doc.qt.io/qt-5/qtest.html#QTRY_VERIFY_WITH_TIMEOUT +[qdoc-qverify2]: https://doc.qt.io/qt-5/qtest.html#QVERIFY2 +[qdoc-qverify]: https://doc.qt.io/qt-5/qtest.html#QVERIFY +[qdoc-qverify-exception-thrown]: https://doc.qt.io/qt-5/qtest.html#QVERIFY_EXCEPTION_THROWN + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/mkdocs.yml new/qcoro-0.9.0/mkdocs.yml --- old/qcoro-0.8.0/mkdocs.yml 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/mkdocs.yml 2023-04-27 10:09:19.000000000 +0200 @@ -101,6 +101,8 @@ - Qml: - reference/qml/index.md - QCoro::QmlTask: reference/qml/qmltask.md + - Test: + - reference/test/index.md - Changelog: changelog.md - About: - License: about/license.md diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/CMakeLists.txt new/qcoro-0.9.0/qcoro/CMakeLists.txt --- old/qcoro-0.8.0/qcoro/CMakeLists.txt 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/CMakeLists.txt 2023-04-27 10:09:19.000000000 +0200 @@ -94,3 +94,7 @@ if (QCORO_WITH_QML) add_subdirectory(qml) endif() + +if (QCORO_WITH_QTTEST) + add_subdirectory(test) +endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/QCoroMacros.cmake new/qcoro-0.9.0/qcoro/QCoroMacros.cmake --- old/qcoro-0.8.0/qcoro/QCoroMacros.cmake 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/QCoroMacros.cmake 2023-04-27 10:09:19.000000000 +0200 @@ -5,7 +5,9 @@ if (MSVC) # clang-cl behaves like MSVC and enables coroutines automatically when C++20 is enabled else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts") + endif() endif() elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # MSVC auto-enables coroutine support when C++20 is enabled diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/core/qcoroprocess.cpp new/qcoro-0.9.0/qcoro/core/qcoroprocess.cpp --- old/qcoro-0.8.0/qcoro/core/qcoroprocess.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/core/qcoroprocess.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -2,6 +2,10 @@ // // SPDX-License-Identifier: MIT +#include <QtGlobal> + +#if QT_CONFIG(process) + #include "qcoroprocess.h" #include "qcorosignal.h" @@ -51,3 +55,5 @@ static_cast<QProcess *>(mDevice.data())->start(program, arguments, mode); return waitForStarted(timeout); } + +#endif // QT_CONFIG(process) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/core/qcoroprocess.h new/qcoro-0.9.0/qcoro/core/qcoroprocess.h --- old/qcoro-0.8.0/qcoro/core/qcoroprocess.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/core/qcoroprocess.h 2023-04-27 10:09:19.000000000 +0200 @@ -12,6 +12,8 @@ #include <QIODevice> +#if QT_CONFIG(process) + class QProcess; namespace QCoro::detail { @@ -126,3 +128,4 @@ return QCoro::detail::QCoroProcess{p}; } +#endif // QT_CONFIG(process) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/core/qcorosignal.h new/qcoro-0.9.0/qcoro/core/qcorosignal.h --- old/qcoro-0.8.0/qcoro/core/qcorosignal.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/core/qcorosignal.h 2023-04-27 10:09:19.000000000 +0200 @@ -105,11 +105,13 @@ using typename QCoroSignalBase<T, FuncPtr>::result_type; QCoroSignal(T *obj, FuncPtr &&ptr, std::chrono::milliseconds timeout) - : QCoroSignalBase<T, FuncPtr>(obj, std::forward<FuncPtr>(ptr), timeout) {} + : QCoroSignalBase<T, FuncPtr>(obj, std::forward<FuncPtr>(ptr), timeout) + , mDummyReceiver(std::make_unique<QObject>()) {} QCoroSignal(const QCoroSignal &) = delete; QCoroSignal(QCoroSignal &&other) noexcept : QCoroSignalBase<T, FuncPtr>(std::move(other)) - , mResult(std::move(other.mResult)) { + , mResult(std::move(other.mResult)) + , mDummyReceiver(std::move(other.mDummyReceiver)) { if (this->mConn) { QObject::disconnect(this->mConn); setupConnection(); @@ -119,6 +121,7 @@ QCoroSignal &operator=(QCoroSignal &&other) noexcept { QCoroSignalBase<T, FuncPtr>::operator=(std::move(other)); std::swap(mResult, other.mResult); + std::swap(mDummyReceiver, other.mDummyReceiver); if (this->mConn) { QObject::disconnect(this->mConn); setupConnection(); @@ -148,7 +151,7 @@ void setupConnection() { Q_ASSERT(!this->mConn); this->mConn = QObject::connect( - this->mObj, this->mFuncPtr, this->mObj, + this->mObj, this->mFuncPtr, this->mDummyReceiver.get(), [this](auto &&...args) mutable { if (this->mTimeoutTimer) { this->mTimeoutTimer->stop(); @@ -165,6 +168,7 @@ result_type mResult; std::coroutine_handle<> mAwaitingCoroutine; + std::unique_ptr<QObject> mDummyReceiver; }; template<concepts::QObject T, typename FuncPtr> @@ -237,7 +241,7 @@ return; } this->mConn = QObject::connect( - this->mObj, this->mFuncPtr, this->mObj, + this->mObj, this->mFuncPtr, &this->mDummyReceiver, [this](auto && ...args) mutable { if (this->mTimeoutTimer) { this->mTimeoutTimer->stop(); @@ -253,6 +257,7 @@ std::coroutine_handle<> mAwaitingCoroutine; std::deque<typename result_type::value_type> mQueue; + QObject mDummyReceiver; }; template<concepts::QObject T, typename FuncPtr> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/qcorogenerator.h new/qcoro-0.9.0/qcoro/qcorogenerator.h --- old/qcoro-0.8.0/qcoro/qcorogenerator.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/qcorogenerator.h 2023-04-27 10:09:19.000000000 +0200 @@ -236,9 +236,16 @@ using iterator = GeneratorIterator<T>; explicit Generator() = default; - Generator(Generator &&) noexcept = default; + Generator(Generator &&other) noexcept { + mGeneratorCoroutine = other.mGeneratorCoroutine; + other.mGeneratorCoroutine = std::coroutine_handle<promise_type>::from_address(nullptr); + } Generator(const Generator &) = delete; - Generator &operator=(Generator &&) noexcept = default; + Generator &operator=(Generator &&other) noexcept { + mGeneratorCoroutine = other.mGeneratorCoroutine; + other.mGeneratorCoroutine = std::coroutine_handle<promise_type>::from_address(nullptr); + return *this; + }; Generator &operator=(const Generator &) = delete; /** * @brief Destroys this Generator object and the associated generator coroutine. @@ -246,7 +253,9 @@ * All values allocated on the generator stack are destroyed automatically. */ ~Generator() { - mGeneratorCoroutine.destroy(); + if (mGeneratorCoroutine.address() != nullptr) { + mGeneratorCoroutine.destroy(); + } } /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/qcorotask.h new/qcoro-0.9.0/qcoro/qcorotask.h --- old/qcoro-0.8.0/qcoro/qcorotask.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/qcorotask.h 2023-04-27 10:09:19.000000000 +0200 @@ -581,7 +581,7 @@ private: template<typename ThenCallback, typename ... Args> - auto invokeCb(ThenCallback &&callback, Args && ... args) { + auto invokeCb(ThenCallback &&callback, [[maybe_unused]] Args && ... args) { if constexpr (std::is_invocable_v<ThenCallback, Args ...>) { return callback(std::forward<Args>(args) ...); } else { @@ -780,7 +780,7 @@ * For all other types, it takes a value of the type as single argument. */ template <typename T, typename QObjectSubclass, typename Callback> -requires std::is_invocable_v<Callback> || std::is_invocable_v<Callback, T> || std::is_invocable_v<Callback, QObjectSubclass *> +requires std::is_invocable_v<Callback> || std::is_invocable_v<Callback, T> || std::is_invocable_v<Callback, QObjectSubclass *> || std::is_invocable_v<Callback, QObjectSubclass *, T> void connect(QCoro::Task<T> &&task, QObjectSubclass *context, Callback func) { QPointer ctxWatcher = context; if constexpr (std::is_same_v<T, void>) { @@ -796,12 +796,10 @@ } else { task.then([ctxWatcher, func = std::move(func)](auto &&value) { if (ctxWatcher) { - if constexpr (std::is_invocable_v<Callback, T>) { - if constexpr (std::is_member_function_pointer_v<Callback>) { - (ctxWatcher->*func)(std::forward<decltype(value)>(value)); - } else { - func(std::forward<decltype(value)>(value)); - } + if constexpr (std::is_invocable_v<Callback, QObjectSubclass, T>) { + (ctxWatcher->*func)(std::forward<decltype(value)>(value)); + } else if constexpr (std::is_invocable_v<Callback, T>) { + func(std::forward<decltype(value)>(value)); } else { Q_UNUSED(value); if constexpr (std::is_member_function_pointer_v<Callback>) { @@ -817,7 +815,7 @@ template <typename T, typename QObjectSubclass, typename Callback> requires detail::TaskConvertible<T> - && (std::is_invocable_v<Callback> || std::is_invocable_v<Callback, detail::awaitable_return_type_t<T>> || std::is_invocable_v<Callback, QObjectSubclass *>) + && (std::is_invocable_v<Callback> || std::is_invocable_v<Callback, detail::convertible_awaitable_return_type_t<T>> || std::is_invocable_v<Callback, QObjectSubclass *> || std::is_invocable_v<Callback, QObjectSubclass *, T>) && (!detail::isTask_v<T>) void connect(T &&future, QObjectSubclass *context, Callback func) { auto task = detail::toTask(std::move(future)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/qml/qcoroqml.cpp new/qcoro-0.9.0/qcoro/qml/qcoroqml.cpp --- old/qcoro-0.8.0/qcoro/qml/qcoroqml.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/qml/qcoroqml.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -10,4 +10,5 @@ void QCoro::Qml::registerTypes() { qRegisterMetaType<QCoro::QmlTask>(); + qmlRegisterAnonymousType<QCoro::QmlTaskListener>("QCoro", 0); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/qml/qcoroqmltask.cpp new/qcoro-0.9.0/qcoro/qml/qcoroqmltask.cpp --- old/qcoro-0.8.0/qcoro/qml/qcoroqmltask.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/qml/qcoroqmltask.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -9,7 +9,10 @@ #include <optional> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4458 4201) #include <private/qjsvalue_p.h> +QT_WARNING_POP #endif Q_DECLARE_LOGGING_CATEGORY(qcoroqml) @@ -70,4 +73,29 @@ }); } +QmlTaskListener *QmlTask::await(const QVariant &intermediateValue) +{ + QPointer listener = new QmlTaskListener(); + if (!intermediateValue.isNull()) { + listener->setValue(QVariant(intermediateValue)); + } + d->task->then([listener](auto &&value) { + if (listener) { + listener->setValue(std::move(value)); + } + }); + return listener; +} + +QVariant QmlTaskListener::value() const +{ + return m_value; +} + +void QmlTaskListener::setValue(QVariant &&value) +{ + m_value = std::move(value); + Q_EMIT valueChanged(); +} + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/qml/qcoroqmltask.h new/qcoro-0.9.0/qcoro/qml/qcoroqmltask.h --- old/qcoro-0.8.0/qcoro/qml/qcoroqmltask.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/qml/qcoroqmltask.h 2023-04-27 10:09:19.000000000 +0200 @@ -17,6 +17,8 @@ struct QmlTaskPrivate; +class QmlTaskListener; + //! QML type that allows to react to asynchronous computations from QML struct QCOROQML_EXPORT QmlTask { Q_GADGET @@ -77,10 +79,36 @@ */ Q_INVOKABLE void then(QJSValue func); + //! Allows to use tasks in property bindings + /*! + * The value property is initially null, and will be updated to the actual value once it is available. + * Example usage: + * ``` + * text: asyncLoadText().await().value + * ``` + * + * Optionally, an intermediate value can be passed to await, + * which will be returned by value while calculating the asynchronous result. + */ + Q_INVOKABLE QCoro::QmlTaskListener *await(const QVariant &intermediateValue = {}); + private: QSharedDataPointer<QmlTaskPrivate> d; }; +class QmlTaskListener : public QObject { + Q_OBJECT + Q_PROPERTY(QVariant value READ value NOTIFY valueChanged) + +public: + QVariant value() const; + void setValue(QVariant &&value); + Q_SIGNAL void valueChanged(); + +private: + QVariant m_value; +}; + } Q_DECLARE_METATYPE(QCoro::QmlTask) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/test/CMakeLists.txt new/qcoro-0.9.0/qcoro/test/CMakeLists.txt --- old/qcoro-0.8.0/qcoro/test/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/test/CMakeLists.txt 2023-04-27 10:09:19.000000000 +0200 @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2023 Daniel Vrátil <[email protected]> +# +# SPDX-License-Identifier: MIT + +add_qcoro_library( + NAME Test + INTERFACE + CAMELCASE_HEADERS + QCoroTest + QT_LINK_LIBRARIES + INTERFACE Test +) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/qcoro/test/qcorotest.h new/qcoro-0.9.0/qcoro/test/qcorotest.h --- old/qcoro-0.8.0/qcoro/test/qcorotest.h 1970-01-01 01:00:00.000000000 +0100 +++ new/qcoro-0.9.0/qcoro/test/qcorotest.h 2023-04-27 10:09:19.000000000 +0200 @@ -0,0 +1,190 @@ +// SPDX-FileCopyrightText: 2022 Daniel Vrátil <[email protected]> +// +// SPDX-License-Identifier: MIT + +/* + * This code is very closely based on qtestcase.h header file from Qt + * Copyright (c) 2016 The Qt Company Ltd., LGPLv3/GPLv2 or GPLv3 or any + * later version approved by the KDE Free Qt Foundation. + */ + +#pragma once + +#include <qtestcase.h> +#include <QString> + +/** + * @brief Coroutine-friendly version of QVERIFY test macro. + **/ +#define QCORO_VERIFY(statement) \ + do { \ + if (!QTest::qVerify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__)) \ + co_return; \ + } while (false) + +/** + * @brief Coroutine-friendly version of QFAIL test macro. + **/ +#define QCORO_FAIL(message) \ + do { \ + QTest::qFail(static_cast<const char *>(message), __FILE__, __LINE__); \ + co_return; \ + } while (false) + +/** + * @brief Coroutine-friendly version of QVERIFY2 test macro. + **/ +#define QCORO_VERIFY2(statement, description) \ + do { \ + if (statement) { \ + if (!QTest::qVerify(true, #statement, static_cast<const char *>(description), __FILE__, __LINE__)) \ + co_return; \ + } else { \ + if (!QTest::qVerify(false, #statement, static_cast<const char *>(description), __FILE__, __LINE__)) \ + co_return; \ + } \ + } while (false) + +/** + * @brief Coroutine-friendly version of QCOMPARE test macro. + **/ +#define QCORO_COMPARE(actual, expected) \ + do { \ + if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ + co_return; \ + } while (false) + + +#if !defined(QT_NO_EXCEPTIONS) + +/** + * @brief Coroutine-friendly version of QVERIFY_EXCEPTION_THROWN macro. + **/ +#define QCORO_VERIFY_EXCEPTION_THROWN(expression, exceptionType) \ + do { \ + try { \ + try { \ + expression; \ + QTest::qFail("Expected exception of type " #exceptionType " to be thrown" \ + " but no exception caught", \ + __FILE__, __LINE__); \ + co_return; \ + } catch (const exceptionType &) {} \ + } catch (const std::exception &e) { \ + const QByteArray msg = QByteArray() + \ + "Expected exception of type " #exceptionType \ + " to be thrown, but std::exception caught with message " + \ + e.what(); \ + QTest::qFail(msg.constData(), __FILE__, __LINE__); \ + co_return; \ + } catch (...) { \ + QTest::qFail("Expected exception of type " #exceptionType " to be thrown" \ + " but unknown exception caught", \ + __FILE__, __LINE__); \ + co_return; \ + } \ + } while (false) + +#else // QT_NO_EXCEPTIONS + +#define QCORO_VERIFY_EXCEPTION_THROWN(expression, exceptionType) \ + static_assert(false, "Support of exceptions is disabled") + +#endif // QT_NO_EXCEPTIONS + +/* @cond INTERNAL */ + +#define QCORO_TRY_TIMEOUT_DEBUG_IMPL(expr, timeoutValue, step) \ + if (!(expr)) { \ + QTRY_LOOP_IMPL((expr), (2 * timeoutValue), step); \ + if (expr) { \ + QString msg = QString::fromUtf8("QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) was too short, %3 ms would have been sufficient this time."); \ + msg = msg.arg(QString::fromUtf8(#expr)).arg(timeoutValue).arg(timeoutValue + qt_test_i); \ + QCORO_FAIL(qPrintable(msg)); \ + } \ + } + +#define QCORO_TRY_IMPL(expr, timeout) \ + const int qcoro_test_step = timeout < 350 ? timeout / 7 + 1 : 50; \ + const int qcoro_test_timeoutValue = timeout; \ + { QCORO_TRY_LOOP_IMPL((expr), qcoro_test_timeoutValue, qcoro_test_step); } \ + QCORO_TRY_TIMEOUT_DEBUG_IMPL((expr), qcoro_test_timeoutValue, qcoro_test_step) \ + +/* @endcond */ + +/** + * @brief Coroutine-friendly version of QVERIFY_WITH_TIMEOUT test macro. + **/ +#define QCORO_TRY_VERIFY_WITH_TIMEOUT(expr, timeout) \ + do { \ + QCORO_TRY_IMPL((expr), timeout); \ + QCORO_VERIFY(expr); \ + } while (false); + +/** + * @brief Coroutine-friendly version of QTRY_VERIFY test macro. + **/ +#define QCORO_TRY_VERIFY(expr) QCORO_TRY_VERIFY_WITH_TIMEOUT((expr), 5000) + +/** + * @brief Coroutine-friendly version of QTRY_VERIFY2_WITH_TIMEOUT test macro. + **/ +#define QCORO_TRY_VERIFY2_WITH_TIMEOUT(expr, messageExpression, timeout) \ + do { \ + QCORO_TRY_IMPL((expr), timeout); \ + QCORO_VERIFY2((expr), messageExpression); \ + } while (false) + +/** + * @brief Coroutine-friendly version of QTRY_VERIFY2 test macro. + **/ +#define QCORO_TRY_VERIFY2(expr, messageExpression) \ + QCORO_TRY_VERIFY2_WITH_TIMEOUT((expr), (messageExpression), 5000) + +/** + * @brief Coroutine-friendly version of QTRY_COMPARE_WITH_TIMEOUT test macro. + **/ +#define QCORO_TRY_COMPARE_WITH_TIMEOUT(expr, expected, timeout) \ + do { \ + QCORO_TRY_IMPL(((expr) == (expected)), timeout); \ + QCORO_COMPARE((expr), expected); \ + } while (false) + +/** + * @brief Coroutine-friendly version of QTRY_COMPARE test macro. + **/ +#define QCORO_TRY_COMPARE(expr, expected) \ + QCORO_TRY_COMPARE_WITH_TIMEOUT((expr), (expected), 5000) + +/** + * @internal + **/ +#define QCORO_SKIP_INTERNAL(statement) \ + do { \ + QTest::qSkip(static_cast<const char *>(statement), __FILE__, __LINE__); \ + co_return; \ + } while (false) + +/** + * @brief Coroutine-friendly version of QSKIP test macro. + **/ +#define QCORO_SKIP(statement, ...) QCORO_SKIP_INTERNAL(statement) + +/** + * @brief Coroutine-friendly version of QEXPECT_FAIL test macro. + **/ +#define QCORO_EXPECT_FAIL(dataIndex, comment, mode) \ + do { \ + if (!QTest::qExpectFail(dataIndex, static_cast<const char *>(comment), QTest::mode, __FILE__, __LINE__)) \ + co_return; \ + } while (false) + +/** + * @brief Coroutine-friendly version of QTEST test macro. + **/ +#define QCORO_TEST(actual, testElement) \ + do { \ + if (!QTest::qTest(actual, testElement, #actual, #testElement, __FILE__, __LINE__)) \ + co_return; \ + } while (false) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/requirements.txt new/qcoro-0.9.0/requirements.txt --- old/qcoro-0.8.0/requirements.txt 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/requirements.txt 2023-04-27 10:09:19.000000000 +0200 @@ -1,7 +1,7 @@ setuptools wheel -pymdown-extensions~=9.9 -pygments~=2.14 +pymdown-extensions~=9.11 +pygments~=2.15 mkdocs mkdocs-material mkdocs-section-index diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/CMakeLists.txt new/qcoro-0.9.0/tests/CMakeLists.txt --- old/qcoro-0.8.0/tests/CMakeLists.txt 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/CMakeLists.txt 2023-04-27 10:09:19.000000000 +0200 @@ -23,7 +23,7 @@ target_link_libraries( test-${_name} PRIVATE - qcoro_test + qcoro_testlib QCoro${QT_VERSION_MAJOR}Core Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/qcoroasyncgenerator.cpp new/qcoro-0.9.0/tests/qcoroasyncgenerator.cpp --- old/qcoro-0.8.0/tests/qcoroasyncgenerator.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/qcoroasyncgenerator.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -168,6 +168,25 @@ QCORO_COMPARE(testvalue, 4); } + QCoro::Task<> testMovedGenerator_coro(QCoro::TestContext) { + const auto createGenerator = []() -> QCoro::AsyncGenerator<int> { + for (int i = 0; i < 4; ++i) { + co_await sleep(10ms); + co_yield i; + } + }; + + auto originalGenerator = createGenerator(); + auto generator = std::move(originalGenerator); + int testvalue = 0; + for (auto it = co_await generator.begin(), end = generator.end(); it != end; co_await ++it) { + int value = *it; + QCORO_COMPARE(value, testvalue++); + } + QCORO_COMPARE(testvalue, 4); + + } + QCoro::Task<> testException_coro(QCoro::TestContext) { const auto createGenerator = []() -> QCoro::AsyncGenerator<int> { for (int i = 0; i < 4; ++i) { @@ -243,6 +262,7 @@ addTest(ReferenceGenerator) addTest(ConstReferenceGenerator) addTest(MoveonlyGenerator) + addTest(MovedGenerator) addTest(Exception) addTest(ExceptionInDereference) addTest(ExceptionInBegin) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/qcorogenerator.cpp new/qcoro-0.9.0/tests/qcorogenerator.cpp --- old/qcoro-0.8.0/tests/qcorogenerator.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/qcorogenerator.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT #include "qcorogenerator.h" -#include "testmacros.h" +#include "qcorotest.h" #include <QObject> #include <QTest> @@ -145,6 +145,25 @@ ++it; } QCOMPARE(testval, 4); + } + + void testMovedGenerator() { + const auto createGenerator = []() -> QCoro::Generator<int> { + for (int i = 0; i < 4; ++i) { + co_yield i; + } + }; + + auto originalGenerator = createGenerator(); + auto generator = std::move(originalGenerator); + auto it = generator.begin(); + int testval = 0; + while (it != generator.end()) { + int value = *it; + QCOMPARE(value, testval++); + ++it; + } + QCOMPARE(testval, 4); } void testException() { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/qcoroqmltask.cpp new/qcoro-0.9.0/tests/qcoroqmltask.cpp --- old/qcoro-0.8.0/tests/qcoroqmltask.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/qcoroqmltask.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -47,7 +47,7 @@ Q_INVOKABLE void reportTestSuccess() { numTestsPassed++; - if (numTestsPassed == 3) { // Number of java script functions that call reportTestSuccess + if (numTestsPassed == 4) { // Number of java script functions that call reportTestSuccess Q_EMIT success(); } } @@ -72,9 +72,21 @@ engine.loadData(R"( import qcoro.test 0.1 +import QCoro 0 import QtQuick 2.7 QtObject { + property string value: QmlObject.qmlTaskFromFuture().await("Loading...").value + + property string valueWithoutIntermediate: QmlObject.qmlTaskFromFuture().await().value + + onValueChanged: { + if (value == "Success") { + console.log("awaiting finished") + QmlObject.reportTestSuccess() + } + } + Component.onCompleted: { QmlObject.startTimer().then(() => { console.log("QCoro::Task JavaScript callback called") @@ -106,6 +118,7 @@ timeout->stop(); running = false; }); + // Crash the test in case the timeout was reachaed without the callback being called connect(timeout, &QTimer::timeout, this, [&]() { #if defined(Q_CC_CLANG) && defined(Q_OS_WINDOWS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/qcorosignal.cpp new/qcoro-0.9.0/tests/qcorosignal.cpp --- old/qcoro-0.8.0/tests/qcorosignal.cpp 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/qcorosignal.cpp 2023-04-27 10:09:19.000000000 +0200 @@ -48,6 +48,27 @@ QTimer mTimer; }; + +class SimpleSignal: public QObject { + Q_OBJECT +public: + void send(int id) { + Q_EMIT messageReceived(id); + } + + Q_SIGNAL void messageReceived(int id); + + QCoro::Task<int> waitForMessage(int id) { + QCORO_FOREACH(int msgId, qCoroSignalListener(this, &SimpleSignal::messageReceived)) { + if (msgId == id) { + co_return id; + } + } + + co_return -1; + } +}; + class QCoroSignalTest : public QCoro::TestObject<QCoroSignalTest> { Q_OBJECT @@ -279,6 +300,16 @@ QCORO_COMPARE(count, 10); } + QCoro::Task<> testSignalAfterListenerQuits_coro(QCoro::TestContext) { + SimpleSignal simple; + auto msg1 = simple.waitForMessage(1); + auto msg2 = simple.waitForMessage(2); + simple.send(1); + simple.send(2); + QCORO_COMPARE(co_await msg1, 1); + QCORO_COMPARE(co_await msg2, 2); + } + private Q_SLOTS: addTest(Triggers) addTest(ReturnsValue) @@ -298,6 +329,7 @@ addTest(SignalListenerTuple) addTest(SignalListenerTimeout) addTest(SignalListenerQueue) + addTest(SignalAfterListenerQuits) }; QTEST_GUILESS_MAIN(QCoroSignalTest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/testlibs/CMakeLists.txt new/qcoro-0.9.0/tests/testlibs/CMakeLists.txt --- old/qcoro-0.8.0/tests/testlibs/CMakeLists.txt 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/testlibs/CMakeLists.txt 2023-04-27 10:09:19.000000000 +0200 @@ -1,11 +1,12 @@ -add_library(qcoro_test +add_library(qcoro_testlib STATIC testobject.cpp testloop.cpp ) -target_include_directories(qcoro_test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(qcoro_test PUBLIC +target_include_directories(qcoro_testlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(qcoro_testlib PUBLIC QCoro${QT_VERSION_MAJOR}Core + QCoro${QT_VERSION_MAJOR}Test Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/testlibs/testmacros.h new/qcoro-0.9.0/tests/testlibs/testmacros.h --- old/qcoro-0.8.0/tests/testlibs/testmacros.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/testlibs/testmacros.h 2023-04-27 10:09:19.000000000 +0200 @@ -4,12 +4,10 @@ #pragma once - //! Executes given \c expr with 10ms delay. #define QCORO_DELAY(expr) \ QTimer::singleShot(10ms, [&]() { expr; }) - #define QCORO_TEST_TIMEOUT(expr) { \ const auto start = std::chrono::steady_clock::now(); \ const bool ok = expr; \ @@ -18,46 +16,4 @@ QCORO_VERIFY((end - start) < 500ms); \ } -#define QCORO_VERIFY(statement) \ - do { \ - if (!QTest::qVerify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__)) \ - co_return; \ - } while (false) - -#define QCORO_COMPARE(actual, expected) \ - do { \ - if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ - co_return; \ - } while (false) - -#define QCORO_FAIL(message) \ - do { \ - QTest::qFail(static_cast<const char *>(message), __FILE__, __LINE__); \ - co_return; \ - } while (false) - -#define QCORO_VERIFY_EXCEPTION_THROWN(expression, exceptionType) \ - do { \ - try { \ - try { \ - expression; \ - QTest::qFail("Expected exception of type " #exceptionType " to be thrown" \ - " but no exception caught", \ - __FILE__, __LINE__); \ - co_return; \ - } catch (const exceptionType &) {} \ - } catch (const std::exception &e) { \ - const QByteArray msg = QByteArray() + \ - "Expected exception of type " #exceptionType \ - " to be thrown, but std::exception caught with message " + \ - e.what(); \ - QTest::qFail(msg.constData(), __FILE__, __LINE__); \ - co_return; \ - } catch (...) { \ - QTest::qFail("Expected exception of type " #exceptionType " to be thrown" \ - " but unknown exception caught", \ - __FILE__, __LINE__); \ - co_return; \ - } \ - } while (false) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/qcoro-0.8.0/tests/testlibs/testobject.h new/qcoro-0.9.0/tests/testlibs/testobject.h --- old/qcoro-0.8.0/tests/testlibs/testobject.h 2023-01-31 20:50:01.000000000 +0100 +++ new/qcoro-0.9.0/tests/testlibs/testobject.h 2023-04-27 10:09:19.000000000 +0200 @@ -4,15 +4,15 @@ #pragma once -#include "testmacros.h" - #include <QEventLoop> #include <QObject> #include <QTest> #include <QTimer> #include <QVariant> -#include "qcoro/qcorotask.h" +#include "qcorotask.h" +#include "qcorotest.h" +#include "testmacros.h" #include "testloop.h" #include <chrono>
