diff options
Diffstat (limited to 'tests/auto/corelib/thread/qpromise/tst_qpromise.cpp')
-rw-r--r-- | tests/auto/corelib/thread/qpromise/tst_qpromise.cpp | 265 |
1 files changed, 195 insertions, 70 deletions
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(); |