diff options
Diffstat (limited to 'tests/auto/corelib/thread/qpromise')
-rw-r--r-- | tests/auto/corelib/thread/qpromise/CMakeLists.txt | 11 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp | 55 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qpromise/tst_qpromise.cpp | 265 |
3 files changed, 209 insertions, 122 deletions
diff --git a/tests/auto/corelib/thread/qpromise/CMakeLists.txt b/tests/auto/corelib/thread/qpromise/CMakeLists.txt index b841c7812b..c1ca30b34a 100644 --- a/tests/auto/corelib/thread/qpromise/CMakeLists.txt +++ b/tests/auto/corelib/thread/qpromise/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qpromise.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qpromise Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpromise LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpromise SOURCES tst_qpromise.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp index 2e7b8832d3..2cef0dca95 100644 --- a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // Note: this file is published under a license that is different from a default // test sources license. This is intentional to comply with default @@ -100,9 +53,9 @@ void snippet_QPromise::multithreadExample() QFuture<int> future = sharedPromise->future(); // ... -//! [multithread_init] sharedPromise->start(); +//! [multithread_init] //! [multithread_main] // here, QPromise is shared between threads via a smart pointer @@ -135,7 +88,9 @@ void snippet_QPromise::multithreadExample() for (auto& t : threads) t->wait(); +//! [multithread_cleanup] sharedPromise->finish(); +//! [multithread_cleanup] #endif } diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp index b956619e11..2c12e41c93 100644 --- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QDebug> @@ -39,6 +14,8 @@ #include <memory> #include <chrono> +using namespace std::chrono_literals; + class tst_QPromise : public QObject { Q_OBJECT @@ -47,6 +24,7 @@ private slots: void promise(); void futureFromPromise(); void addResult(); + void addResultWithBracedInitializer(); void addResultOutOfOrder(); #ifndef QT_NO_EXCEPTIONS void setException(); @@ -64,6 +42,10 @@ private slots: void cancelWhenDestroyed(); #endif void cancelWhenReassigned(); + void cancelWhenDestroyedWithoutStarting(); + void cancelWhenDestroyedRunsContinuations(); + void cancelWhenDestroyedWithFailureHandler(); // QTBUG-114606 + void continuationsRunWhenFinished(); void finishWhenSwapped(); void cancelWhenMoved(); void waitUntilResumed(); @@ -77,22 +59,21 @@ private slots: struct TrivialType { int field = 0; }; struct CopyOnlyType { - Q_DISABLE_MOVE(CopyOnlyType) - CopyOnlyType() = default; + constexpr CopyOnlyType(int field = 0) noexcept : field(field) {} CopyOnlyType(const CopyOnlyType &) = default; CopyOnlyType& operator=(const CopyOnlyType &) = default; ~CopyOnlyType() = default; - int field = 0; + int field; }; struct MoveOnlyType { Q_DISABLE_COPY(MoveOnlyType) - MoveOnlyType() = default; + constexpr MoveOnlyType(int field = 0) noexcept : field(field) {} MoveOnlyType(MoveOnlyType &&) = default; MoveOnlyType& operator=(MoveOnlyType &&) = default; ~MoveOnlyType() = default; - int field = 0; + int field; }; bool operator==(const CopyOnlyType &a, const CopyOnlyType &b) { return a.field == b.field; } bool operator==(const MoveOnlyType &a, const MoveOnlyType &b) { return a.field == b.field; } @@ -192,6 +173,15 @@ void tst_QPromise::addResult() QCOMPARE(f.resultCount(), 3); QCOMPARE(f.resultAt(2), result); } + // add multiple results in one go: + { + QList results = {42, 4242, 424242}; + QVERIFY(promise.addResults(results)); + QCOMPARE(f.resultCount(), 6); + QCOMPARE(f.resultAt(3), 42); + QCOMPARE(f.resultAt(4), 4242); + QCOMPARE(f.resultAt(5), 424242); + } // add as lvalue at position and overwrite { int result = -1; @@ -209,6 +199,28 @@ void tst_QPromise::addResult() } } +void tst_QPromise::addResultWithBracedInitializer() // QTBUG-111826 +{ + struct MyClass + { + QString strValue; + int intValue = 0; +#ifndef __cpp_aggregate_paren_init // make emplacement work with MyClass + MyClass(QString s, int i) : strValue(std::move(s)), intValue(i) {} +#endif + }; + + { + QPromise<MyClass> myPromise; + myPromise.addResult({"bar", 1}); + } + + { + QPromise<MyClass> myPromise; + myPromise.emplaceResult("bar", 1); + } +} + void tst_QPromise::addResultOutOfOrder() { // Compare results available in QFuture to expected results @@ -280,6 +292,10 @@ void tst_QPromise::setException() std::make_exception_ptr(TestException())); RUN_TEST_FUNC(testExceptionCaught, QPromise<int>(), std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(testExceptionCaught, QPromise<CopyOnlyType>(), + std::make_exception_ptr(TestException())); + RUN_TEST_FUNC(testExceptionCaught, QPromise<MoveOnlyType>(), + std::make_exception_ptr(TestException())); } #endif @@ -293,6 +309,8 @@ void tst_QPromise::cancel() testCancel(QPromise<void>()); testCancel(QPromise<int>()); + testCancel(QPromise<CopyOnlyType>()); + testCancel(QPromise<MoveOnlyType>()); } void tst_QPromise::progress() @@ -320,13 +338,13 @@ void tst_QPromise::progress() RUN_TEST_FUNC(testProgress, QPromise<void>()); RUN_TEST_FUNC(testProgress, QPromise<int>()); + RUN_TEST_FUNC(testProgress, QPromise<CopyOnlyType>()); + RUN_TEST_FUNC(testProgress, QPromise<MoveOnlyType>()); } void tst_QPromise::addInThread() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) const auto testAddResult = [] (auto promise, const auto &result) { promise.start(); auto f = promise.future(); @@ -347,9 +365,7 @@ void tst_QPromise::addInThread() void tst_QPromise::addInThreadMoveOnlyObject() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<MoveOnlyType> promise; promise.start(); auto f = promise.future(); @@ -366,9 +382,7 @@ void tst_QPromise::addInThreadMoveOnlyObject() void tst_QPromise::reportFromMultipleThreads() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> promise; auto f = promise.future(); promise.start(); @@ -392,9 +406,7 @@ void tst_QPromise::reportFromMultipleThreads() void tst_QPromise::reportFromMultipleThreadsByMovedPromise() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> initialPromise; auto f = initialPromise.future(); { @@ -426,9 +438,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise() void tst_QPromise::doNotCancelWhenFinished() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) const auto testFinishedPromise = [] (auto promise) { auto f = promise.future(); promise.start(); @@ -445,15 +455,15 @@ void tst_QPromise::doNotCancelWhenFinished() RUN_TEST_FUNC(testFinishedPromise, QPromise<void>()); RUN_TEST_FUNC(testFinishedPromise, QPromise<int>()); RUN_TEST_FUNC(testFinishedPromise, QPromise<QString>()); + RUN_TEST_FUNC(testFinishedPromise, QPromise<CopyOnlyType>()); + RUN_TEST_FUNC(testFinishedPromise, QPromise<MoveOnlyType>()); #endif } #ifndef QT_NO_EXCEPTIONS void tst_QPromise::cancelWhenDestroyed() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> initialPromise; auto f = initialPromise.future(); @@ -487,15 +497,13 @@ void tst_QPromise::cancelWhenDestroyed() void tst_QPromise::cancelWhenReassigned() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> promise; auto f = promise.future(); promise.start(); ThreadWrapper thr([p = std::move(promise)] () mutable { - QThread::msleep(100); + QThread::sleep(100ms); p = QPromise<int>(); // assign new promise, old must be correctly destroyed }); @@ -506,11 +514,123 @@ void tst_QPromise::cancelWhenReassigned() #endif } -void tst_QPromise::finishWhenSwapped() +template <typename T> +static inline void testCancelWhenDestroyedWithoutStarting() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); + QFuture<T> future; + { + QPromise<T> promise; + future = promise.future(); + } + future.waitForFinished(); + QVERIFY(!future.isStarted()); + QVERIFY(future.isCanceled()); + QVERIFY(future.isFinished()); +} + +void tst_QPromise::cancelWhenDestroyedWithoutStarting() +{ + testCancelWhenDestroyedWithoutStarting<void>(); + testCancelWhenDestroyedWithoutStarting<int>(); + testCancelWhenDestroyedWithoutStarting<CopyOnlyType>(); + testCancelWhenDestroyedWithoutStarting<MoveOnlyType>(); +} + +template <typename T> +static inline void testCancelWhenDestroyedRunsContinuations() +{ + QFuture<T> future; + bool onCanceledCalled = false; + bool thenCalled = false; + { + QPromise<T> promise; + future = promise.future(); + future.then([&] (auto&&) { + thenCalled = true; + }).onCanceled([&] () { + onCanceledCalled = true; + }); + } + QVERIFY(future.isFinished()); + QVERIFY(!thenCalled); + QVERIFY(onCanceledCalled); +} + +void tst_QPromise::cancelWhenDestroyedRunsContinuations() +{ + testCancelWhenDestroyedRunsContinuations<void>(); + testCancelWhenDestroyedRunsContinuations<int>(); + testCancelWhenDestroyedRunsContinuations<CopyOnlyType>(); + testCancelWhenDestroyedRunsContinuations<MoveOnlyType>(); +} + +template <typename T> +static inline void testCancelWhenDestroyedWithFailureHandler() +{ + QFuture<T> future; + bool onFailedCalled = false; + bool thenCalled = false; + { + QPromise<T> promise; + future = promise.future(); + future + .onFailed([&] () { + onFailedCalled = true; + if constexpr (!std::is_same_v<void, T>) + return T{}; + }) + .then([&] (auto&&) { + thenCalled = true; + }); + } + QVERIFY(future.isFinished()); + QVERIFY(!onFailedCalled); + QVERIFY(!thenCalled); +} + +void tst_QPromise::cancelWhenDestroyedWithFailureHandler() +{ +#ifndef QT_NO_EXCEPTIONS + testCancelWhenDestroyedWithFailureHandler<void>(); + testCancelWhenDestroyedWithFailureHandler<int>(); + testCancelWhenDestroyedWithFailureHandler<CopyOnlyType>(); + testCancelWhenDestroyedWithFailureHandler<MoveOnlyType>(); #else + QSKIP("Exceptions are disabled, skipping the test"); +#endif +} + +template <typename T> +static inline void testContinuationsRunWhenFinished() +{ + QPromise<T> promise; + QFuture<T> future = promise.future(); + + bool thenCalled = false; + future.then([&] (auto&&) { + thenCalled = true; + }); + + promise.start(); + if constexpr (!std::is_void_v<T>) { + promise.addResult(T{}); + } + promise.finish(); + + QVERIFY(thenCalled); +} + +void tst_QPromise::continuationsRunWhenFinished() +{ + testContinuationsRunWhenFinished<void>(); + testContinuationsRunWhenFinished<int>(); + testContinuationsRunWhenFinished<CopyOnlyType>(); + testContinuationsRunWhenFinished<MoveOnlyType>(); +} + +void tst_QPromise::finishWhenSwapped() +{ +#if QT_CONFIG(cxx11_future) QPromise<int> promise1; auto f1 = promise1.future(); promise1.start(); @@ -520,7 +640,7 @@ void tst_QPromise::finishWhenSwapped() promise2.start(); ThreadWrapper thr([&promise1, &promise2] () mutable { - QThread::msleep(100); + QThread::sleep(100ms); promise1.addResult(0); promise2.addResult(1); swap(promise1, promise2); // ADL must resolve this @@ -548,22 +668,21 @@ void tst_QPromise::finishWhenSwapped() #endif } -void tst_QPromise::cancelWhenMoved() +template <typename T> +void testCancelWhenMoved() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else - QPromise<int> promise1; +#if QT_CONFIG(cxx11_future) + QPromise<T> promise1; auto f1 = promise1.future(); promise1.start(); - QPromise<int> promise2; + QPromise<T> promise2; auto f2 = promise2.future(); promise2.start(); // Move promises to local scope to test cancellation behavior ThreadWrapper thr([p1 = std::move(promise1), p2 = std::move(promise2)] () mutable { - QThread::msleep(100); + QThread::sleep(100ms); p1 = std::move(p2); p1.finish(); // this finish is for future #2 }); @@ -581,6 +700,14 @@ void tst_QPromise::cancelWhenMoved() #endif } +void tst_QPromise::cancelWhenMoved() +{ + testCancelWhenMoved<void>(); + testCancelWhenMoved<int>(); + testCancelWhenMoved<CopyOnlyType>(); + testCancelWhenMoved<MoveOnlyType>(); +} + void tst_QPromise::waitUntilResumed() { #if !QT_CONFIG(cxx11_future) @@ -599,7 +726,7 @@ void tst_QPromise::waitUntilResumed() while (!f.isSuspended()) { // busy wait until worker thread suspends QCOMPARE(f.isFinished(), false); // exit condition in case of failure - QThread::msleep(50); // allow another thread to actually carry on + QThread::sleep(50ms); // allow another thread to actually carry on } f.resume(); @@ -612,9 +739,7 @@ void tst_QPromise::waitUntilResumed() void tst_QPromise::waitUntilCanceled() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> promise; promise.start(); auto f = promise.future(); @@ -628,7 +753,7 @@ void tst_QPromise::waitUntilCanceled() while (!f.isSuspended()) { // busy wait until worker thread suspends QCOMPARE(f.isFinished(), false); // exit condition in case of failure - QThread::msleep(50); // allow another thread to actually carry on + QThread::sleep(50ms); // allow another thread to actually carry on } f.cancel(); |