diff options
Diffstat (limited to 'tests/auto/corelib/thread/qpromise')
5 files changed, 300 insertions, 174 deletions
diff --git a/tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt b/tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt deleted file mode 100644 index dc25a38fea..0000000000 --- a/tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Generated from qpromise.pro. - -##################################################################### -## tst_qpromise Test: -##################################################################### - -qt_add_test(tst_qpromise - SOURCES - tst_qpromise.cpp - PUBLIC_LIBRARIES - Qt::CorePrivate -) diff --git a/tests/auto/corelib/thread/qpromise/CMakeLists.txt b/tests/auto/corelib/thread/qpromise/CMakeLists.txt index dc25a38fea..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: ##################################################################### -qt_add_test(tst_qpromise +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/qpromise.pro b/tests/auto/corelib/thread/qpromise/qpromise.pro deleted file mode 100644 index 9ea1aaacdf..0000000000 --- a/tests/auto/corelib/thread/qpromise/qpromise.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpromise -QT = core core-private testlib -SOURCES = tst_qpromise.cpp diff --git a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp index 571078feba..2cef0dca95 100644 --- a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp @@ -1,64 +1,17 @@ -/**************************************************************************** -** -** 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 // snippet license. #include <QCoreApplication> -#include <QtTest/QtTest> +#include <QTest> #include <qfuture.h> #include <qfuturewatcher.h> #include <qpromise.h> -#include <qpointer.h> +#include <qscopedpointer.h> #include <qsharedpointer.h> class snippet_QPromise @@ -71,49 +24,49 @@ public: void snippet_QPromise::basicExample() { +#if QT_CONFIG(cxx11_future) //! [basic] QPromise<int> promise; QFuture<int> future = promise.future(); - // Note: calling reportStarted() prior to thread creation to enforce order - // of calls: first promise.reportStarted() then future.waitForFinished() - promise.reportStarted(); // notifies QFuture that the computation is started - - QPointer<QThread> thread(QThread::create([] (QPromise<int> promise) { + QScopedPointer<QThread> thread(QThread::create([] (QPromise<int> promise) { + promise.start(); // notifies QFuture that the computation is started promise.addResult(42); - promise.reportFinished(); // notifies QFuture that the computation is finished + promise.finish(); // notifies QFuture that the computation is finished }, std::move(promise))); thread->start(); - future.waitForFinished(); // blocks until QPromise::reportFinished is called + future.waitForFinished(); // blocks until QPromise::finish is called future.result(); // returns 42 //! [basic] QCOMPARE(future.result(), 42); thread->wait(); +#endif } void snippet_QPromise::multithreadExample() { +#if QT_CONFIG(cxx11_future) //! [multithread_init] QSharedPointer<QPromise<int>> sharedPromise(new QPromise<int>()); QFuture<int> future = sharedPromise->future(); // ... -//! [multithread_init] - sharedPromise->reportStarted(); + sharedPromise->start(); +//! [multithread_init] //! [multithread_main] // here, QPromise is shared between threads via a smart pointer - QPointer<QThread> threads[] = { - QPointer<QThread>(QThread::create([] (auto sharedPromise) { + QScopedPointer<QThread> threads[] = { + QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) { sharedPromise->addResult(0, 0); // adds value 0 by index 0 }, sharedPromise)), - QPointer<QThread>(QThread::create([] (auto sharedPromise) { + QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) { sharedPromise->addResult(-1, 1); // adds value -1 by index 1 }, sharedPromise)), - QPointer<QThread>(QThread::create([] (auto sharedPromise) { + QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) { sharedPromise->addResult(-2, 2); // adds value -2 by index 2 }, sharedPromise)), // ... @@ -135,26 +88,30 @@ void snippet_QPromise::multithreadExample() for (auto& t : threads) t->wait(); - sharedPromise->reportFinished(); +//! [multithread_cleanup] + sharedPromise->finish(); +//! [multithread_cleanup] +#endif } void snippet_QPromise::suspendExample() { +#if QT_CONFIG(cxx11_future) //! [suspend_start] // Create promise and future QPromise<int> promise; QFuture<int> future = promise.future(); - promise.reportStarted(); + promise.start(); // Start a computation thread that supports suspension and cancellation - QPointer<QThread> thread(QThread::create([] (QPromise<int> promise) { + QScopedPointer<QThread> thread(QThread::create([] (QPromise<int> promise) { for (int i = 0; i < 100; ++i) { promise.addResult(i); promise.suspendIfRequested(); // support suspension if (promise.isCanceled()) // support cancellation break; } - promise.reportFinished(); + promise.finish(); }, std::move(promise))); thread->start(); //! [suspend_start] @@ -197,4 +154,5 @@ void snippet_QPromise::suspendExample() QList<int> expected(100); std::iota(expected.begin(), expected.end(), 0); QCOMPARE(future.results(), expected); +#endif } diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp index 2b8853ccd7..2c12e41c93 100644 --- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** 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> #define QPROMISE_TEST -#include <QtTest/QtTest> +#include <QTest> #include <qfuture.h> #include <qfuturewatcher.h> #include <qpromise.h> @@ -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; + Q_DISABLE_COPY(MoveOnlyType) + 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; } @@ -105,6 +86,7 @@ do { \ QFAIL("Test case " #test "(" #__VA_ARGS__ ") failed"); \ } while (false) +#if QT_CONFIG(cxx11_future) // std::thread-like wrapper that ensures that the thread is joined at the end of // a scope to prevent potential std::terminate struct ThreadWrapper @@ -121,13 +103,14 @@ struct ThreadWrapper t->wait(); } }; +#endif void tst_QPromise::promise() { const auto testCanCreatePromise = [] (auto promise) { - promise.reportStarted(); + promise.start(); promise.suspendIfRequested(); // should not block on its own - promise.reportFinished(); + promise.finish(); }; RUN_TEST_FUNC(testCanCreatePromise, QPromise<void>()); @@ -144,11 +127,11 @@ void tst_QPromise::futureFromPromise() auto future = promise.future(); QVERIFY(!future.isValid()); - promise.reportStarted(); + promise.start(); QCOMPARE(future.isStarted(), true); QVERIFY(future.isValid()); - promise.reportFinished(); + promise.finish(); QCOMPARE(future.isFinished(), true); QVERIFY(future.isValid()); @@ -169,34 +152,72 @@ void tst_QPromise::addResult() auto f = promise.future(); // add as lvalue + int resultAt0 = 456; { - int result = 456; - promise.addResult(result); + QVERIFY(promise.addResult(resultAt0)); QCOMPARE(f.resultCount(), 1); - QCOMPARE(f.result(), result); - QCOMPARE(f.resultAt(0), result); + QCOMPARE(f.result(), resultAt0); + QCOMPARE(f.resultAt(0), resultAt0); } // add as rvalue { int result = 789; - promise.addResult(789); + QVERIFY(promise.addResult(789)); QCOMPARE(f.resultCount(), 2); QCOMPARE(f.resultAt(1), result); } // add at position { int result = 56238; - promise.addResult(result, 2); + QVERIFY(promise.addResult(result, 2)); QCOMPARE(f.resultCount(), 3); QCOMPARE(f.resultAt(2), result); } - // add at position and overwrite + // 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; const auto originalCount = f.resultCount(); - promise.addResult(result, 0); + QVERIFY(!promise.addResult(result, 0)); QCOMPARE(f.resultCount(), originalCount); - QCOMPARE(f.resultAt(0), result); + QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work + } + // add as rvalue at position and overwrite + { + const auto originalCount = f.resultCount(); + QVERIFY(!promise.addResult(-1, 0)); + QCOMPARE(f.resultCount(), originalCount); + QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work + } +} + +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); } } @@ -216,9 +237,9 @@ void tst_QPromise::addResultOutOfOrder() { QPromise<int> promise; auto f = promise.future(); - promise.addResult(456, 1); + QVERIFY(promise.addResult(456, 1)); QCOMPARE(f.resultCount(), 0); - promise.addResult(123, 0); + QVERIFY(promise.addResult(123, 0)); QList<int> expected({123, 456}); RUN_TEST_FUNC(compareResults, f, expected); @@ -229,16 +250,16 @@ void tst_QPromise::addResultOutOfOrder() { QPromise<int> promise; auto f = promise.future(); - promise.addResult(0, 0); - promise.addResult(1, 1); - promise.addResult(3, 3); // intentional gap here + QVERIFY(promise.addResult(0, 0)); + QVERIFY(promise.addResult(1, 1)); + QVERIFY(promise.addResult(3, 3)); // intentional gap here QList<int> expectedWhenGapExists({0, 1}); RUN_TEST_FUNC(compareResults, f, expectedWhenGapExists); QCOMPARE(f.resultAt(3), 3); QList<int> expectedWhenNoGap({0, 1, 2, 3}); - promise.addResult(2, 2); // fill a gap with a value + QVERIFY(promise.addResult(2, 2)); // fill a gap with a value RUN_TEST_FUNC(compareResults, f, expectedWhenNoGap); QCOMPARE(f.results(), expectedWhenNoGap); } @@ -250,9 +271,9 @@ void tst_QPromise::setException() struct TestException {}; // custom exception class const auto testExceptionCaught = [] (auto promise, const auto& exception) { auto f = promise.future(); - promise.reportStarted(); + promise.start(); promise.setException(exception); - promise.reportFinished(); + promise.finish(); bool caught = false; try { @@ -271,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 @@ -284,6 +309,8 @@ void tst_QPromise::cancel() testCancel(QPromise<void>()); testCancel(QPromise<int>()); + testCancel(QPromise<CopyOnlyType>()); + testCancel(QPromise<MoveOnlyType>()); } void tst_QPromise::progress() @@ -301,7 +328,6 @@ void tst_QPromise::progress() promise.setProgressValue(0); // decrement QCOMPARE(f.progressValue(), 1); promise.setProgressValue(10); // out of range - QEXPECT_FAIL("", "Out of range value is set - QTBUG-84729", Continue); QCOMPARE(f.progressValue(), 1); promise.setProgressRange(0, 100); @@ -312,12 +338,15 @@ 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) const auto testAddResult = [] (auto promise, const auto &result) { - promise.reportStarted(); + promise.start(); auto f = promise.future(); // move construct QPromise ThreadWrapper thr([p = std::move(promise), &result] () mutable { @@ -331,12 +360,14 @@ void tst_QPromise::addInThread() RUN_TEST_FUNC(testAddResult, QPromise<int>(), 42); RUN_TEST_FUNC(testAddResult, QPromise<QString>(), u8"42"); RUN_TEST_FUNC(testAddResult, QPromise<CopyOnlyType>(), CopyOnlyType{99}); +#endif } void tst_QPromise::addInThreadMoveOnlyObject() { +#if QT_CONFIG(cxx11_future) QPromise<MoveOnlyType> promise; - promise.reportStarted(); + promise.start(); auto f = promise.future(); ThreadWrapper thr([p = std::move(promise)] () mutable { @@ -346,13 +377,15 @@ void tst_QPromise::addInThreadMoveOnlyObject() // Iterators wait for result first for (auto& result : f) QCOMPARE(result, MoveOnlyType{-11}); +#endif } void tst_QPromise::reportFromMultipleThreads() { +#if QT_CONFIG(cxx11_future) QPromise<int> promise; auto f = promise.future(); - promise.reportStarted(); + promise.start(); ThreadWrapper threads[] = { ThreadWrapper([&promise] () mutable { promise.addResult(42); }), @@ -361,17 +394,19 @@ void tst_QPromise::reportFromMultipleThreads() }; for (auto& t : threads) t.join(); - promise.reportFinished(); + promise.finish(); QList<int> expected = {42, 43, 44}; for (auto actual : f.results()) { QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end()); expected.removeOne(actual); } +#endif } void tst_QPromise::reportFromMultipleThreadsByMovedPromise() { +#if QT_CONFIG(cxx11_future) QPromise<int> initialPromise; auto f = initialPromise.future(); { @@ -379,7 +414,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise() // move-constructed) must be able to set results, QFuture must still // hold correct references to results. auto promise = std::move(initialPromise); - promise.reportStarted(); + promise.start(); ThreadWrapper threads[] = { ThreadWrapper([&promise] () mutable { promise.addResult(42); }), ThreadWrapper([&promise] () mutable { promise.addResult(43); }), @@ -387,7 +422,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise() }; for (auto& t : threads) t.join(); - promise.reportFinished(); + promise.finish(); } QCOMPARE(f.isFinished(), true); @@ -398,16 +433,18 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise() QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end()); expected.removeOne(actual); } +#endif } void tst_QPromise::doNotCancelWhenFinished() { +#if QT_CONFIG(cxx11_future) const auto testFinishedPromise = [] (auto promise) { auto f = promise.future(); - promise.reportStarted(); + promise.start(); // Finish QPromise inside thread, destructor must not call cancel() - ThreadWrapper([p = std::move(promise)] () mutable { p.reportFinished(); }).join(); + ThreadWrapper([p = std::move(promise)] () mutable { p.finish(); }).join(); f.waitForFinished(); @@ -418,18 +455,22 @@ 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) QPromise<int> initialPromise; auto f = initialPromise.future(); try { // Move QPromise to local scope. On destruction, it must call cancel(). auto promise = std::move(initialPromise); - promise.reportStarted(); + promise.start(); ThreadWrapper threads[] = { ThreadWrapper([&promise] () mutable { promise.addResult(42); }), ThreadWrapper([&promise] () mutable { promise.addResult(43); }), @@ -437,8 +478,8 @@ void tst_QPromise::cancelWhenDestroyed() }; for (auto& t : threads) t.join(); - throw "Throw in the middle, we lose our promise here, reportFinished() not called!"; - promise.reportFinished(); + throw "Throw in the middle, we lose our promise here, finish() not called!"; + promise.finish(); } catch (...) {} QCOMPARE(f.isFinished(), true); @@ -450,17 +491,19 @@ void tst_QPromise::cancelWhenDestroyed() QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end()); expected.removeOne(actual); } +#endif } #endif void tst_QPromise::cancelWhenReassigned() { +#if QT_CONFIG(cxx11_future) QPromise<int> promise; auto f = promise.future(); - promise.reportStarted(); + 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 }); @@ -468,27 +511,143 @@ void tst_QPromise::cancelWhenReassigned() QCOMPARE(f.isFinished(), true); QCOMPARE(f.isCanceled(), true); +#endif +} + +template <typename T> +static inline void testCancelWhenDestroyedWithoutStarting() +{ + 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.reportStarted(); + promise1.start(); QPromise<int> promise2; auto f2 = promise2.future(); - promise2.reportStarted(); + 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 promise1.addResult(2); promise2.addResult(3); - promise1.reportFinished(); // this finish is for future #2 - promise2.reportFinished(); // this finish is for future #1 + promise1.finish(); // this finish is for future #2 + promise2.finish(); // this finish is for future #1 }); f1.waitForFinished(); @@ -506,23 +665,26 @@ void tst_QPromise::finishWhenSwapped() QCOMPARE(f2.resultAt(0), 1); QCOMPARE(f2.resultAt(1), 2); +#endif } -void tst_QPromise::cancelWhenMoved() +template <typename T> +void testCancelWhenMoved() { - QPromise<int> promise1; +#if QT_CONFIG(cxx11_future) + QPromise<T> promise1; auto f1 = promise1.future(); - promise1.reportStarted(); + promise1.start(); - QPromise<int> promise2; + QPromise<T> promise2; auto f2 = promise2.future(); - promise2.reportStarted(); + 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.reportFinished(); // this finish is for future #2 + p1.finish(); // this finish is for future #2 }); f1.waitForFinished(); @@ -535,24 +697,36 @@ void tst_QPromise::cancelWhenMoved() // Future #2 is explicitly finished inside thread QCOMPARE(f2.isFinished(), true); QCOMPARE(f2.isCanceled(), false); +#endif +} + +void tst_QPromise::cancelWhenMoved() +{ + testCancelWhenMoved<void>(); + testCancelWhenMoved<int>(); + testCancelWhenMoved<CopyOnlyType>(); + testCancelWhenMoved<MoveOnlyType>(); } void tst_QPromise::waitUntilResumed() { +#if !QT_CONFIG(cxx11_future) + QSKIP("This test requires QThread::create"); +#else QPromise<int> promise; - promise.reportStarted(); + promise.start(); auto f = promise.future(); f.suspend(); ThreadWrapper thr([p = std::move(promise)] () mutable { p.suspendIfRequested(); p.addResult(42); // result added after suspend - p.reportFinished(); + p.finish(); }); 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(); @@ -560,30 +734,33 @@ void tst_QPromise::waitUntilResumed() QCOMPARE(f.resultCount(), 1); QCOMPARE(f.result(), 42); +#endif } void tst_QPromise::waitUntilCanceled() { +#if QT_CONFIG(cxx11_future) QPromise<int> promise; - promise.reportStarted(); + promise.start(); auto f = promise.future(); f.suspend(); ThreadWrapper thr([p = std::move(promise)] () mutable { p.suspendIfRequested(); p.addResult(42); // result not added due to QFuture::cancel() - p.reportFinished(); + p.finish(); }); 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(); f.waitForFinished(); QCOMPARE(f.resultCount(), 0); +#endif } // Below is a quick and dirty hack to make snippets a part of a test suite |