diff options
Diffstat (limited to 'tests/auto/concurrent')
41 files changed, 3969 insertions, 916 deletions
diff --git a/tests/auto/concurrent/CMakeLists.txt b/tests/auto/concurrent/CMakeLists.txt index 2ef8cfd128..0088ebfcf6 100644 --- a/tests/auto/concurrent/CMakeLists.txt +++ b/tests/auto/concurrent/CMakeLists.txt @@ -1,9 +1,13 @@ -# Generated from concurrent.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qtconcurrentfilter) add_subdirectory(qtconcurrentiteratekernel) +add_subdirectory(qtconcurrentfiltermapgenerated) add_subdirectory(qtconcurrentmap) add_subdirectory(qtconcurrentmedian) -add_subdirectory(qtconcurrentrun) +if(NOT INTEGRITY) + add_subdirectory(qtconcurrentrun) + add_subdirectory(qtconcurrenttask) +endif() add_subdirectory(qtconcurrentthreadengine) -add_subdirectory(qtconcurrenttask) diff --git a/tests/auto/concurrent/concurrent.pro b/tests/auto/concurrent/concurrent.pro deleted file mode 100644 index ad3231ff90..0000000000 --- a/tests/auto/concurrent/concurrent.pro +++ /dev/null @@ -1,10 +0,0 @@ -TEMPLATE=subdirs -SUBDIRS=\ - qtconcurrentfilter \ - qtconcurrentiteratekernel \ - qtconcurrentmap \ - qtconcurrentmedian \ - qtconcurrentrun \ - qtconcurrentthreadengine \ - qtconcurrenttask - diff --git a/tests/auto/concurrent/qtconcurrentfilter/.prev_CMakeLists.txt b/tests/auto/concurrent/qtconcurrentfilter/.prev_CMakeLists.txt deleted file mode 100644 index be8c936c48..0000000000 --- a/tests/auto/concurrent/qtconcurrentfilter/.prev_CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Generated from qtconcurrentfilter.pro. - -##################################################################### -## tst_qtconcurrentfilter Test: -##################################################################### - -add_qt_test(tst_qtconcurrentfilter - SOURCES - tst_qtconcurrentfilter.cpp - PUBLIC_LIBRARIES - Qt::Concurrent -) diff --git a/tests/auto/concurrent/qtconcurrentfilter/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentfilter/CMakeLists.txt index 7868906178..3c00393d39 100644 --- a/tests/auto/concurrent/qtconcurrentfilter/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrentfilter/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qtconcurrentfilter.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrentfilter Test: ##################################################################### -qt_add_test(tst_qtconcurrentfilter +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentfilter LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentfilter SOURCES tst_qtconcurrentfilter.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent ) diff --git a/tests/auto/concurrent/qtconcurrentfilter/qtconcurrentfilter.pro b/tests/auto/concurrent/qtconcurrentfilter/qtconcurrentfilter.pro deleted file mode 100644 index f2e3e0c8e1..0000000000 --- a/tests/auto/concurrent/qtconcurrentfilter/qtconcurrentfilter.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrentfilter -QT = core testlib concurrent -SOURCES = tst_qtconcurrentfilter.cpp diff --git a/tests/auto/concurrent/qtconcurrentfilter/tst_qtconcurrentfilter.cpp b/tests/auto/concurrent/qtconcurrentfilter/tst_qtconcurrentfilter.cpp index 945f286c9d..e19a596d5d 100644 --- a/tests/auto/concurrent/qtconcurrentfilter/tst_qtconcurrentfilter.cpp +++ b/tests/auto/concurrent/qtconcurrentfilter/tst_qtconcurrentfilter.cpp @@ -1,36 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtconcurrentfilter.h> #include <QCoreApplication> #include <QList> -#include <QtTest/QtTest> +#include <QTest> +#include <QSet> -#include "../qtconcurrentmap/functions.h" +#include "../testhelper_functions.h" class tst_QtConcurrentFilter : public QObject { @@ -39,14 +15,19 @@ class tst_QtConcurrentFilter : public QObject private slots: void filter(); void filterThreadPool(); + void filterWithMoveOnlyCallable(); void filtered(); void filteredThreadPool(); + void filteredWithMoveOnlyCallable(); void filteredReduced(); void filteredReducedThreadPool(); + void filteredReducedWithMoveOnlyCallables(); void filteredReducedDifferentType(); void filteredReducedInitialValue(); void filteredReducedInitialValueThreadPool(); + void filteredReducedInitialValueWithMoveOnlyCallables(); void filteredReducedDifferentTypeInitialValue(); + void filteredReduceOptionConvertableToResultType(); void resultAt(); void incrementalResults(); void noDetach(); @@ -98,6 +79,19 @@ void tst_QtConcurrentFilter::filter() CHECK_FAIL("member"); testFilter(intList, intListEven, lambdaIsEven); CHECK_FAIL("lambda"); + + // non-template sequences + { + + NonTemplateSequence list({ 1, 2, 3, 4 }); + QtConcurrent::filter(list, keepEvenNumbers).waitForFinished(); + QCOMPARE(list, NonTemplateSequence({ 2, 4 })); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + QtConcurrent::blockingFilter(list, keepEvenNumbers); + QCOMPARE(list, NonTemplateSequence({ 2, 4 })); + } } static QSemaphore semaphore(1); @@ -175,6 +169,47 @@ void tst_QtConcurrentFilter::filterThreadPool() CHECK_FAIL("function"); testFilterThreadPool(&pool, intList, intListEven, lambdaIsOdd); CHECK_FAIL("lambda"); + + // non-template sequences + { + + NonTemplateSequence list({ 1, 2, 3, 4 }); + QtConcurrent::filter(list, keepEvenIntegers).waitForFinished(); + QCOMPARE(list, NonTemplateSequence({ 2, 4 })); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + QtConcurrent::blockingFilter(list, keepEvenIntegers); + QCOMPARE(list, NonTemplateSequence({ 2, 4 })); + } +} + +void tst_QtConcurrentFilter::filterWithMoveOnlyCallable() +{ + const QList<int> intListEven { 2, 4 }; + { + QList<int> intList { 1, 2, 3, 4 }; + QtConcurrent::filter(intList, KeepEvenIntegersMoveOnly()).waitForFinished(); + QCOMPARE(intList, intListEven); + } + + { + QList<int> intList { 1, 2, 3, 4 }; + QtConcurrent::blockingFilter(intList, KeepEvenIntegersMoveOnly()); + QCOMPARE(intList, intListEven); + } + + QThreadPool pool; + { + QList<int> intList { 1, 2, 3, 4 }; + QtConcurrent::filter(&pool, intList, KeepEvenIntegersMoveOnly()).waitForFinished(); + QCOMPARE(intList, intListEven); + } + { + QList<int> intList { 1, 2, 3, 4 }; + QtConcurrent::blockingFilter(&pool, intList, KeepEvenIntegersMoveOnly()); + QCOMPARE(intList, intListEven); + } } template <typename SourceObject, @@ -221,6 +256,40 @@ void tst_QtConcurrentFilter::filtered() CHECK_FAIL("member"); testFiltered(intList, intListEven, lambdaIsEven); CHECK_FAIL("lambda"); + + // non-template sequences + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto future = QtConcurrent::filtered(list, keepEvenIntegers); + QCOMPARE(future.results(), QList({ 2, 4 })); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto result = QtConcurrent::blockingFiltered(list, keepEvenIntegers); + QCOMPARE(result, NonTemplateSequence({ 2, 4 })); + } + + { + // rvalue sequences + auto future = QtConcurrent::filtered(std::vector { 1, 2, 3, 4 }, keepEvenIntegers); + QCOMPARE(future.results(), QList<int>({ 2, 4 })); + + auto result = QtConcurrent::blockingFiltered(std::vector { 1, 2, 3, 4 }, keepEvenIntegers); + QCOMPARE(result, std::vector<int>({ 2, 4 })); + } + + { + // move only types sequences + auto future = QtConcurrent::filtered(MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers); + QCOMPARE(future.results(), QList<int>({ 2, 4 })); + +#if 0 + // does not work yet + auto result = QtConcurrent::blockingFiltered( + MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers); + QCOMPARE(result, std::vector<int>({ 2, 4 })); +#endif + } } template <typename SourceObject, @@ -274,6 +343,89 @@ void tst_QtConcurrentFilter::filteredThreadPool() CHECK_FAIL("function"); testFilteredThreadPool(&pool, intList, intListEven, lambdaIsOdd); CHECK_FAIL("lambda"); + + // non-template sequences + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto future = QtConcurrent::filtered(&pool, list, keepEvenIntegers); + QCOMPARE(future.results(), QList({ 2, 4 })); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto result = QtConcurrent::blockingFiltered(&pool, list, keepEvenIntegers); + QCOMPARE(result, NonTemplateSequence({ 2, 4 })); + } + + { + // rvalue sequences + auto future = QtConcurrent::filtered(&pool, std::vector { 1, 2, 3, 4 }, keepEvenIntegers); + QCOMPARE(future.results(), QList<int>({ 2, 4 })); + + auto result = + QtConcurrent::blockingFiltered(&pool, std::vector { 1, 2, 3, 4 }, keepEvenIntegers); + QCOMPARE(result, std::vector<int>({ 2, 4 })); + } + + { + // move-only sequences + auto future = QtConcurrent::filtered(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepEvenIntegers); + QCOMPARE(future.results(), QList<int>({ 2, 4 })); + +#if 0 + // does not work yet + auto result = + QtConcurrent::blockingFiltered( + &pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers); + QCOMPARE(result, std::vector<int>({ 2, 4 })); +#endif + } +} + +void tst_QtConcurrentFilter::filteredWithMoveOnlyCallable() +{ + const QList<int> intList { 1, 2, 3, 4 }; + const QList<int> intListEven { 2, 4 }; + { + const auto result = QtConcurrent::filtered(intList, KeepEvenIntegersMoveOnly()).results(); + QCOMPARE(result, intListEven); + } + { + const auto result = QtConcurrent::filtered( + intList.begin(), intList.end(), KeepEvenIntegersMoveOnly()).results(); + QCOMPARE(result, intListEven); + } + { + const auto result = QtConcurrent::blockingFiltered(intList, KeepEvenIntegersMoveOnly()); + QCOMPARE(result, intListEven); + } + { + const auto result = QtConcurrent::blockingFiltered<QList<int>>( + intList.begin(), intList.end(), KeepEvenIntegersMoveOnly()); + QCOMPARE(result, intListEven); + } + + QThreadPool pool; + { + const auto result = + QtConcurrent::filtered(&pool, intList, KeepEvenIntegersMoveOnly()).results(); + QCOMPARE(result, intListEven); + } + { + const auto result = QtConcurrent::filtered(&pool, intList.begin(), intList.end(), + KeepEvenIntegersMoveOnly()).results(); + QCOMPARE(result, intListEven); + } + { + const auto result = + QtConcurrent::blockingFiltered(&pool, intList, KeepEvenIntegersMoveOnly()); + QCOMPARE(result, intListEven); + } + { + const auto result = QtConcurrent::blockingFiltered<QList<int>>( + &pool, intList.begin(), intList.end(), KeepEvenIntegersMoveOnly()); + QCOMPARE(result, intListEven); + } } template <typename SourceObject, @@ -285,23 +437,47 @@ void testFilteredReduced(const QList<SourceObject> &sourceObjectList, FilterObject filterObject, ReduceObject reduceObject) { - const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( - sourceObjectList, filterObject, reduceObject); - QCOMPARE(result1, expectedResult); - - const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject); - QCOMPARE(result2, expectedResult); - - const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( - sourceObjectList, filterObject, reduceObject); - QCOMPARE(result3, expectedResult); + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( + sourceObjectList, filterObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( + sourceObjectList, filterObject, reduceObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject); + QCOMPARE(result4, expectedResult); + } - const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject); - QCOMPARE(result4, expectedResult); + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::filteredReduced( + sourceObjectList, filterObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::filteredReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced( + sourceObjectList, filterObject, reduceObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject); + QCOMPARE(result4, expectedResult); + } } template <typename SourceObject, @@ -315,12 +491,13 @@ void testFilteredReduced(const QList<SourceObject> &sourceObjectList, QtConcurrent::ReduceOptions options) { const ResultObject result1 = QtConcurrent::filteredReduced( - sourceObjectList, filterObject, reduceObject, options); + sourceObjectList, filterObject, reduceObject, options).result(); QCOMPARE(result1, expectedResult); - const ResultObject result2 = QtConcurrent::filteredReduced( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), filterObject, - reduceObject, options); + const ResultObject result2 = + QtConcurrent::filteredReduced(sourceObjectList.constBegin(), + sourceObjectList.constEnd(), + filterObject, reduceObject, options).result(); QCOMPARE(result2, expectedResult); const ResultObject result3 = QtConcurrent::blockingFilteredReduced( @@ -356,8 +533,9 @@ void tst_QtConcurrentFilter::filteredReduced() const int intSum = 6; // sum of even values const Number numberSum = 6; // sum of even values - void (QList<int>::*pushBackInt)(const int &) = &QList<int>::push_back; - void (QList<Number>::*pushBackNumber)(const Number &) = &QList<Number>::push_back; + void (QList<int>::*pushBackInt)(QList<int>::parameter_type) = &QList<int>::push_back; + void (QList<Number>::*pushBackNumber)(QList<Number>::parameter_type) = + &QList<Number>::push_back; auto lambdaIsEven = [](const int &x) { return (x & 1) == 0; @@ -409,6 +587,40 @@ void tst_QtConcurrentFilter::filteredReduced() CHECK_FAIL("lambda-member"); testFilteredReduced(intList, intSum, lambdaIsEven, lambdaIntSumReduce); CHECK_FAIL("lambda-lambda"); + + // non-template sequences + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto future = QtConcurrent::filteredReduced(list, keepEvenIntegers, intSumReduce); + QCOMPARE(future.result(), intSum); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto result = QtConcurrent::blockingFilteredReduced(list, keepEvenIntegers, intSumReduce); + QCOMPARE(result, intSum); + } + + { + // rvalue sequences + auto future = QtConcurrent::filteredReduced(std::vector { 1, 2, 3, 4 }, keepEvenIntegers, + intSumReduce); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced(std::vector { 1, 2, 3, 4 }, + keepEvenIntegers, intSumReduce); + QCOMPARE(result, intSum); + } + + { + // move only sequences + auto future = QtConcurrent::filteredReduced(MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepEvenIntegers, intSumReduce); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced(MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepEvenIntegers, intSumReduce); + QCOMPARE(result, intSum); + } } template <typename SourceObject, @@ -421,27 +633,57 @@ void testFilteredReducedThreadPool(QThreadPool *pool, FilterObject filterObject, ReduceObject reduceObject) { - const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( - pool, sourceObjectList, filterObject, reduceObject); - QCOMPARE(result1, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>( - pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject); - QCOMPARE(result2, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( - pool, sourceObjectList, filterObject, reduceObject); - QCOMPARE(result3, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( + pool, sourceObjectList, filterObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::filteredReduced<ResultObject>(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), filterObject, + reduceObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( + pool, sourceObjectList, filterObject, reduceObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } - const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( - pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject); - QCOMPARE(result4, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::filteredReduced( + pool, sourceObjectList, filterObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::filteredReduced(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), filterObject, + reduceObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced( + pool, sourceObjectList, filterObject, reduceObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } } void tst_QtConcurrentFilter::filteredReducedThreadPool() @@ -485,6 +727,96 @@ void tst_QtConcurrentFilter::filteredReducedThreadPool() CHECK_FAIL("lambda-function"); testFilteredReducedThreadPool(&pool, intList, intSum, lambdaIsOdd, lambdaSumReduce); CHECK_FAIL("lambda-lambda"); + + // non-template sequences + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto future = QtConcurrent::filteredReduced(&pool, list, keepOddIntegers, intSumReduce); + QCOMPARE(future.result(), intSum); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto result = + QtConcurrent::blockingFilteredReduced(&pool, list, keepOddIntegers, intSumReduce); + QCOMPARE(result, intSum); + } + + { + // rvalue sequences + auto future = QtConcurrent::filteredReduced(&pool, std::vector { 1, 2, 3, 4 }, + keepOddIntegers, intSumReduce); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced(&pool, std::vector { 1, 2, 3, 4 }, + keepOddIntegers, intSumReduce); + QCOMPARE(result, intSum); + } + + { + // move only sequences + auto future = QtConcurrent::filteredReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepOddIntegers, intSumReduce); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced( + &pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepOddIntegers, intSumReduce); + QCOMPARE(result, intSum); + } +} + +void tst_QtConcurrentFilter::filteredReducedWithMoveOnlyCallables() +{ + const QList<int> intList { 1, 2, 3, 4 }; + const QList<int> intListEven { 2, 4 }; + const auto sum = 6; + { + const auto result = + QtConcurrent::filteredReduced(intList, KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = + QtConcurrent::filteredReduced(intList.begin(), intList.end(), + KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + intList, KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + intList.begin(), intList.end(), KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } + + QThreadPool pool; + { + const auto result = + QtConcurrent::filteredReduced(&pool, intList, KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::filteredReduced( + &pool, intList.begin(), intList.end(), + KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + &pool, intList, KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + &pool, intList.begin(), intList.end(), KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } } void tst_QtConcurrentFilter::filteredReducedDifferentType() @@ -547,23 +879,47 @@ void testFilteredReducedInitialValue(const QList<SourceObject> &sourceObjectList ReduceObject reduceObject, InitialObject &&initialObject) { - const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( - sourceObjectList, filterObject, reduceObject, initialObject); - QCOMPARE(result1, expectedResult); - - const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject, initialObject); - QCOMPARE(result2, expectedResult); - - const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( - sourceObjectList, filterObject, reduceObject, initialObject); - QCOMPARE(result3, expectedResult); + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( + sourceObjectList, filterObject, reduceObject, initialObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( + sourceObjectList, filterObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + } - const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject, initialObject); - QCOMPARE(result4, expectedResult); + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::filteredReduced( + sourceObjectList, filterObject, reduceObject, initialObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::filteredReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced( + sourceObjectList, filterObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + } } template <typename SourceObject, @@ -579,12 +935,13 @@ void testFilteredReducedInitialValue(const QList<SourceObject> &sourceObjectList QtConcurrent::ReduceOptions options) { const ResultObject result1 = QtConcurrent::filteredReduced( - sourceObjectList, filterObject, reduceObject, initialObject, options); + sourceObjectList, filterObject, reduceObject, initialObject, options).result(); QCOMPARE(result1, expectedResult); - const ResultObject result2 = QtConcurrent::filteredReduced( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject, initialObject, options); + const ResultObject result2 = + QtConcurrent::filteredReduced(sourceObjectList.constBegin(), + sourceObjectList.constEnd(), filterObject, reduceObject, + initialObject, options).result(); QCOMPARE(result2, expectedResult); const ResultObject result3 = QtConcurrent::blockingFilteredReduced( @@ -612,8 +969,9 @@ void tst_QtConcurrentFilter::filteredReducedInitialValue() const int intSum = 16; // sum of even values and initial value const Number numberSum = 16; // sum of even values and initial value - void (QList<int>::*pushBackInt)(const int &) = &QList<int>::push_back; - void (QList<Number>::*pushBackNumber)(const Number &) = &QList<Number>::push_back; + void (QList<int>::*pushBackInt)(QList<int>::parameter_type) = &QList<int>::push_back; + void (QList<Number>::*pushBackNumber)(QList<Number>::parameter_type) = + &QList<Number>::push_back; auto lambdaIsEven = [](const int &x) { return (x & 1) == 0; @@ -680,6 +1038,42 @@ void tst_QtConcurrentFilter::filteredReducedInitialValue() testFilteredReducedInitialValue(intList, intSum, lambdaIsEven, lambdaIntSumReduce, intInitial); CHECK_FAIL("lambda-lambda"); + + // non-template sequences + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto future = + QtConcurrent::filteredReduced(list, keepEvenIntegers, intSumReduce, intInitial); + QCOMPARE(future.result(), intSum); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto result = QtConcurrent::blockingFilteredReduced(list, keepEvenIntegers, intSumReduce, + intInitial); + QCOMPARE(result, intSum); + } + + { + // rvalue sequences + auto future = QtConcurrent::filteredReduced(std::vector { 1, 2, 3, 4 }, keepEvenIntegers, + intSumReduce, intInitial); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced( + std::vector { 1, 2, 3, 4 }, keepEvenIntegers, intSumReduce, intInitial); + QCOMPARE(result, intSum); + } + + { + // move only sequences + auto future = QtConcurrent::filteredReduced(MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepEvenIntegers, intSumReduce, intInitial); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced( + MoveOnlyVector<int>({ 1, 2, 3, 4 }), keepEvenIntegers, intSumReduce, intInitial); + QCOMPARE(result, intSum); + } } template <typename SourceObject, @@ -694,27 +1088,57 @@ void testFilteredReducedInitialValueThreadPool(QThreadPool *pool, ReduceObject reduceObject, InitialObject &&initialObject) { - const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( - pool, sourceObjectList, filterObject, reduceObject, initialObject); - QCOMPARE(result1, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result2 = QtConcurrent::filteredReduced<ResultObject>( - pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject, initialObject); - QCOMPARE(result2, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( - pool, sourceObjectList, filterObject, reduceObject, initialObject); - QCOMPARE(result3, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::filteredReduced<ResultObject>( + pool, sourceObjectList, filterObject, reduceObject, initialObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::filteredReduced<ResultObject>(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), filterObject, + reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced<ResultObject>( + pool, sourceObjectList, filterObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } - const ResultObject result4 = QtConcurrent::blockingFilteredReduced<ResultObject>( - pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), - filterObject, reduceObject, initialObject); - QCOMPARE(result4, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::filteredReduced( + pool, sourceObjectList, filterObject, reduceObject, initialObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::filteredReduced(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), filterObject, + reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingFilteredReduced( + pool, sourceObjectList, filterObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingFilteredReduced( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + filterObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } } void tst_QtConcurrentFilter::filteredReducedInitialValueThreadPool() @@ -768,6 +1192,101 @@ void tst_QtConcurrentFilter::filteredReducedInitialValueThreadPool() testFilteredReducedInitialValueThreadPool(&pool, intList, intSum, lambdaIsOdd, lambdaSumReduce, intInitial); CHECK_FAIL("lambda-lambda"); + + // non-template sequences + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto future = + QtConcurrent::filteredReduced(list, keepOddIntegers, intSumReduce, intInitial); + QCOMPARE(future.result(), intSum); + } + { + NonTemplateSequence list({ 1, 2, 3, 4 }); + auto result = QtConcurrent::blockingFilteredReduced(list, keepOddIntegers, intSumReduce, + intInitial); + QCOMPARE(result, intSum); + } + + { + // rvalue sequences + auto future = QtConcurrent::filteredReduced(&pool, std::vector { 1, 2, 3, 4 }, + keepOddIntegers, intSumReduce, intInitial); + QCOMPARE(future.result(), intSum); + + auto result = QtConcurrent::blockingFilteredReduced( + &pool, std::vector { 1, 2, 3, 4 }, keepOddIntegers, intSumReduce, intInitial); + QCOMPARE(result, intSum); + } + + { + // move only sequences + auto future = QtConcurrent::filteredReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepOddIntegers, intSumReduce, intInitial); + QCOMPARE(future.result(), intSum); + + auto result = + QtConcurrent::blockingFilteredReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3, 4 }), + keepOddIntegers, intSumReduce, intInitial); + QCOMPARE(result, intSum); + } +} + +void tst_QtConcurrentFilter::filteredReducedInitialValueWithMoveOnlyCallables() +{ + const QList<int> intList { 1, 2, 3, 4 }; + const QList<int> intListEven { 2, 4 }; + const auto initial = 10; + const auto sum = 16; + { + const auto result = + QtConcurrent::filteredReduced(intList, KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly(), initial).result(); + QCOMPARE(result, sum); + } + { + const auto result = + QtConcurrent::filteredReduced(intList.begin(), intList.end(), + KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly(), initial).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + intList, KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly(), initial); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + intList.begin(), intList.end(), KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly(), + initial); + QCOMPARE(result, sum); + } + + QThreadPool pool; + { + const auto result = + QtConcurrent::filteredReduced(&pool, intList, KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly(), initial).result(); + QCOMPARE(result, sum); + } + { + const auto result = + QtConcurrent::filteredReduced( + &pool, intList.begin(), intList.end(), + KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly(), initial).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + &pool, intList, KeepEvenIntegersMoveOnly(), IntSumReduceMoveOnly(), initial); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingFilteredReduced( + &pool, intList.begin(), intList.end(), KeepEvenIntegersMoveOnly(), + IntSumReduceMoveOnly(), initial); + QCOMPARE(result, sum); + } } void tst_QtConcurrentFilter::filteredReducedDifferentTypeInitialValue() @@ -820,6 +1339,56 @@ void tst_QtConcurrentFilter::filteredReducedDifferentTypeInitialValue() CHECK_FAIL("lambda-lambda"); } +void tst_QtConcurrentFilter::filteredReduceOptionConvertableToResultType() +{ + const QList<int> intList { 1, 2, 3 }; + const int sum = 4; + QThreadPool p; + ReduceOption ro = OrderedReduce; + + // With container + QCOMPARE(QtConcurrent::filteredReduced(intList, keepOddIntegers, intSumReduce, ro).result(), + sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced(intList, keepOddIntegers, intSumReduce, ro), + sum); + + // With iterators + QCOMPARE(QtConcurrent::filteredReduced(intList.begin(), intList.end(), keepOddIntegers, + intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced(intList.begin(), intList.end(), keepOddIntegers, + intSumReduce, ro), sum); + + // With custom QThreadPool; + QCOMPARE(QtConcurrent::filteredReduced(&p, intList, keepOddIntegers, intSumReduce, ro).result(), + sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced(&p, intList, keepOddIntegers, intSumReduce, ro), + sum); + QCOMPARE(QtConcurrent::filteredReduced(&p, intList.begin(), intList.end(), keepOddIntegers, + intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced(&p, intList.begin(), intList.end(), + keepOddIntegers, intSumReduce, ro), sum); + + // The same as above, but specify the result type explicitly (this invokes different overloads) + QCOMPARE(QtConcurrent::filteredReduced<int>(intList, keepOddIntegers, intSumReduce, + ro).result(), sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced<int>(intList, keepOddIntegers, intSumReduce, ro), + sum); + + QCOMPARE(QtConcurrent::filteredReduced<int>(intList.begin(), intList.end(), keepOddIntegers, + intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced<int>(intList.begin(), intList.end(), + keepOddIntegers, intSumReduce, ro), sum); + + QCOMPARE(QtConcurrent::filteredReduced<int>(&p, intList, keepOddIntegers, intSumReduce, + ro).result(), sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced<int>(&p, intList, keepOddIntegers, intSumReduce, + ro), sum); + QCOMPARE(QtConcurrent::filteredReduced<int>(&p, intList.begin(), intList.end(), keepOddIntegers, + intSumReduce, ro).result(),sum); + QCOMPARE(QtConcurrent::blockingFilteredReduced<int>(&p, intList.begin(), intList.end(), + keepOddIntegers, intSumReduce, ro), sum); +} + bool filterfn(int i) { return (i % 2); @@ -865,7 +1434,7 @@ void tst_QtConcurrentFilter::incrementalResults() QCOMPARE(future.isFinished(), true); QCOMPARE(future.resultCount(), count / 2); - QCOMPARE(future.results().count(), count / 2); + QCOMPARE(future.results().size(), count / 2); } void tst_QtConcurrentFilter::noDetach() diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/.gitignore b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/.gitignore new file mode 100644 index 0000000000..6e6bf470c9 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/.gitignore @@ -0,0 +1 @@ +tst_qtconcurrentfiltermapgenerated diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/CMakeLists.txt new file mode 100644 index 0000000000..12545702eb --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qtconcurrentfiltermapgenerated Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentfiltermapgenerated LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentfiltermapgenerated + SOURCES + tst_qtconcurrent_selected_tests.cpp + tst_qtconcurrentfiltermapgenerated.cpp tst_qtconcurrentfiltermapgenerated.h + LIBRARIES + Qt::Concurrent +) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/README.txt b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/README.txt new file mode 100644 index 0000000000..63766a2a56 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/README.txt @@ -0,0 +1,13 @@ +This directory contains a generator for unit tests for QtConcurrent. + +The subdirectory 'generator' contains the generator. Run the file +"generate_gui.py" therein. +Python3.8 and PySide2 are required. + +The generator writes on each click a testcase into the file +tst_qtconcurrentfiltermapgenerated.cpp +and +tst_qtconcurrentfiltermapgenerated.h. + +Testcases which should be preserved can be copy-pasted into +tst_qtconcurrent_selected_tests.cpp. diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generation_helpers.h b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generation_helpers.h new file mode 100644 index 0000000000..aaa0d85002 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generation_helpers.h @@ -0,0 +1,265 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef QTBASE_GENERATION_HELPERS_H +#define QTBASE_GENERATION_HELPERS_H + +#include "qglobal.h" + +#include <vector> + +struct tag_input +{ +}; +struct tag_mapped +{ +}; +struct tag_reduction +{ +}; + +template<typename tag> +struct SequenceItem +{ + SequenceItem() = default; + // bool as a stronger "explicit": should never be called inside of QtConcurrent + SequenceItem(int val, bool) : value(val) { } + + bool operator==(const SequenceItem<tag> &other) const { return value == other.value; } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename tag> +struct NoConstructSequenceItem +{ + NoConstructSequenceItem() = delete; + // bool as a stronger "explicit": should never be called inside of QtConcurrent + NoConstructSequenceItem(int val, bool) : value(val) { } + + bool operator==(const NoConstructSequenceItem<tag> &other) const + { + return value == other.value; + } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename tag> +struct MoveOnlySequenceItem +{ + MoveOnlySequenceItem() = default; + ~MoveOnlySequenceItem() = default; + MoveOnlySequenceItem(const MoveOnlySequenceItem &) = delete; + MoveOnlySequenceItem(MoveOnlySequenceItem &&other) : value(other.value) { other.value = -1; } + MoveOnlySequenceItem &operator=(const MoveOnlySequenceItem &) = delete; + MoveOnlySequenceItem &operator=(MoveOnlySequenceItem &&other) + { + value = other.value; + other.value = -1; + } + + // bool as a stronger "explicit": should never be called inside of QtConcurrent + MoveOnlySequenceItem(int val, bool) : value(val) { } + + bool operator==(const MoveOnlySequenceItem<tag> &other) const { return value == other.value; } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename tag> +struct MoveOnlyNoConstructSequenceItem +{ + MoveOnlyNoConstructSequenceItem() = delete; + ~MoveOnlyNoConstructSequenceItem() = default; + MoveOnlyNoConstructSequenceItem(const MoveOnlyNoConstructSequenceItem &) = delete; + MoveOnlyNoConstructSequenceItem(MoveOnlyNoConstructSequenceItem &&other) : value(other.value) + { + other.value = -1; + } + MoveOnlyNoConstructSequenceItem &operator=(const MoveOnlyNoConstructSequenceItem &) = delete; + MoveOnlyNoConstructSequenceItem &operator=(MoveOnlyNoConstructSequenceItem &&other) + { + value = other.value; + other.value = -1; + } + + // bool as a stronger "explicit": should never be called inside of QtConcurrent + MoveOnlyNoConstructSequenceItem(int val, bool) : value(val) { } + + bool operator==(const MoveOnlyNoConstructSequenceItem<tag> &other) const + { + return value == other.value; + } + bool isOdd() const { return value & 1; } + void multiplyByTwo() { value *= 2; } + + int value = 0; +}; + +template<typename T> +bool myfilter(const T &el) +{ + return el.isOdd(); +} + +template<typename T> +class MyFilter +{ +public: + bool operator()(const T &el) { return el.isOdd(); } +}; + +template<typename T> +class MyMoveOnlyFilter +{ + bool movedFrom = false; + +public: + MyMoveOnlyFilter() = default; + MyMoveOnlyFilter(const MyMoveOnlyFilter<T> &) = delete; + MyMoveOnlyFilter &operator=(const MyMoveOnlyFilter<T> &) = delete; + + MyMoveOnlyFilter(MyMoveOnlyFilter<T> &&other) { other.movedFrom = true; } + MyMoveOnlyFilter &operator=(MyMoveOnlyFilter<T> &&other) { other.movedFrom = true; } + + bool operator()(const T &el) + { + return movedFrom || el.isOdd(); + } +}; + +template<typename From, typename To> +To myMap(const From &f) +{ + return To(f.value * 2, true); +} + +template<typename T> +void myInplaceMap(T &el) +{ + el.multiplyByTwo(); +} + +template<typename From, typename To> +class MyMap +{ +public: + To operator()(const From &el) { return To(el.value * 2, true); } +}; + +template<typename T> +class MyInplaceMap +{ +public: + void operator()(T &el) { el.multiplyByTwo(); } +}; + +template<typename From, typename To> +class MyMoveOnlyMap +{ + bool movedFrom = false; + +public: + MyMoveOnlyMap() = default; + MyMoveOnlyMap(const MyMoveOnlyMap<From, To> &) = delete; + MyMoveOnlyMap &operator=(const MyMoveOnlyMap<From, To> &) = delete; + + MyMoveOnlyMap(MyMoveOnlyMap<From, To> &&other) { other.movedFrom = true; } + MyMoveOnlyMap &operator=(MyMoveOnlyMap<From, To> &&other) { other.movedFrom = true; } + + To operator()(const From &el) + { + if (!movedFrom) + return To(el.value * 2, true); + else + return To(-1, true); + } +}; + +template<typename T> +class MyMoveOnlyInplaceMap +{ + bool movedFrom = false; + +public: + MyMoveOnlyInplaceMap() = default; + MyMoveOnlyInplaceMap(const MyMoveOnlyInplaceMap<T> &) = delete; + MyMoveOnlyInplaceMap &operator=(const MyMoveOnlyInplaceMap<T> &) = delete; + + MyMoveOnlyInplaceMap(MyMoveOnlyInplaceMap<T> &&other) { other.movedFrom = true; } + MyMoveOnlyInplaceMap &operator=(MyMoveOnlyInplaceMap<T> &&other) { other.movedFrom = true; } + + void operator()(T &el) + { + if (!movedFrom) + el.multiplyByTwo(); + } +}; + +template<typename From, typename To> +void myReduce(To &sum, const From &val) +{ + sum.value += val.value; +} + +template<typename From, typename To> +class MyReduce +{ +public: + void operator()(To &sum, const From &val) { sum.value += val.value; } +}; + +template<typename From, typename To> +class MyMoveOnlyReduce +{ + bool movedFrom = false; + +public: + MyMoveOnlyReduce() = default; + MyMoveOnlyReduce(const MyMoveOnlyReduce<From, To> &) = delete; + MyMoveOnlyReduce &operator=(const MyMoveOnlyReduce<From, To> &) = delete; + + MyMoveOnlyReduce(MyMoveOnlyReduce<From, To> &&other) { other.movedFrom = true; } + MyMoveOnlyReduce &operator=(MyMoveOnlyReduce<From, To> &&other) { other.movedFrom = true; } + + void operator()(To &sum, const From &val) + { + if (!movedFrom) + sum.value += val.value; + } +}; + +QT_BEGIN_NAMESPACE + +// pretty printing +template<typename tag> +char *toString(const SequenceItem<tag> &i) +{ + using QTest::toString; + return toString(QString::number(i.value)); +} + +// pretty printing +template<typename T> +char *toString(const std::vector<T> &vec) +{ + using QTest::toString; + QString result(""); + for (const auto &i : vec) { + result.append(QString::number(i.value) + ", "); + } + if (result.size()) + result.chop(2); + return toString(result); +} + +QT_END_NAMESPACE + +#endif // QTBASE_GENERATION_HELPERS_H diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/function_signature.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/function_signature.py new file mode 100644 index 0000000000..d87c8a2e9b --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/function_signature.py @@ -0,0 +1,138 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + +from option_management import need_separate_output_sequence + + +# ["hallo", "welt"] -> "halloWelt" +def build_camel_case(*args): + assert all(isinstance(x, str) for x in args) + if len(args) == 0: + return "" + if len(args) == 1: + return args[0] + return args[0] + "".join(x.capitalize() for x in args[1:]) + + +def build_function_name(options): + result = [] + for part in ["blocking", "filter", "map", "reduce"]: + if options[part]: + result.append(part) + + def make_potentially_passive(verb): + if verb == "map": + return "mapped" + if verb == "blocking": + return "blocking" + + if verb[-1] == "e": + verb += "d" + else: + verb += "ed" + + return verb + + if not options["inplace"]: + result = [make_potentially_passive(x) for x in result] + + result = build_camel_case(*result) + return result + + +def build_blocking_return_type(options): + if options["inplace"]: + if options["filter"] and options["iterators"] and not options["reduce"]: + return "Iterator" # have to mark new end + else: + return "void" + else: + if options["reduce"]: + return "ResultType" + + if need_separate_output_sequence(options): + return "OutputSequence" + else: + return "Sequence" + + +def build_return_type(options): + if options["blocking"]: + return build_blocking_return_type(options) + else: + return f"QFuture<{build_blocking_return_type(options)}>" + + +def build_template_argument_list(options): + required_types = [] + if options["reduce"]: + required_types.append("typename ResultType") + + need_output_sequence = need_separate_output_sequence(options) + if need_output_sequence: + required_types.append("OutputSequence") + + if options["iterators"]: + required_types.append("Iterator") + else: + if need_output_sequence: + required_types.append("InputSequence") + else: + required_types.append("Sequence") + + # Functors + if options["filter"]: + required_types.append("KeepFunctor") + + if options["map"]: + required_types.append("MapFunctor") + + if options["reduce"]: + required_types.append("ReduceFunctor") + + if options["initialvalue"]: + required_types.append("reductionitemtype") + + return "template<" + ", ".join(["typename "+x for x in required_types]) + ">" + + +def build_argument_list(options): + required_arguments = [] + if options["pool"]: + required_arguments.append("QThreadPool* pool") + + if options["iterators"]: + required_arguments.append("Iterator begin") + required_arguments.append("Iterator end") + else: + if options["inplace"]: + required_arguments.append("Sequence & sequence") + else: + if need_separate_output_sequence(options): + required_arguments.append("InputSequence && sequence") + else: + required_arguments.append("const Sequence & sequence") + + if options["filter"]: + required_arguments.append("KeepFunctor filterFunction") + + if options["map"]: + required_arguments.append("MapFunctor function") + + if options["reduce"]: + required_arguments.append("ReduceFunctor reduceFunction") + if options["initialvalue"]: + required_arguments.append("reductionitemtype && initialValue") + + required_arguments.append("ReduceOptions") + + return "(" + ", ".join(required_arguments) + ")" + + +def build_function_signature(options): + return (build_template_argument_list(options) + "\n" + + build_return_type( + options) + " " + build_function_name(options) + build_argument_list(options) + + ";" + ) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_excel.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_excel.py new file mode 100644 index 0000000000..d05e31fc21 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_excel.py @@ -0,0 +1,46 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import pandas as pd +from option_management import function_describing_options +from function_signature import build_function_signature + + +def generate_excel_file_of_functions(filename): + olist = [] + for options in function_describing_options(): + # filter out unneccesary cases: + if options["reduce"] and options["inplace"]: + # we cannot do a reduction in-place + options["comment"] = "reduce-inplace:nonsense" + options["signature"] = "" + + elif options["initialvalue"] and not options["reduce"]: + options["comment"] = "initial-noreduce:nonsense" + options["signature"] = "" + + elif not options["reduce"] and not options["map"] and not options["filter"]: + # no operation at all + options["comment"] = "noop" + options["signature"] = "" + + else: + options["comment"] = "" + if options["map"] and options["filter"]: + options["implemented"] = "no:filter+map" + elif not options["map"] and not options["filter"]: + options["implemented"] = "no:nofilter+nomap" + elif options["inplace"] and options["iterators"] and options["filter"]: + options["implemented"] = "no:inplace+iterator+filter" + else: + options["implemented"] = "yes" + + options["signature"] = build_function_signature(options) + + olist.append(options) + + df = pd.DataFrame(olist) + df.to_excel(filename) + + +generate_excel_file_of_functions("functions.xls") diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_gui.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_gui.py new file mode 100644 index 0000000000..54c1285e74 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_gui.py @@ -0,0 +1,156 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import importlib +import sys +import PySide2 +from PySide2.QtCore import Signal +from PySide2.QtWidgets import QVBoxLayout, QRadioButton, QGroupBox, QWidget, QApplication, QPlainTextEdit, QHBoxLayout + +import generate_testcase +from helpers import insert_testcases_into_file +from option_management import (Option, OptionManager, testcase_describing_options, function_describing_options, + skip_function_description, disabled_testcase_describing_options, + skip_testcase_description) + + +class MyRadioButton(QRadioButton): + def __init__(self, value): + super(MyRadioButton, self).__init__(text=str(value)) + self.value = value + + self.toggled.connect(lambda x: x and self.activated.emit(self.value)) + + activated = Signal(object) + + +class OptionSelector(QGroupBox): + def __init__(self, parent: QWidget, option: Option): + super(OptionSelector, self).__init__(title=option.name, parent=parent) + self.layout = QVBoxLayout() + self.setLayout(self.layout) + + self.radiobuttons = [] + for val in option.possible_options: + rb = MyRadioButton(val) + self.layout.addWidget(rb) + rb.activated.connect(lambda value: self.valueSelected.emit(option.name, value)) + self.radiobuttons.append(rb) + + self.radiobuttons[0].setChecked(True) + + valueSelected = Signal(str, object) + + +class OptionsSelector(QGroupBox): + def __init__(self, parent: QWidget, option_manager: OptionManager): + super(OptionsSelector, self).__init__(title=option_manager.name, parent=parent) + self.vlayout = QVBoxLayout() + self.setLayout(self.vlayout) + self.layout1 = QHBoxLayout() + self.layout2 = QHBoxLayout() + self.layout3 = QHBoxLayout() + self.vlayout.addLayout(self.layout1) + self.vlayout.addLayout(self.layout2) + self.vlayout.addLayout(self.layout3) + self.disabledOptions = [] + + self.selectors = {} + for option in option_manager.options.values(): + os = OptionSelector(parent=self, option=option) + if "type" in option.name: + self.layout2.addWidget(os) + elif "passing" in option.name: + self.layout3.addWidget(os) + else: + self.layout1.addWidget(os) + os.valueSelected.connect(self._handle_slection) + self.selectors[option.name] = os + + self.selectedOptionsDict = {option.name: option.possible_options[0] for option in + option_manager.options.values()} + + def get_current_option_set(self): + return {k: v for k, v in self.selectedOptionsDict.items() if k not in self.disabledOptions} + + def _handle_slection(self, name: str, value: object): + self.selectedOptionsDict[name] = value + self.optionsSelected.emit(self.get_current_option_set()) + + def set_disabled_options(self, options): + self.disabledOptions = options + for name, selector in self.selectors.items(): + if name in self.disabledOptions: + selector.setEnabled(False) + else: + selector.setEnabled(True) + + optionsSelected = Signal(dict) + + +class MainWindow(QWidget): + def __init__(self): + super(MainWindow, self).__init__() + self.layout = QVBoxLayout() + self.setLayout(self.layout) + + self.functionSelector = OptionsSelector(parent=self, option_manager=function_describing_options()) + self.layout.addWidget(self.functionSelector) + self.testcaseSelector = OptionsSelector(parent=self, option_manager=testcase_describing_options()) + self.layout.addWidget(self.testcaseSelector) + + self.plainTextEdit = QPlainTextEdit() + self.plainTextEdit.setReadOnly(True) + self.layout.addWidget(self.plainTextEdit) + self.plainTextEdit.setFont(PySide2.QtGui.QFont("Fira Code", 8)) + + # temp + self.functionSelector.optionsSelected.connect(lambda o: self._handle_function_change()) + self.testcaseSelector.optionsSelected.connect(lambda o: self._handle_testcase_change()) + + self._handle_function_change() + + def _handle_function_change(self): + options = self.functionSelector.get_current_option_set() + if m := skip_function_description(options): + self.plainTextEdit.setPlainText(m) + return + + options_to_disable = disabled_testcase_describing_options(options) + self.testcaseSelector.set_disabled_options(options_to_disable) + + options.update(self.testcaseSelector.get_current_option_set()) + if m := skip_testcase_description(options): + self.plainTextEdit.setPlainText(m) + return + + self._generate_new_testcase() + + def _handle_testcase_change(self): + options = self.functionSelector.get_current_option_set() + options.update(self.testcaseSelector.get_current_option_set()) + if m := skip_testcase_description(options): + self.plainTextEdit.setPlainText(m) + return + + self._generate_new_testcase() + + def _generate_new_testcase(self): + foptions = self.functionSelector.get_current_option_set() + toptions = self.testcaseSelector.get_current_option_set() + importlib.reload(generate_testcase) + testcase = generate_testcase.generate_testcase(foptions, toptions) + self.plainTextEdit.setPlainText(testcase[1]) + filename = "../tst_qtconcurrentfiltermapgenerated.cpp" + insert_testcases_into_file(filename, [testcase]) + filename = "../tst_qtconcurrentfiltermapgenerated.h" + insert_testcases_into_file(filename, [testcase]) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + + m = MainWindow() + m.show() + + app.exec_() diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_testcase.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_testcase.py new file mode 100644 index 0000000000..d35a7e9065 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generate_testcase.py @@ -0,0 +1,366 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +import textwrap +import time +from subprocess import Popen, PIPE + +from function_signature import build_function_signature, build_function_name +from option_management import need_separate_output_sequence, qt_quirk_case + + +def InputSequenceItem(toptions): + if toptions["inputitemtype"] == "standard": + return "SequenceItem<tag_input>" + if toptions["inputitemtype"] == "noconstruct": + return "NoConstructSequenceItem<tag_input>" + if toptions["inputitemtype"] == "moveonly": + return "MoveOnlySequenceItem<tag_input>" + if toptions["inputitemtype"] == "moveonlynoconstruct": + return "MoveOnlyNoConstructSequenceItem<tag_input>" + assert False + + +def InputSequence(toptions): + item = InputSequenceItem(toptions) + if toptions["inputsequence"] == "standard": + return "std::vector<{}>".format(item) + if toptions["inputsequence"] == "moveonly": + return "MoveOnlyVector<{}>".format(item) + assert False + + +def InputSequenceInitializationString(toptions): + t = InputSequenceItem(toptions) + # construct IILE + mystring = ("[](){" + InputSequence(toptions) + " result;\n" + + "\n".join("result.push_back({}({}, true));".format(t, i) for i in range(1, 7)) + + "\n return result; }()") + return mystring + + +def OutputSequenceItem(toptions): + if toptions["map"] and (toptions["inplace"] or toptions["maptype"] == "same"): + return InputSequenceItem(toptions) + + if toptions["map"]: + if toptions["mappeditemtype"] == "standard": + return "SequenceItem<tag_mapped>" + if toptions["mappeditemtype"] == "noconstruct": + return "NoConstructSequenceItem<tag_mapped>" + if toptions["mappeditemtype"] == "moveonly": + return "MoveOnlySequenceItem<tag_mapped>" + if toptions["mappeditemtype"] == "moveonlynoconstruct": + return "MoveOnlyNoConstructSequenceItem<tag_mapped>" + assert(False) + else: + return InputSequenceItem(toptions) + + +def ReducedItem(toptions): + if toptions["reductiontype"] == "same": + return OutputSequenceItem(toptions) + else: + if toptions["reductionitemtype"] == "standard": + return "SequenceItem<tag_reduction>" + if toptions["reductionitemtype"] == "noconstruct": + return "NoConstructSequenceItem<tag_reduction>" + if toptions["reductionitemtype"] == "moveonly": + return "MoveOnlySequenceItem<tag_reduction>" + if toptions["reductionitemtype"] == "moveonlynoconstruct": + return "MoveOnlyNoConstructSequenceItem<tag_reduction>" + assert(False) + + +def OutputSequence(toptions): + item = OutputSequenceItem(toptions) + # quirk of qt: not a QFuture<Sequence> is returned + if qt_quirk_case(toptions): + return "QList<{}>".format(item) + + needs_extra = need_separate_output_sequence(toptions) + if not needs_extra: + return InputSequence(toptions) + + + if toptions["outputsequence"] == "standard": + return "std::vector<{}>".format(item) + if toptions["outputsequence"] == "moveonly": + return "MoveOnlyVector<{}>".format(item) + assert False + + +def resultData(toptions): + result = [1, 2, 3, 4, 5, 6] + if toptions["filter"]: + result = filter(lambda x: x % 2 == 1, result) + if toptions["map"]: + result = map(lambda x: 2 * x, result) + if toptions["reduce"]: + result = sum(result) + return result + + +def OutputSequenceInitializationString(toptions): + t = OutputSequenceItem(toptions) + # construct IILE + mystring = ("[](){" + OutputSequence(toptions) + " result;\n" + + "\n".join("result.push_back({}({}, true));".format(t, i) for i in resultData(toptions)) + + "\n return result; }()") + return mystring + + +def OutputScalarInitializationString(toptions): + result = resultData(toptions) + assert isinstance(result, int) + return ReducedItem(toptions) + "(" + str(result) + ", true)" + + +def FilterInitializationString(toptions): + item = InputSequenceItem(toptions) + if toptions["filterfunction"] == "function": + return "myfilter<{}>".format(item) + if toptions["filterfunction"] == "functor": + return "MyFilter<{}>{{}}".format(item) + if toptions["filterfunction"] == "memberfunction": + return "&{}::isOdd".format(item) + if toptions["filterfunction"] == "lambda": + return "[](const {}& x){{ return myfilter<{}>(x); }}".format(item, item) + if toptions["filterfunction"] == "moveonlyfunctor": + return "MyMoveOnlyFilter<{}>{{}}".format(item) + assert False + + +def MapInitializationString(toptions): + oldtype = InputSequenceItem(toptions) + newtype = OutputSequenceItem(toptions) + if toptions["inplace"]: + assert oldtype == newtype + if toptions["mapfunction"] == "function": + return "myInplaceMap<{}>".format(oldtype) + if toptions["mapfunction"] == "functor": + return "MyInplaceMap<{}>{{}}".format(oldtype) + if toptions["mapfunction"] == "memberfunction": + return "&{}::multiplyByTwo".format(oldtype) + if toptions["mapfunction"] == "lambda": + return "[]({}& x){{ return myInplaceMap<{}>(x); }}".format(oldtype, oldtype) + if toptions["mapfunction"] == "moveonlyfunctor": + return "MyMoveOnlyInplaceMap<{}>{{}}".format(oldtype) + assert False + else: + if toptions["mapfunction"] == "function": + return "myMap<{f},{t}>".format(f=oldtype, t=newtype) + if toptions["mapfunction"] == "functor": + return "MyMap<{f},{t}>{{}}".format(f=oldtype, t=newtype) + if toptions["mapfunction"] == "memberfunction": + return "&{}::multiplyByTwo".format(newtype) + if toptions["mapfunction"] == "lambda": + return "[](const {f}& x){{ return myMap<{f},{t}>(x); }}".format(f=oldtype, t=newtype) + if toptions["mapfunction"] == "moveonlyfunctor": + return "MyMoveOnlyMap<{f},{t}>{{}}".format(f=oldtype, t=newtype) + assert False + + +def ReductionInitializationString(toptions): + elementtype = OutputSequenceItem(toptions) + sumtype = ReducedItem(toptions) + + if toptions["reductionfunction"] == "function": + return "myReduce<{f},{t}>".format(f=elementtype, t=sumtype) + if toptions["reductionfunction"] == "functor": + return "MyReduce<{f},{t}>{{}}".format(f=elementtype, t=sumtype) + if toptions["reductionfunction"] == "lambda": + return "[]({t}& sum, const {f}& x){{ return myReduce<{f},{t}>(sum, x); }}".format(f=elementtype, t=sumtype) + if toptions["reductionfunction"] == "moveonlyfunctor": + return "MyMoveOnlyReduce<{f},{t}>{{}}".format(f=elementtype, t=sumtype) + assert False + + +def ReductionInitialvalueInitializationString(options): + return ReducedItem(options) + "(0, true)" + + +def function_template_args(options): + args = [] + out_s = OutputSequence(options) + in_s = InputSequence(options) + if options["reduce"] and options["reductionfunction"] == "lambda": + args.append(ReducedItem(options)) + elif out_s != in_s: + if not qt_quirk_case(options): + args.append(out_s) + + if len(args): + return "<" + ", ".join(args) + ">" + + return "" + + +numcall = 0 + + +def generate_testcase(function_options, testcase_options): + options = {**function_options, **testcase_options} + + option_description = "\n".join(" {}={}".format( + a, b) for a, b in testcase_options.items()) + option_description = textwrap.indent(option_description, " "*12) + function_signature = textwrap.indent(build_function_signature(function_options), " "*12) + testcase_name = "_".join("{}_{}".format(x, y) for x, y in options.items()) + global numcall + numcall += 1 + testcase_name = "test" + str(numcall) + function_name = build_function_name(options) + + arguments = [] + + template_args = function_template_args(options) + + # care about the thread pool + if options["pool"]: + pool_initialization = """QThreadPool pool; + pool.setMaxThreadCount(1);""" + arguments.append("&pool") + else: + pool_initialization = "" + + # care about the input sequence + input_sequence_initialization_string = InputSequenceInitializationString(options) + + if "inputsequencepassing" in options and options["inputsequencepassing"] == "lvalue" or options["inplace"]: + input_sequence_initialization = "auto input_sequence = " + \ + input_sequence_initialization_string + ";" + arguments.append("input_sequence") + elif "inputsequencepassing" in options and options["inputsequencepassing"] == "rvalue": + input_sequence_initialization = "" + arguments.append(input_sequence_initialization_string) + else: + input_sequence_initialization = "auto input_sequence = " + \ + input_sequence_initialization_string + ";" + arguments.append("input_sequence.begin()") + arguments.append("input_sequence.end()") + + # care about the map: + if options["map"]: + map_initialization_string = MapInitializationString(options) + if options["mapfunctionpassing"] == "lvalue": + map_initialization = "auto map = " + map_initialization_string + ";" + arguments.append("map") + elif options["mapfunctionpassing"] == "rvalue": + map_initialization = "" + arguments.append(map_initialization_string) + else: + assert False + else: + map_initialization = "" + + # care about the filter + if options["filter"]: + filter_initialization_string = FilterInitializationString(options) + if options["filterfunctionpassing"] == "lvalue": + filter_initialization = "auto filter = " + filter_initialization_string + ";" + arguments.append("filter") + elif options["filterfunctionpassing"] == "rvalue": + filter_initialization = "" + arguments.append(filter_initialization_string) + else: + assert (False) + else: + filter_initialization = "" + + reduction_initialvalue_initialization = "" + # care about reduction + if options["reduce"]: + reduction_initialization_expression = ReductionInitializationString(options) + if options["reductionfunctionpassing"] == "lvalue": + reduction_initialization = "auto reductor = " + reduction_initialization_expression + ";" + arguments.append("reductor") + elif options["reductionfunctionpassing"] == "rvalue": + reduction_initialization = "" + arguments.append(reduction_initialization_expression) + else: + assert (False) + + # initialvalue: + if options["initialvalue"]: + reduction_initialvalue_initialization_expression = ReductionInitialvalueInitializationString(options) + if options["reductioninitialvaluepassing"] == "lvalue": + reduction_initialvalue_initialization = "auto initialvalue = " + reduction_initialvalue_initialization_expression + ";" + arguments.append("initialvalue") + elif options["reductioninitialvaluepassing"] == "rvalue": + reduction_initialvalue_initialization = "" + arguments.append(reduction_initialvalue_initialization_expression) + else: + assert (False) + + if options["reductionoptions"] == "UnorderedReduce": + arguments.append("QtConcurrent::UnorderedReduce") + elif options["reductionoptions"] == "OrderedReduce": + arguments.append("QtConcurrent::OrderedReduce") + elif options["reductionoptions"] == "SequentialReduce": + arguments.append("QtConcurrent::SequentialReduce") + else: + assert options["reductionoptions"] == "unspecified" + else: + reduction_initialization = "" + + # what is the expected result + if options["filter"]: + if not options["reduce"]: + expected_result_expression = OutputSequenceInitializationString(options) + else: + expected_result_expression = OutputScalarInitializationString(options) + elif options["map"]: + if not options["reduce"]: + expected_result_expression = OutputSequenceInitializationString(options) + else: + expected_result_expression = OutputScalarInitializationString(options) + + wait_result_expression = "" + if options["inplace"]: + if options["blocking"]: + result_accepting = "" + result_variable = "input_sequence" + else: + result_accepting = "auto future = " + result_variable = "input_sequence" + wait_result_expression = "future.waitForFinished();" + elif options["blocking"]: + result_accepting = "auto result = " + result_variable = "result" + else: + if not options["reduce"]: + result_accepting = "auto result = " + result_variable = "result.results()" + else: + result_accepting = "auto result = " + result_variable = "result.takeResult()" + + arguments_passing = ", ".join(arguments) + final_string = f""" + void tst_QtConcurrentFilterMapGenerated::{testcase_name}() + {{ + /* test for +{function_signature} + + with +{option_description} + */ + + {pool_initialization} + {input_sequence_initialization} + {filter_initialization} + {map_initialization} + {reduction_initialization} + {reduction_initialvalue_initialization} + + {result_accepting}QtConcurrent::{function_name}{template_args}({arguments_passing}); + + auto expected_result = {expected_result_expression}; + {wait_result_expression} + QCOMPARE({result_variable}, expected_result); + }} + """ + p = Popen(["clang-format"], stdin=PIPE, stdout=PIPE, stderr=PIPE) + final_string = p.communicate(final_string.encode())[0].decode() + + return (f" void {testcase_name}();\n", final_string) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generator_main.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generator_main.py new file mode 100644 index 0000000000..87cb4c7bc4 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/generator_main.py @@ -0,0 +1,33 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from option_management import function_describing_options, skip_function_description, testcase_describing_options +from generate_testcase import generate_testcase +from helpers import insert_testcases_into_file +filename = "../tst_qtconcurrentfiltermapgenerated.cpp" + +testcases = [] +counter = 0 +for fo in function_describing_options(): + if skip_function_description(fo): + continue + + if not ( + fo["blocking"] + and fo["filter"] + # and not fo["map"] + and fo["reduce"] + and not fo["inplace"] + and not fo["iterators"] + and not fo["initialvalue"] + and not fo["pool"] + ): + continue + + for to in testcase_describing_options(fo): + print("generate test") + testcases.append(generate_testcase(fo, to)) + counter += 1 + +print(counter) +insert_testcases_into_file(filename, testcases) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/helpers.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/helpers.py new file mode 100644 index 0000000000..fbe969789c --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/helpers.py @@ -0,0 +1,31 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + +def insert_testcases_into_file(filename, testcases): + # assume testcases is an array of tuples of (declaration, definition) + with open(filename) as f: + inputlines = f.readlines() + outputlines = [] + skipping = False + for line in inputlines: + if not skipping: + outputlines.append(line) + else: + if "END_GENERATED" in line: + outputlines.append(line) + skipping = False + + if "START_GENERATED_SLOTS" in line: + # put in testcases + outputlines += [t[0] for t in testcases] + skipping = True + + if "START_GENERATED_IMPLEMENTATIONS" in line: + # put in testcases + outputlines += [t[1] for t in testcases] + skipping = True + + if outputlines != inputlines: + with open(filename, "w") as f: + f.writelines(outputlines) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/option_management.py b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/option_management.py new file mode 100644 index 0000000000..6a1fc87f9f --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/generator/option_management.py @@ -0,0 +1,195 @@ +# Copyright (C) 2020 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import itertools + + +class Option: + def __init__(self, name, possible_options): + self.name = name + self.possible_options = possible_options + + +class OptionManager: + def __init__(self, name): + self.options = {} + self.name = name + + def add_option(self, option: Option): + self.options[option.name] = option + + def iterate(self): + for x in itertools.product(*[ + [(name, x) for x in self.options[name]] + for name in self.options.keys() + ]): + yield dict(x) + + +def function_describing_options(): + om = OptionManager("function options") + + om.add_option(Option("blocking", [True, False])) + om.add_option(Option("filter", [True, False])) + om.add_option(Option("map", [False, True])) + om.add_option(Option("reduce", [False, True])) + om.add_option(Option("inplace", [True, False])) + om.add_option(Option("iterators", [False, True])) + om.add_option(Option("initialvalue", [False, True])) + om.add_option(Option("pool", [True, False])) + + return om + + +def skip_function_description(options): + if options["reduce"] and options["inplace"]: + return "we cannot do a reduction in-place" + + if options["initialvalue"] and not options["reduce"]: + return "without reduction, we do not need an initial value" + + if not options["reduce"] and not options["map"] and not options["filter"]: + return "no operation at all" + + # the following things are skipped because Qt does not support them + if options["filter"] and options["map"]: + return "Not supported by Qt: both map and filter operation" + + if not options["filter"] and not options["map"]: + return "Not supported by Qt: no map and no filter operation" + + if options["inplace"] and options["iterators"] and options["filter"]: + return "Not supported by Qt: filter operation in-place with iterators" + + return False + + +def qt_quirk_case(options): + # whenever a function should return a QFuture<Sequence>, + # it returns a QFuture<item> instead + if options["inplace"] or options["reduce"] or options["blocking"]: + return False + + return True + + +def need_separate_output_sequence(options): + # do we need an output sequence? + if not (options["inplace"] or options["reduce"]): + # transforming a sequence into a sequence + if options["iterators"] or options["map"]: + return True + + return False + + +def testcase_describing_options(): + om = OptionManager("testcase options") + + om.add_option(Option("inputsequence", ["standard", "moveonly"])) + om.add_option(Option("inputsequencepassing", ["lvalue", "rvalue"])) + om.add_option(Option("inputitemtype", ["standard", "noconstruct", "moveonly", "moveonlynoconstruct"])) + + om.add_option(Option("outputsequence", ["standard", "moveonly"])) + + om.add_option(Option("maptype", ["same", "different"])) + om.add_option(Option("mappeditemtype", ["standard", "noconstruct", "moveonly", "moveonlynoconstruct"])) + + om.add_option(Option("reductiontype", ["same", "different"])) + + om.add_option(Option("reductionitemtype", [ + "standard", "noconstruct", "moveonly", "moveonlynoconstruct"])) + + om.add_option(Option("filterfunction", ["functor", "function", "memberfunction", "lambda", "moveonlyfunctor"])) + om.add_option(Option("filterfunctionpassing", ["lvalue", "rvalue"])) + + om.add_option(Option("mapfunction", ["functor", "function", "memberfunction", "lambda", "moveonlyfunctor"])) + om.add_option(Option("mapfunctionpassing", ["lvalue", "rvalue"])) + + om.add_option(Option("reductionfunction", ["functor", "function", "lambda", "moveonlyfunctor"])) + om.add_option(Option("reductionfunctionpassing", ["lvalue", "rvalue"])) + + om.add_option(Option("reductioninitialvaluepassing", ["lvalue", "rvalue"])) + + om.add_option(Option("reductionoptions", [ + "unspecified", "UnorderedReduce", "OrderedReduce", "SequentialReduce"])) + + return om + + +def disabled_testcase_describing_options(options): + disabled_options = [] + + if options["inplace"] or options["iterators"]: + disabled_options.append("inputsequencepassing") + + if not need_separate_output_sequence(options): + disabled_options.append("outputsequence") + + if not options["map"]: + disabled_options.append("mappeditemtype") + + if options["map"] and options["inplace"]: + disabled_options.append("mappeditemtype") + + if not options["filter"]: + disabled_options.append("filterfunction") + disabled_options.append("filterfunctionpassing") + + if not options["map"]: + disabled_options.append("mapfunction") + disabled_options.append("mapfunctionpassing") + + if not options["reduce"]: + disabled_options.append("reductionfunction") + disabled_options.append("reductionfunctionpassing") + + if not options["reduce"]: + disabled_options.append("reductiontype") + disabled_options.append("reductioninitialvaluepassing") + disabled_options.append("reductionoptions") + disabled_options.append("reductionitemtype") + + if not options["initialvalue"]: + disabled_options.append("reductioninitialvaluepassing") + + if not options["map"]: + disabled_options.append("maptype") + else: + if options["inplace"]: + disabled_options.append("maptype") + + return disabled_options + + +def skip_testcase_description(options): + if ( + "maptype" in options and + options["maptype"] == "same" and + "inputitemtype" in options and "mappeditemtype" in options and + (options["inputitemtype"] != options["mappeditemtype"]) + ): + return ("Not possible: map should map to same type, " + "but mapped item type should differ from input item type.") + + if ( + "reductiontype" in options and + options["reductiontype"] == "same"): + # we have to check that this is possible + if ("mappeditemtype" in options and "reductionitemtype" in options + and (options["mappeditemtype"] != options["reductionitemtype"]) + ): + return ("Not possible: should reduce in the same type, " + "but reduction item type should differ from mapped item type.") + if ("mappeditemtype" not in options + and (options["inputitemtype"] != options["reductionitemtype"])): + return ("Not possible: should reduce in the same type, " + "but reduction item type should differ from input item type.") + + if ( + options["map"] and not options["inplace"] + and options["mapfunction"] == "memberfunction" + ): + return "map with memberfunction only available for in-place map" + + return False diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrent_selected_tests.cpp b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrent_selected_tests.cpp new file mode 100644 index 0000000000..edb7cce4c9 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrent_selected_tests.cpp @@ -0,0 +1,347 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "tst_qtconcurrentfiltermapgenerated.h" + +void tst_QtConcurrentFilterMapGenerated::mapReduceThroughDifferentTypes() +{ + /* test for + template<typename typename ResultType, typename Iterator, typename MapFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingMappedReduced(Iterator begin, + Iterator end, MapFunctor function, ReduceFunctor reduceFunction, reductionitemtype && + initialValue, ReduceOptions); + + with + inputsequence=standard + inputitemtype=standard + maptype=different + mappeditemtype=standard + reductiontype=different + reductionitemtype=standard + mapfunction=function + mapfunctionpassing=lvalue + reductionfunction=function + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=lvalue + reductionoptions=unspecified + */ + + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto map = myMap<SequenceItem<tag_input>, SequenceItem<tag_mapped>>; + auto reductor = myReduce<SequenceItem<tag_mapped>, SequenceItem<tag_reduction>>; + auto initialvalue = SequenceItem<tag_reduction>(0, true); + + auto result = QtConcurrent::blockingMappedReduced(input_sequence.begin(), input_sequence.end(), + map, reductor, initialvalue); + + auto expected_result = SequenceItem<tag_reduction>(42, true); + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyFilterObject() +{ + /* test for + template<typename Sequence, typename KeepFunctor> + void blockingFilter(QThreadPool* pool, Sequence & sequence, KeepFunctor filterFunction); + + with + inputsequence=standard + inputitemtype=standard + filterfunction=moveonlyfunctor + filterfunctionpassing=rvalue + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + QtConcurrent::blockingFilter(&pool, input_sequence, + MyMoveOnlyFilter<SequenceItem<tag_input>> {}); + + auto expected_result = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + return result; + }(); + + QCOMPARE(input_sequence, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyMapObject() +{ + /* test for + template<typename Sequence, typename MapFunctor> + void blockingMap(QThreadPool* pool, Sequence & sequence, MapFunctor function); + + with + inputsequence=standard + inputitemtype=standard + mapfunction=moveonlyfunctor + mapfunctionpassing=rvalue + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + QtConcurrent::blockingMap(&pool, input_sequence, + MyMoveOnlyInplaceMap<SequenceItem<tag_input>> {}); + + auto expected_result = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + result.push_back(SequenceItem<tag_input>(8, true)); + result.push_back(SequenceItem<tag_input>(10, true)); + result.push_back(SequenceItem<tag_input>(12, true)); + return result; + }(); + + QCOMPARE(input_sequence, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyReduceObject() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename MapFunctor, typename + ReduceFunctor> ResultType blockingMappedReduced(QThreadPool* pool, const Sequence & sequence, + MapFunctor function, ReduceFunctor reduceFunction, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + maptype=same + mappeditemtype=standard + reductiontype=same + reductionitemtype=standard + mapfunction=functor + mapfunctionpassing=lvalue + reductionfunction=moveonlyfunctor + reductionfunctionpassing=rvalue + reductionoptions=unspecified + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto map = MyMap<SequenceItem<tag_input>, SequenceItem<tag_input>> {}; + + auto result = QtConcurrent::blockingMappedReduced<SequenceItem<tag_input>>( + &pool, input_sequence, map, + MyMoveOnlyReduce<SequenceItem<tag_input>, SequenceItem<tag_input>> {}); + + auto expected_result = SequenceItem<tag_input>(42, true); + + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::functorAsReduction() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename KeepFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingFilteredReduced(QThreadPool* pool, + const Sequence & sequence, KeepFunctor filterFunction, ReduceFunctor reduceFunction, + reductionitemtype && initialValue, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + reductiontype=same + reductionitemtype=standard + filterfunction=functor + filterfunctionpassing=lvalue + reductionfunction=functor + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=lvalue + reductionoptions=unspecified + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + auto filter = MyFilter<SequenceItem<tag_input>> {}; + + auto reductor = MyReduce<SequenceItem<tag_input>, SequenceItem<tag_input>> {}; + auto initialvalue = SequenceItem<tag_input>(0, true); + + auto result = QtConcurrent::blockingFilteredReduced<SequenceItem<tag_input>>( + &pool, input_sequence, filter, reductor, initialvalue); + + auto expected_result = SequenceItem<tag_input>(9, true); + + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::moveOnlyReductionItem() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename KeepFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingFilteredReduced(QThreadPool* pool, + const Sequence & sequence, KeepFunctor filterFunction, ReduceFunctor reduceFunction, + reductionitemtype && initialValue, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + reductiontype=different + reductionitemtype=moveonly + filterfunction=moveonlyfunctor + filterfunctionpassing=rvalue + reductionfunction=function + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=rvalue + reductionoptions=unspecified + */ +/* TODO: does not work yet + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto reductor = myReduce<SequenceItem<tag_input>, MoveOnlySequenceItem<tag_reduction>>; + + auto result = QtConcurrent::blockingFilteredReduced( + &pool, input_sequence, MyMoveOnlyFilter<SequenceItem<tag_input>> {}, reductor, + MoveOnlySequenceItem<tag_reduction>(0, true)); + + auto expected_result = MoveOnlySequenceItem<tag_reduction>(9, true); + + QCOMPARE(result, expected_result);*/ +} + +void tst_QtConcurrentFilterMapGenerated::noDefaultConstructorItemMapped() +{ + /* test for + template<typename typename ResultType, typename Sequence, typename MapFunctor, typename + ReduceFunctor, typename reductionitemtype> ResultType blockingMappedReduced(QThreadPool* pool, + const Sequence & sequence, MapFunctor function, ReduceFunctor reduceFunction, reductionitemtype + && initialValue, ReduceOptions); + + with + inputsequence=standard + inputsequencepassing=lvalue + inputitemtype=standard + maptype=same + mappeditemtype=standard + reductiontype=different + reductionitemtype=noconstruct + mapfunction=functor + mapfunctionpassing=lvalue + reductionfunction=function + reductionfunctionpassing=lvalue + reductioninitialvaluepassing=lvalue + reductionoptions=unspecified + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto map = MyMap<SequenceItem<tag_input>, SequenceItem<tag_input>> {}; + auto reductor = myReduce<SequenceItem<tag_input>, NoConstructSequenceItem<tag_reduction>>; + auto initialvalue = NoConstructSequenceItem<tag_reduction>(0, true); + + auto result = + QtConcurrent::blockingMappedReduced(&pool, input_sequence, map, reductor, initialvalue); + + auto expected_result = NoConstructSequenceItem<tag_reduction>(42, true); + + QCOMPARE(result, expected_result); +} + +void tst_QtConcurrentFilterMapGenerated::noDefaultConstructorItemFiltered() +{ + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + + auto filter = MyFilter<SequenceItem<tag_input>> {}; + auto reductor = myReduce<SequenceItem<tag_input>, NoConstructSequenceItem<tag_reduction>>; + auto initialvalue = NoConstructSequenceItem<tag_reduction>(0, true); + + auto result = QtConcurrent::blockingFilteredReduced(&pool, input_sequence, filter, reductor, + initialvalue); + + auto expected_result = NoConstructSequenceItem<tag_reduction>(9, true); + + QCOMPARE(result, expected_result); +} diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.cpp b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.cpp new file mode 100644 index 0000000000..089ca3f867 --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <qtconcurrentfilter.h> +#include <qtconcurrentmap.h> +#include <QCoreApplication> +#include <QList> +#include <QTest> + +#include "../testhelper_functions.h" +#include "generation_helpers.h" + +#include "tst_qtconcurrentfiltermapgenerated.h" + +using namespace QtConcurrent; + +// START_GENERATED_IMPLEMENTATIONS (see generate_tests.py) + +void tst_QtConcurrentFilterMapGenerated::test1() +{ + /* test for + template<typename Sequence, typename KeepFunctor> + void blockingFilter(QThreadPool* pool, Sequence & sequence, KeepFunctor filterFunction); + + with + inputsequence=standard + inputitemtype=standard + filterfunction=functor + filterfunctionpassing=lvalue + */ + + QThreadPool pool; + pool.setMaxThreadCount(1); + auto input_sequence = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(2, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(4, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + result.push_back(SequenceItem<tag_input>(6, true)); + return result; + }(); + auto filter = MyFilter<SequenceItem<tag_input>> {}; + + QtConcurrent::blockingFilter(&pool, input_sequence, filter); + + auto expected_result = []() { + std::vector<SequenceItem<tag_input>> result; + result.push_back(SequenceItem<tag_input>(1, true)); + result.push_back(SequenceItem<tag_input>(3, true)); + result.push_back(SequenceItem<tag_input>(5, true)); + return result; + }(); + + QCOMPARE(input_sequence, expected_result); +} +// END_GENERATED_IMPLEMENTATION (see generate_tests.py) + +QTEST_MAIN(tst_QtConcurrentFilterMapGenerated) diff --git a/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.h b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.h new file mode 100644 index 0000000000..31b62ac4fd --- /dev/null +++ b/tests/auto/concurrent/qtconcurrentfiltermapgenerated/tst_qtconcurrentfiltermapgenerated.h @@ -0,0 +1,27 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <qtconcurrentfilter.h> +#include <qtconcurrentmap.h> +#include <QTest> + +#include "generation_helpers.h" + +using namespace QtConcurrent; + +class tst_QtConcurrentFilterMapGenerated : public QObject +{ + Q_OBJECT + +private slots: + void mapReduceThroughDifferentTypes(); + void moveOnlyFilterObject(); + void moveOnlyMapObject(); + void moveOnlyReduceObject(); + void functorAsReduction(); + void moveOnlyReductionItem(); + void noDefaultConstructorItemMapped(); + void noDefaultConstructorItemFiltered(); + // START_GENERATED_SLOTS (see generate_tests.py) + void test1(); + // END_GENERATED_SLOTS (see generate_tests.py) +}; diff --git a/tests/auto/concurrent/qtconcurrentiteratekernel/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentiteratekernel/CMakeLists.txt index e73d574fcb..2eea340795 100644 --- a/tests/auto/concurrent/qtconcurrentiteratekernel/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrentiteratekernel/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qtconcurrentiteratekernel.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrentiteratekernel Test: ##################################################################### -qt_add_test(tst_qtconcurrentiteratekernel +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentiteratekernel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentiteratekernel SOURCES tst_qtconcurrentiteratekernel.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent ) diff --git a/tests/auto/concurrent/qtconcurrentiteratekernel/qtconcurrentiteratekernel.pro b/tests/auto/concurrent/qtconcurrentiteratekernel/qtconcurrentiteratekernel.pro deleted file mode 100644 index f599372c6f..0000000000 --- a/tests/auto/concurrent/qtconcurrentiteratekernel/qtconcurrentiteratekernel.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrentiteratekernel -QT = core testlib concurrent -SOURCES = tst_qtconcurrentiteratekernel.cpp diff --git a/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp b/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp index 574c82b78e..27113ad8b7 100644 --- a/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp +++ b/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp @@ -1,31 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QThread> +#include <QSet> struct TestIterator { @@ -67,7 +43,7 @@ int distance(TestIterator &a, TestIterator &b) } #include <qtconcurrentiteratekernel.h> -#include <QtTest/QtTest> +#include <QTest> using namespace QtConcurrent; @@ -126,7 +102,8 @@ public: void tst_QtConcurrentIterateKernel::instantiate() { - startThreadEngine(new PrintFor(0, 40)).startBlocking(); + auto future = startThreadEngine(new PrintFor(0, 40)).startAsynchronously(); + future.waitForFinished(); QCOMPARE(iterations.loadRelaxed(), 40); } @@ -165,8 +142,10 @@ void tst_QtConcurrentIterateKernel::stresstest() const int times = 50; for (int i = 0; i < times; ++i) { counter.storeRelaxed(0); - CountFor f(0, iterations); - f.startBlocking(); + // ThreadEngine will delete f when it finishes + auto f = new CountFor(0, iterations); + auto future = f->startAsynchronously(); + future.waitForFinished(); QCOMPARE(counter.loadRelaxed(), iterations); } } @@ -174,8 +153,12 @@ void tst_QtConcurrentIterateKernel::stresstest() void tst_QtConcurrentIterateKernel::noIterations() { const int times = 20000; - for (int i = 0; i < times; ++i) - startThreadEngine(new IterateKernel<TestIterator, void>(QThreadPool::globalInstance(), 0, 0)).startBlocking(); + for (int i = 0; i < times; ++i) { + auto future = startThreadEngine(new IterateKernel<TestIterator, void>( + QThreadPool::globalInstance(), 0, 0)) + .startAsynchronously(); + future.waitForFinished(); + } } QMutex threadsMutex; @@ -230,13 +213,15 @@ void tst_QtConcurrentIterateKernel::throttling() threads.clear(); - ThrottleFor f(0, totalIterations); - f.startBlocking(); + // ThreadEngine will delete f when it finishes + auto f = new ThrottleFor(0, totalIterations); + auto future = f->startAsynchronously(); + future.waitForFinished(); QCOMPARE(iterations.loadRelaxed(), totalIterations); - QCOMPARE(threads.count(), 1); + QCOMPARE(threads.size(), 1); } class MultipleResultsFor : public IterateKernel<TestIterator, int> @@ -254,7 +239,7 @@ public: void tst_QtConcurrentIterateKernel::multipleResults() { QFuture<int> f = startThreadEngine(new MultipleResultsFor(0, 10)).startAsynchronously(); - QCOMPARE(f.results().count() , 10); + QCOMPARE(f.results().size() , 10); QCOMPARE(f.resultAt(0), 0); QCOMPARE(f.resultAt(5), 5); QCOMPARE(f.resultAt(9), 9); diff --git a/tests/auto/concurrent/qtconcurrentmap/.prev_CMakeLists.txt b/tests/auto/concurrent/qtconcurrentmap/.prev_CMakeLists.txt deleted file mode 100644 index be51bf09bd..0000000000 --- a/tests/auto/concurrent/qtconcurrentmap/.prev_CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Generated from qtconcurrentmap.pro. - -##################################################################### -## tst_qtconcurrentmap Test: -##################################################################### - -add_qt_test(tst_qtconcurrentmap - SOURCES - tst_qtconcurrentmap.cpp - DEFINES - -QT_NO_LINKED_LIST - PUBLIC_LIBRARIES - Qt::Concurrent -) - -## Scopes: -##################################################################### diff --git a/tests/auto/concurrent/qtconcurrentmap/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentmap/CMakeLists.txt index c14780e0de..62b434a25f 100644 --- a/tests/auto/concurrent/qtconcurrentmap/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrentmap/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtconcurrentmap.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrentmap Test: ##################################################################### -qt_add_test(tst_qtconcurrentmap +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentmap LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentmap SOURCES tst_qtconcurrentmap.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent ) diff --git a/tests/auto/concurrent/qtconcurrentmap/functions.h b/tests/auto/concurrent/qtconcurrentmap/functions.h deleted file mode 100644 index b37ea08716..0000000000 --- a/tests/auto/concurrent/qtconcurrentmap/functions.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ -#ifndef FUNCTIONS_H -#define FUNCTIONS_H - -bool keepEvenIntegers(const int &x) -{ - return (x & 1) == 0; -} - -class KeepEvenIntegers -{ -public: - bool operator()(const int &x) - { - return (x & 1) == 0; - } -}; - -class Number -{ - int n; - -public: - Number() - : n(0) - { } - - Number(int n) - : n(n) - { } - - void multiplyBy2() - { - n *= 2; - } - - Number multipliedBy2() const - { - return n * 2; - } - - bool isEven() const - { - return (n & 1) == 0; - } - - int toInt() const - { - return n; - } - - QString toString() const - { - return QString::number(n); - } - - Number squared() const - { - return Number(n * n); - } - - bool operator==(const Number &other) const - { - return n == other.n; - } -}; - -bool keepEvenNumbers(const Number &x) -{ - return (x.toInt() & 1) == 0; -} - -class KeepEvenNumbers -{ -public: - bool operator()(const Number &x) - { - return (x.toInt() & 1) == 0; - } -}; - -void intSumReduce(int &sum, int x) -{ - sum += x; -} - -class IntSumReduce -{ -public: - void operator()(int &sum, int x) - { - sum += x; - } -}; - -void numberSumReduce(int &sum, const Number &x) -{ - sum += x.toInt(); -} - -class NumberSumReduce -{ -public: - void operator()(int &sum, const Number &x) - { - sum += x.toInt(); - } -}; - -#endif diff --git a/tests/auto/concurrent/qtconcurrentmap/qtconcurrentmap.pro b/tests/auto/concurrent/qtconcurrentmap/qtconcurrentmap.pro deleted file mode 100644 index bbadd1ada1..0000000000 --- a/tests/auto/concurrent/qtconcurrentmap/qtconcurrentmap.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrentmap -QT = core testlib concurrent -SOURCES = tst_qtconcurrentmap.cpp - -# Force C++17 if available -contains(QT_CONFIG, c++1z): CONFIG += c++1z diff --git a/tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp b/tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp index 39af0acd06..3e3165013f 100644 --- a/tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp +++ b/tests/auto/concurrent/qtconcurrentmap/tst_qtconcurrentmap.cpp @@ -1,55 +1,36 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtconcurrentmap.h> #include <qexception.h> - #include <qdebug.h> + #include <QThread> #include <QMutex> +#include <QTest> +#include <QSet> +#include <QRandomGenerator> -#include <QtTest/QtTest> - -#include "functions.h" +#include "../testhelper_functions.h" class tst_QtConcurrentMap : public QObject { Q_OBJECT private slots: void map(); - void blocking_map(); + void blockingMap(); + void mapOnRvalue(); void mapped(); void mappedThreadPool(); + void mappedWithMoveOnlyCallable(); void mappedReduced(); void mappedReducedThreadPool(); + void mappedReducedWithMoveOnlyCallable(); void mappedReducedDifferentType(); void mappedReducedInitialValue(); void mappedReducedInitialValueThreadPool(); + void mappedReducedInitialValueWithMoveOnlyCallable(); void mappedReducedDifferentTypeInitialValue(); + void mappedReduceOptionConvertableToResultType(); void assignResult(); void functionOverloads(); void noExceptFunctionOverloads(); @@ -69,7 +50,7 @@ public slots: using namespace QtConcurrent; -void multiplyBy2Immutable(int x) +void multiplyBy2Immutable([[maybe_unused]] int x) { x *= 2; } @@ -77,7 +58,7 @@ void multiplyBy2Immutable(int x) class MultiplyBy2Immutable { public: - void operator()(int x) + void operator()([[maybe_unused]] int x) { x *= 2; } @@ -97,6 +78,19 @@ public: } }; +class MultiplyBy2InPlaceMoveOnly +{ +public: + MultiplyBy2InPlaceMoveOnly() = default; + MultiplyBy2InPlaceMoveOnly(MultiplyBy2InPlaceMoveOnly &&) = default; + MultiplyBy2InPlaceMoveOnly &operator=(MultiplyBy2InPlaceMoveOnly &&other) = default; + + MultiplyBy2InPlaceMoveOnly(const MultiplyBy2InPlaceMoveOnly &) = delete; + MultiplyBy2InPlaceMoveOnly &operator=(const MultiplyBy2InPlaceMoveOnly &) = delete; + + void operator()(int &x) { x *= 2; } +}; + Q_DECLARE_METATYPE(QList<Number>); void tst_QtConcurrentMap::map() @@ -137,6 +131,12 @@ void tst_QtConcurrentMap::map() QCOMPARE(list, QList<int>() << 128 << 256 << 384); QtConcurrent::map(list.begin(), list.end(), [](int &x){x *= 2;}).waitForFinished(); QCOMPARE(list, QList<int>() << 256 << 512 << 768); + + // move-only functor + QtConcurrent::map(list, MultiplyBy2InPlaceMoveOnly()).waitForFinished(); + QCOMPARE(list, QList<int>() << 512 << 1024 << 1536); + QtConcurrent::map(list.begin(), list.end(), MultiplyBy2InPlaceMoveOnly()).waitForFinished(); + QCOMPARE(list, QList<int>() << 1024 << 2048 << 3072); } // functors don't take arguments by reference, making these no-ops @@ -163,12 +163,39 @@ void tst_QtConcurrentMap::map() QCOMPARE(list, QList<int>() << 1 << 2 << 3); // lambda - QtConcurrent::map(list, [](int x){x *= 2;}).waitForFinished(); + QtConcurrent::map(list, []([[maybe_unused]] int x){x *= 2;}).waitForFinished(); QCOMPARE(list, QList<int>() << 1 << 2 << 3); - QtConcurrent::map(list.begin(), list.end(), [](int x){x *= 2;}).waitForFinished(); + QtConcurrent::map(list.begin(), list.end(), []([[maybe_unused]] int x){x *= 2;}).waitForFinished(); QCOMPARE(list, QList<int>() << 1 << 2 << 3); } + // functors take arguments by reference, modifying the move-only sequence in place + { + MoveOnlyVector<int> moveOnlyVector({ 1, 2, 3 }); + + // functor + QtConcurrent::map(moveOnlyVector, MultiplyBy2InPlace()).waitForFinished(); + QCOMPARE(moveOnlyVector, MoveOnlyVector<int>({ 2, 4, 6 })); + } + + // non-template sequences + { + NonTemplateSequence list { 1, 2, 3 }; + QtConcurrent::map(list, multiplyBy2InPlace).waitForFinished(); + QCOMPARE(list, NonTemplateSequence({ 2, 4, 6 })); + } + + // custom pool with invalid number of threads + { + QList<int> list; + list << 1 << 2 << 3; + QThreadPool pool; + pool.setMaxThreadCount(0); // explicitly set incorrect value + // This should not crash + QtConcurrent::map(&pool, list, MultiplyBy2InPlace()).waitForFinished(); + QCOMPARE(list, QList<int>() << 2 << 4 << 6); + } + #if 0 // not allowed: map() with immutable sequences makes no sense { @@ -199,7 +226,7 @@ void tst_QtConcurrentMap::map() #if 0 // not allowed: map() on a const list, where functors try to modify the items in the list { - const QList<int> list = QList<int>() << 1 << 2 << 3;; + const QList<int> list = QList<int>() << 1 << 2 << 3; QtConcurrent::map(list, MultiplyBy2InPlace()); QtConcurrent::map(list, multiplyBy2InPlace); @@ -211,7 +238,7 @@ void tst_QtConcurrentMap::map() #endif } -void tst_QtConcurrentMap::blocking_map() +void tst_QtConcurrentMap::blockingMap() { // functors take arguments by reference, modifying the sequence in place { @@ -249,6 +276,24 @@ void tst_QtConcurrentMap::blocking_map() QCOMPARE(list, QList<int>() << 128 << 256 << 384); QtConcurrent::blockingMap(list.begin(), list.end(), [](int &x) { x *= 2; }); QCOMPARE(list, QList<int>() << 256 << 512 << 768); + + // move-only functor + QtConcurrent::blockingMap(list, MultiplyBy2InPlaceMoveOnly()); + QCOMPARE(list, QList<int>() << 512 << 1024 << 1536); + QtConcurrent::blockingMap(list.begin(), list.end(), MultiplyBy2InPlaceMoveOnly()); + QCOMPARE(list, QList<int>() << 1024 << 2048 << 3072); + } + + // functors take arguments by reference, modifying the move-only sequence in place + { + MoveOnlyVector<int> moveOnlyVector({ 1, 2, 3 }); + + // functor + QtConcurrent::blockingMap(moveOnlyVector, MultiplyBy2InPlace()); + QCOMPARE(moveOnlyVector, MoveOnlyVector<int>({ 2, 4, 6 })); + QtConcurrent::blockingMap(moveOnlyVector.begin(), moveOnlyVector.end(), + MultiplyBy2InPlace()); + QCOMPARE(moveOnlyVector, MoveOnlyVector<int>({ 4, 8, 12 })); } // functors don't take arguments by reference, making these no-ops @@ -275,12 +320,19 @@ void tst_QtConcurrentMap::blocking_map() QCOMPARE(list, QList<int>() << 1 << 2 << 3); // lambda - QtConcurrent::blockingMap(list, [](int x) { x *= 2; }); + QtConcurrent::blockingMap(list, []([[maybe_unused]] int x) { x *= 2; }); QCOMPARE(list, QList<int>() << 1 << 2 << 3); - QtConcurrent::blockingMap(list.begin(), list.end(), [](int x) { x *= 2; }); + QtConcurrent::blockingMap(list.begin(), list.end(), []([[maybe_unused]] int x) { x *= 2; }); QCOMPARE(list, QList<int>() << 1 << 2 << 3); } + // non-template sequences + { + NonTemplateSequence list { 1, 2, 3 }; + QtConcurrent::blockingMap(list, multiplyBy2InPlace); + QCOMPARE(list, NonTemplateSequence({ 2, 4, 6 })); + } + #if 0 // not allowed: map() with immutable sequences makes no sense { @@ -311,7 +363,7 @@ void tst_QtConcurrentMap::blocking_map() #if 0 // not allowed: map() on a const list, where functors try to modify the items in the list { - const QList<int> list = QList<int>() << 1 << 2 << 3;; + const QList<int> list = QList<int>() << 1 << 2 << 3; QtConcurrent::blockingMap(list, MultiplyBy2InPlace()); QtConcurrent::blockingMap(list, multiplyBy2InPlace); @@ -323,6 +375,54 @@ void tst_QtConcurrentMap::blocking_map() #endif } +void tst_QtConcurrentMap::mapOnRvalue() +{ + struct ListRange + { + using iterator = QList<int>::iterator; + ListRange(iterator b, iterator e) : m_begin(b), m_end(e) { } + + iterator begin() const { return m_begin; } + iterator end() const { return m_end; } + + private: + iterator m_begin; + iterator m_end; + }; + + const QList expected { 1, 4, 6, 4 }; + { + QList list { 1, 2, 3, 4 }; + auto future = + QtConcurrent::map(ListRange(list.begin() + 1, list.end() - 1), multiplyBy2InPlace); + future.waitForFinished(); + QCOMPARE(list, expected); + } + + { + QList list { 1, 2, 3, 4 }; + QThreadPool pool; + auto future = QtConcurrent::map(&pool, ListRange(list.begin() + 1, list.end() - 1), + multiplyBy2InPlace); + future.waitForFinished(); + QCOMPARE(list, expected); + } + + { + QList list { 1, 2, 3, 4 }; + QtConcurrent::blockingMap(ListRange(list.begin() + 1, list.end() - 1), multiplyBy2InPlace); + QCOMPARE(list, expected); + } + + { + QList list { 1, 2, 3, 4 }; + QThreadPool pool; + QtConcurrent::blockingMap(&pool, ListRange(list.begin() + 1, list.end() - 1), + multiplyBy2InPlace); + QCOMPARE(list, expected); + } +} + int multiplyBy2(int x) { int y = x * 2; @@ -339,6 +439,23 @@ public: } }; +class MultiplyBy2MoveOnly +{ +public: + MultiplyBy2MoveOnly() = default; + MultiplyBy2MoveOnly(MultiplyBy2MoveOnly &&) = default; + MultiplyBy2MoveOnly &operator=(MultiplyBy2MoveOnly &&other) = default; + + MultiplyBy2MoveOnly(const MultiplyBy2MoveOnly &) = delete; + MultiplyBy2MoveOnly &operator=(const MultiplyBy2MoveOnly &) = delete; + + int operator()(int x) const + { + int y = x * 2; + return y; + } +}; + double intToDouble(int x) { return double(x); @@ -447,6 +564,36 @@ void tst_QtConcurrentMap::mapped() testMapped(stringList, intList, &QString::toInt); CHECK_FAIL("member"); #endif + + { + // non-template sequences + NonTemplateSequence list { 1, 2, 3 }; + + auto future = QtConcurrent::mapped(list, multiplyBy2); + QCOMPARE(future.results(), QList({ 2, 4, 6 })); + + auto result = QtConcurrent::blockingMapped(list, multiplyBy2); + QCOMPARE(result, NonTemplateSequence({ 2, 4, 6 })); + } + + { + // rvalue sequences + auto future = QtConcurrent::mapped(std::vector { 1, 2, 3 }, multiplyBy2); + QCOMPARE(future.results(), QList<int>({ 2, 4, 6 })); + + auto result = QtConcurrent::blockingMapped(std::vector { 1, 2, 3 }, multiplyBy2); + QCOMPARE(result, std::vector<int>({ 2, 4, 6 })); + } + + { + // move only sequences + auto future = QtConcurrent::mapped(MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2); + QCOMPARE(future.results(), QList<int>({ 2, 4, 6 })); + + auto result = QtConcurrent::blockingMapped<std::vector<int>>( + MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2); + QCOMPARE(result, std::vector<int>({ 2, 4, 6 })); + } } static QSemaphore semaphore(1); @@ -531,6 +678,80 @@ void tst_QtConcurrentMap::mappedThreadPool() CHECK_FAIL("function"); testMappedThreadPool(&pool, intList, intListMultipiedBy3, lambdaMultiplyBy3); CHECK_FAIL("lambda"); + + { + // non-template sequences + NonTemplateSequence list { 1, 2, 3 }; + + auto future = QtConcurrent::mapped(&pool, list, multiplyBy2); + QCOMPARE(future.results(), QList({ 2, 4, 6 })); + + auto result = QtConcurrent::blockingMapped(&pool, list, multiplyBy2); + QCOMPARE(result, NonTemplateSequence({ 2, 4, 6 })); + } + + { + // rvalue sequences + auto future = QtConcurrent::mapped(&pool, std::vector { 1, 2, 3 }, multiplyBy2); + QCOMPARE(future.results(), QList<int>({ 2, 4, 6 })); + + auto result = QtConcurrent::blockingMapped<std::vector<int>>(&pool, std::vector { 1, 2, 3 }, + multiplyBy2); + QCOMPARE(result, std::vector<int>({ 2, 4, 6 })); + } + { + // move only sequences + auto future = QtConcurrent::mapped(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2); + QCOMPARE(future.results(), QList<int>({ 2, 4, 6 })); + + auto result = QtConcurrent::blockingMapped<std::vector<int>>( + &pool, MoveOnlyVector<int>({ 1, 2, 3 }), multiplyBy2); + QCOMPARE(result, std::vector<int>({ 2, 4, 6 })); + } +} + +void tst_QtConcurrentMap::mappedWithMoveOnlyCallable() +{ + const QList<int> intList { 1, 2, 3 }; + const QList<int> intListMultipiedBy2 { 2, 4, 6 }; + { + const auto result = QtConcurrent::mapped(intList, MultiplyBy2()).results(); + QCOMPARE(result, intListMultipiedBy2); + } + { + const auto result = + QtConcurrent::mapped(intList.begin(), intList.end(), MultiplyBy2()).results(); + QCOMPARE(result, intListMultipiedBy2); + } + { + const auto result = QtConcurrent::blockingMapped(intList, MultiplyBy2()); + QCOMPARE(result, intListMultipiedBy2); + } + { + const auto result = QtConcurrent::blockingMapped<QList<int>>(intList.begin(), intList.end(), + MultiplyBy2()); + QCOMPARE(result, intListMultipiedBy2); + } + + QThreadPool pool; + { + const auto result = QtConcurrent::mapped(&pool, intList, MultiplyBy2()).results(); + QCOMPARE(result, intListMultipiedBy2); + } + { + const auto result = QtConcurrent::mapped( + &pool, intList.begin(), intList.end(), MultiplyBy2()).results(); + QCOMPARE(result, intListMultipiedBy2); + } + { + const auto result = QtConcurrent::blockingMapped(&pool, intList, MultiplyBy2()); + QCOMPARE(result, intListMultipiedBy2); + } + { + const auto result = QtConcurrent::blockingMapped<QList<int>>(&pool, intList.begin(), + intList.end(), MultiplyBy2()); + QCOMPARE(result, intListMultipiedBy2); + } } int intSquare(int x) @@ -564,32 +785,57 @@ public: template <typename SourceObject, typename ResultObject, typename MapObject, typename ReduceObject> void testMappedReduced(const QList<SourceObject> &sourceObjectList, const ResultObject &expectedResult, MapObject mapObject, ReduceObject reduceObject) { - const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>( - sourceObjectList, mapObject, reduceObject); - QCOMPARE(result1, expectedResult); - - const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject); - QCOMPARE(result2, expectedResult); - - const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( - sourceObjectList, mapObject, reduceObject); - QCOMPARE(result3, expectedResult); + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>( + sourceObjectList, mapObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( + sourceObjectList, mapObject, reduceObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject); + QCOMPARE(result4, expectedResult); + } - const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject); - QCOMPARE(result4, expectedResult); + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::mappedReduced( + sourceObjectList, mapObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::mappedReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingMappedReduced( + sourceObjectList, mapObject, reduceObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingMappedReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject); + QCOMPARE(result4, expectedResult); + } } template <typename SourceObject, typename ResultObject, typename MapObject, typename ReduceObject> void testMappedReduced(const QList<SourceObject> &sourceObjectList, const ResultObject &expectedResult, MapObject mapObject, ReduceObject reduceObject, QtConcurrent::ReduceOptions options) { const ResultObject result1 = QtConcurrent::mappedReduced( - sourceObjectList, mapObject, reduceObject, options); + sourceObjectList, mapObject, reduceObject, options).result(); QCOMPARE(result1, expectedResult); - const ResultObject result2 = QtConcurrent::mappedReduced( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, options); + const ResultObject result2 = + QtConcurrent::mappedReduced(sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, options).result(); QCOMPARE(result2, expectedResult); const ResultObject result3 = QtConcurrent::blockingMappedReduced( @@ -609,7 +855,7 @@ void tst_QtConcurrentMap::mappedReduced() const int sum = 6; const int sumOfSquares = 14; - void (QList<int>::*push_back)(const int &) = &QList<int>::push_back; + void (QList<int>::*push_back)(QList<int>::parameter_type) = &QList<int>::push_back; auto lambdaSquare = [](int x) { return x * x; @@ -657,6 +903,38 @@ void tst_QtConcurrentMap::mappedReduced() CHECK_FAIL("lambda-member"); testMappedReduced(intList, sumOfSquares, lambdaSquare, lambdaSumReduce); CHECK_FAIL("lambda-lambda"); + + { + // non-template sequences + NonTemplateSequence list { 1, 2, 3 }; + + auto future = QtConcurrent::mappedReduced(list, multiplyBy2, intSumReduce); + QCOMPARE(future.result(), 12); + + auto result = QtConcurrent::blockingMappedReduced(list, multiplyBy2, intSumReduce); + QCOMPARE(result, 12); + } + + { + // rvalue sequences + auto future = QtConcurrent::mappedReduced(std::vector { 1, 2, 3 }, intSquare, intSumReduce); + QCOMPARE(future.result(), sumOfSquares); + + auto result = QtConcurrent::blockingMappedReduced(std::vector { 1, 2, 3 }, intSquare, + intSumReduce); + QCOMPARE(result, sumOfSquares); + } + + { + // move only sequences + auto future = + QtConcurrent::mappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }), intSquare, intSumReduce); + QCOMPARE(future.result(), sumOfSquares); + + auto result = QtConcurrent::blockingMappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }), + intSquare, intSumReduce); + QCOMPARE(result, sumOfSquares); + } } template <typename SourceObject, typename ResultObject, typename MapObject, typename ReduceObject> @@ -666,25 +944,57 @@ void testMappedReducedThreadPool(QThreadPool *pool, MapObject mapObject, ReduceObject reduceObject) { - const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>(pool, - sourceObjectList, mapObject, reduceObject); - QCOMPARE(result1, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>(pool, - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject); - QCOMPARE(result2, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>(pool, - sourceObjectList, mapObject, reduceObject); - QCOMPARE(result3, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>( + pool, sourceObjectList, mapObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::mappedReduced<ResultObject>(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), mapObject, + reduceObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( + pool, sourceObjectList, mapObject, reduceObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } - const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>(pool, - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject); - QCOMPARE(result4, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::mappedReduced( + pool, sourceObjectList, mapObject, reduceObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::mappedReduced(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), mapObject, + reduceObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingMappedReduced( + pool, sourceObjectList, mapObject, reduceObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingMappedReduced( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } } int intCube(int x) @@ -743,6 +1053,100 @@ void tst_QtConcurrentMap::mappedReducedThreadPool() CHECK_FAIL("lambda-function"); testMappedReducedThreadPool(&pool, intList, sumOfCubes, lambdaCube, lambdaSumReduce); CHECK_FAIL("lambda-lambda"); + + { + // non-template sequences + NonTemplateSequence list { 1, 2, 3 }; + + auto future = QtConcurrent::mappedReduced(&pool, list, multiplyBy2, intSumReduce); + QCOMPARE(future.result(), 12); + + auto result = QtConcurrent::blockingMappedReduced(&pool, list, multiplyBy2, intSumReduce); + QCOMPARE(result, 12); + } + + { + // rvalue sequences + auto future = + QtConcurrent::mappedReduced(&pool, std::vector { 1, 2, 3 }, intCube, intSumReduce); + QCOMPARE(future.result(), sumOfCubes); + + auto result = QtConcurrent::blockingMappedReduced(&pool, std::vector { 1, 2, 3 }, intCube, + intSumReduce); + QCOMPARE(result, sumOfCubes); + } + + { + // move only sequences + auto future = QtConcurrent::mappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), intCube, + intSumReduce); + QCOMPARE(future.result(), sumOfCubes); + + auto result = QtConcurrent::blockingMappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), + intCube, intSumReduce); + QCOMPARE(result, sumOfCubes); + } + + { + // pool with invalid number of threads + QThreadPool pool; + pool.setMaxThreadCount(0); // explicitly set incorrect value + + // This should not crash + NonTemplateSequence list { 1, 2, 3 }; + auto future = QtConcurrent::mappedReduced(&pool, list, multiplyBy2, intSumReduce); + QCOMPARE(future.result(), 12); + } +} + +void tst_QtConcurrentMap::mappedReducedWithMoveOnlyCallable() +{ + const QList<int> intList { 1, 2, 3 }; + const auto sum = 12; + { + const auto result = QtConcurrent::mappedReduced( + intList, MultiplyBy2(), IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = + QtConcurrent::mappedReduced(intList.begin(), intList.end(), + MultiplyBy2(), IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced(intList, MultiplyBy2(), + IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced( + intList.begin(), intList.end(), MultiplyBy2(), IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } + + QThreadPool pool; + { + const auto result = QtConcurrent::mappedReduced(&pool, intList, MultiplyBy2(), + IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = + QtConcurrent::mappedReduced(&pool, intList.begin(), intList.end(), + MultiplyBy2(), IntSumReduceMoveOnly()).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced(&pool, intList, MultiplyBy2(), + IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced( + &pool, intList.begin(), intList.end(), MultiplyBy2(), IntSumReduceMoveOnly()); + QCOMPARE(result, sum); + } } void tst_QtConcurrentMap::mappedReducedDifferentType() @@ -802,21 +1206,49 @@ void testMappedReducedInitialValue(const QList<SourceObject> &sourceObjectList, ReduceObject reduceObject, InitialObject &&initialObject) { - const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>( - sourceObjectList, mapObject, reduceObject, initialObject); - QCOMPARE(result1, expectedResult); - - const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, initialObject); - QCOMPARE(result2, expectedResult); - - const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( - sourceObjectList, mapObject, reduceObject, initialObject); - QCOMPARE(result3, expectedResult); + // Result type is passed explicitly + { + const ResultObject result1 = + QtConcurrent::mappedReduced<ResultObject>(sourceObjectList, mapObject, reduceObject, + initialObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( + sourceObjectList, mapObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + } - const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, initialObject); - QCOMPARE(result4, expectedResult); + // Result type is deduced + { + const ResultObject result1 = + QtConcurrent::mappedReduced(sourceObjectList, mapObject, reduceObject, + initialObject).result(); + QCOMPARE(result1, expectedResult); + + const ResultObject result2 = QtConcurrent::mappedReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + + const ResultObject result3 = QtConcurrent::blockingMappedReduced( + sourceObjectList, mapObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + + const ResultObject result4 = QtConcurrent::blockingMappedReduced( + sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + } } template <typename SourceObject, typename ResultObject, typename InitialObject, typename MapObject, typename ReduceObject> @@ -828,11 +1260,12 @@ void testMappedReducedInitialValue(const QList<SourceObject> &sourceObjectList, QtConcurrent::ReduceOptions options) { const ResultObject result1 = QtConcurrent::mappedReduced( - sourceObjectList, mapObject, reduceObject, initialObject, options); + sourceObjectList, mapObject, reduceObject, initialObject, options).result(); QCOMPARE(result1, expectedResult); - const ResultObject result2 = QtConcurrent::mappedReduced( - sourceObjectList.constBegin(), sourceObjectList.constEnd(), mapObject, reduceObject, initialObject, options); + const ResultObject result2 = + QtConcurrent::mappedReduced(sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject, options).result(); QCOMPARE(result2, expectedResult); const ResultObject result3 = QtConcurrent::blockingMappedReduced( @@ -857,7 +1290,7 @@ void tst_QtConcurrentMap::mappedReducedInitialValue() const int sumOfSquares = 24; const int intInitial = 10; - void (QList<int>::*push_back)(const int &) = &QList<int>::push_back; + void (QList<int>::*push_back)(QList<int>::parameter_type) = &QList<int>::push_back; auto lambdaSquare = [](int x) { return x * x; @@ -905,6 +1338,40 @@ void tst_QtConcurrentMap::mappedReducedInitialValue() CHECK_FAIL("lambda-member"); testMappedReducedInitialValue(intList, sumOfSquares, lambdaSquare, lambdaSumReduce, intInitial); CHECK_FAIL("lambda-lambda"); + + { + // non-template sequences + NonTemplateSequence list { 1, 2, 3 }; + + auto future = QtConcurrent::mappedReduced(list, multiplyBy2, intSumReduce, intInitial); + QCOMPARE(future.result(), intInitial + 12); + + auto result = + QtConcurrent::blockingMappedReduced(list, multiplyBy2, intSumReduce, intInitial); + QCOMPARE(result, intInitial + 12); + } + + { + // rvalue sequences + auto future = QtConcurrent::mappedReduced(std::vector { 1, 2, 3 }, intSquare, intSumReduce, + intInitial); + QCOMPARE(future.result(), sumOfSquares); + + auto result = QtConcurrent::blockingMappedReduced(std::vector { 1, 2, 3 }, intSquare, + intSumReduce, intInitial); + QCOMPARE(result, sumOfSquares); + } + + { + // move only sequences + auto future = QtConcurrent::mappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }), intSquare, + intSumReduce, intInitial); + QCOMPARE(future.result(), sumOfSquares); + + auto result = QtConcurrent::blockingMappedReduced(MoveOnlyVector<int>({ 1, 2, 3 }), + intSquare, intSumReduce, intInitial); + QCOMPARE(result, sumOfSquares); + } } template <typename SourceObject, typename ResultObject, typename InitialObject, typename MapObject, typename ReduceObject> @@ -915,27 +1382,57 @@ void testMappedReducedInitialValueThreadPool(QThreadPool *pool, ReduceObject reduceObject, InitialObject &&initialObject) { - const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>( - pool, sourceObjectList, mapObject, reduceObject, initialObject); - QCOMPARE(result1, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result2 = QtConcurrent::mappedReduced<ResultObject>( - pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), - mapObject, reduceObject, initialObject); - QCOMPARE(result2, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working - - const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( - pool, sourceObjectList, mapObject, reduceObject, initialObject); - QCOMPARE(result3, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is passed explicitly + { + const ResultObject result1 = QtConcurrent::mappedReduced<ResultObject>( + pool, sourceObjectList, mapObject, reduceObject, initialObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::mappedReduced<ResultObject>(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), mapObject, + reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingMappedReduced<ResultObject>( + pool, sourceObjectList, mapObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } - const ResultObject result4 = QtConcurrent::blockingMappedReduced<ResultObject>( - pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), - mapObject, reduceObject, initialObject); - QCOMPARE(result4, expectedResult); - QCOMPARE(threadCount(), 1); // ensure the only one thread was working + // Result type is deduced + { + const ResultObject result1 = QtConcurrent::mappedReduced( + pool, sourceObjectList, mapObject, reduceObject, initialObject).result(); + QCOMPARE(result1, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result2 = + QtConcurrent::mappedReduced(pool, sourceObjectList.constBegin(), + sourceObjectList.constEnd(), mapObject, + reduceObject, initialObject).result(); + QCOMPARE(result2, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result3 = QtConcurrent::blockingMappedReduced( + pool, sourceObjectList, mapObject, reduceObject, initialObject); + QCOMPARE(result3, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + + const ResultObject result4 = QtConcurrent::blockingMappedReduced( + pool, sourceObjectList.constBegin(), sourceObjectList.constEnd(), + mapObject, reduceObject, initialObject); + QCOMPARE(result4, expectedResult); + QCOMPARE(threadCount(), 1); // ensure the only one thread was working + } } void tst_QtConcurrentMap::mappedReducedInitialValueThreadPool() @@ -990,6 +1487,96 @@ void tst_QtConcurrentMap::mappedReducedInitialValueThreadPool() testMappedReducedInitialValueThreadPool(&pool, intList, sumOfCubes, lambdaCube, lambdaSumReduce, intInitial); CHECK_FAIL("lambda-lambda"); + + { + // non-template sequences + NonTemplateSequence list { 1, 2, 3 }; + + auto future = + QtConcurrent::mappedReduced(&pool, list, multiplyBy2, intSumReduce, intInitial); + QCOMPARE(future.result(), intInitial + 12); + + auto result = QtConcurrent::blockingMappedReduced(&pool, list, multiplyBy2, intSumReduce, + intInitial); + QCOMPARE(result, intInitial + 12); + } + + { + // rvalue sequences + auto future = QtConcurrent::mappedReduced(&pool, std::vector { 1, 2, 3 }, intCube, + intSumReduce, intInitial); + QCOMPARE(future.result(), sumOfCubes); + + auto result = QtConcurrent::blockingMappedReduced(&pool, std::vector { 1, 2, 3 }, intCube, + intSumReduce, intInitial); + QCOMPARE(result, sumOfCubes); + } + + { + // move only sequences + auto future = QtConcurrent::mappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), intCube, + intSumReduce, intInitial); + QCOMPARE(future.result(), sumOfCubes); + + auto result = QtConcurrent::blockingMappedReduced(&pool, MoveOnlyVector<int>({ 1, 2, 3 }), + intCube, intSumReduce, intInitial); + QCOMPARE(result, sumOfCubes); + } +} + +void tst_QtConcurrentMap::mappedReducedInitialValueWithMoveOnlyCallable() +{ + const QList<int> intList { 1, 2, 3 }; + const auto initialValue = 10; + const auto sum = 22; + { + const auto result = + QtConcurrent::mappedReduced(intList, MultiplyBy2(), + IntSumReduceMoveOnly(), initialValue).result(); + QCOMPARE(result, sum); + } + { + const auto result = + QtConcurrent::mappedReduced(intList.begin(), intList.end(), MultiplyBy2(), + IntSumReduceMoveOnly(), initialValue).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced( + intList, MultiplyBy2(), IntSumReduceMoveOnly(), initialValue); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced( + intList.begin(), intList.end(), MultiplyBy2(), IntSumReduceMoveOnly(), + initialValue); + QCOMPARE(result, sum); + } + + QThreadPool pool; + { + const auto result = + QtConcurrent::mappedReduced(&pool, intList, MultiplyBy2(), + IntSumReduceMoveOnly(), initialValue).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::mappedReduced(&pool, intList.begin(), intList.end(), + MultiplyBy2(), IntSumReduceMoveOnly(), + initialValue).result(); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced( + &pool, intList, MultiplyBy2(), IntSumReduceMoveOnly(), initialValue); + QCOMPARE(result, sum); + } + { + const auto result = QtConcurrent::blockingMappedReduced( + &pool, intList.begin(), intList.end(), MultiplyBy2(), IntSumReduceMoveOnly(), + initialValue); + QCOMPARE(result, sum); + } } void tst_QtConcurrentMap::mappedReducedDifferentTypeInitialValue() @@ -1041,6 +1628,51 @@ void tst_QtConcurrentMap::mappedReducedDifferentTypeInitialValue() CHECK_FAIL("lambda-lambda"); } +void tst_QtConcurrentMap::mappedReduceOptionConvertableToResultType() +{ + const QList<int> intList { 1, 2, 3 }; + const int sum = 12; + QThreadPool p; + ReduceOption ro = OrderedReduce; + + // With container + QCOMPARE(QtConcurrent::mappedReduced(intList, multiplyBy2, intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingMappedReduced(intList, multiplyBy2, intSumReduce, ro), sum); + + // With iterators + QCOMPARE(QtConcurrent::mappedReduced(intList.begin(), intList.end(), multiplyBy2, intSumReduce, + ro).result(), sum); + QCOMPARE(QtConcurrent::blockingMappedReduced(intList.begin(), intList.end(), multiplyBy2, + intSumReduce, ro), sum); + + // With custom QThreadPool; + QCOMPARE(QtConcurrent::mappedReduced(&p, intList, multiplyBy2, intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingMappedReduced(&p, intList, multiplyBy2, intSumReduce, ro), sum); + QCOMPARE(QtConcurrent::mappedReduced(&p, intList.begin(), intList.end(), multiplyBy2, + intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingMappedReduced(&p, intList.begin(), intList.end(), multiplyBy2, + intSumReduce, ro), sum); + + // The same as above, but specify the result type explicitly (this invokes different overloads) + QCOMPARE(QtConcurrent::mappedReduced<int>(intList, multiplyBy2, intSumReduce, ro).result(), + sum); + QCOMPARE(QtConcurrent::blockingMappedReduced<int>(intList, multiplyBy2, intSumReduce, ro), sum); + + QCOMPARE(QtConcurrent::mappedReduced<int>(intList.begin(), intList.end(), multiplyBy2, + intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingMappedReduced<int>(intList.begin(), intList.end(), multiplyBy2, + intSumReduce, ro), sum); + + QCOMPARE(QtConcurrent::mappedReduced<int>(&p, intList, multiplyBy2, intSumReduce, ro).result(), + sum); + QCOMPARE(QtConcurrent::blockingMappedReduced<int>(&p, intList, multiplyBy2, intSumReduce, ro), + sum); + QCOMPARE(QtConcurrent::mappedReduced<int>(&p, intList.begin(), intList.end(), multiplyBy2, + intSumReduce, ro).result(), sum); + QCOMPARE(QtConcurrent::blockingMappedReduced<int>(&p, intList.begin(), intList.end(), + multiplyBy2, intSumReduce, ro), sum); +} + int sleeper(int val) { QTest::qSleep(100); @@ -1221,6 +1853,8 @@ public: { currentInstanceCount.fetchAndAddRelaxed(-1);} inline InstanceCounter(const InstanceCounter &) { currentInstanceCount.fetchAndAddRelaxed(1); updatePeak(); } + constexpr InstanceCounter &operator=(const InstanceCounter &) noexcept + { return *this; } void updatePeak() { @@ -1344,7 +1978,7 @@ void tst_QtConcurrentMap::incrementalResults() QCOMPARE(future.isFinished(), true); QCOMPARE(future.resultCount(), count); - QCOMPARE(future.results().count(), count); + QCOMPARE(future.results().size(), count); } /* @@ -1400,15 +2034,15 @@ void tst_QtConcurrentMap::stlContainers() vector.push_back(1); vector.push_back(2); - std::vector<int> vector2 = QtConcurrent::blockingMapped<std::vector<int> >(vector, mapper); - QCOMPARE(vector2.size(), (std::vector<int>::size_type)(2)); + std::vector<int> vector2 = QtConcurrent::blockingMapped(vector, mapper); + QCOMPARE(vector2.size(), 2u); std::list<int> list; list.push_back(1); list.push_back(2); - std::list<int> list2 = QtConcurrent::blockingMapped<std::list<int> >(list, mapper); - QCOMPARE(list2.size(), (std::vector<int>::size_type)(2)); + std::list<int> list2 = QtConcurrent::blockingMapped(list, mapper); + QCOMPARE(list2.size(), 2u); QtConcurrent::mapped(list, mapper).waitForFinished(); @@ -1421,27 +2055,21 @@ void tst_QtConcurrentMap::stlContainersLambda() vector.push_back(1); vector.push_back(2); - std::vector<int> vector2 = QtConcurrent::blockingMapped<std::vector<int> >(vector, - [](const int &i) { - return mapper(i); - } - ); - QCOMPARE(vector2.size(), (std::vector<int>::size_type)(2)); + std::vector<int> vector2 = + QtConcurrent::blockingMapped(vector, [](const int &i) { return mapper(i); }); + QCOMPARE(vector2.size(), 2u); std::list<int> list; list.push_back(1); list.push_back(2); - std::list<int> list2 = QtConcurrent::blockingMapped<std::list<int> >(list, - [](const int &i) { - return mapper(i); - } - ); - QCOMPARE(list2.size(), (std::vector<int>::size_type)(2)); + std::list<int> list2 = + QtConcurrent::blockingMapped(list, [](const int &i) { return mapper(i); }); + QCOMPARE(list2.size(), 2u); QtConcurrent::mapped(list, [](const int &i) { return mapper(i); }).waitForFinished(); - QtConcurrent::blockingMap(list, [](int x) { x *= 2; }); + QtConcurrent::blockingMap(list, []([[maybe_unused]] int x) { x *= 2; }); } InstanceCounter ic_fn(const InstanceCounter & ic) diff --git a/tests/auto/concurrent/qtconcurrentmedian/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentmedian/CMakeLists.txt index 5d7e95049f..63f0135467 100644 --- a/tests/auto/concurrent/qtconcurrentmedian/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrentmedian/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qtconcurrentmedian.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrentmedian Test: ##################################################################### -qt_add_test(tst_qtconcurrentmedian +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentmedian LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentmedian SOURCES tst_qtconcurrentmedian.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent ) diff --git a/tests/auto/concurrent/qtconcurrentmedian/qtconcurrentmedian.pro b/tests/auto/concurrent/qtconcurrentmedian/qtconcurrentmedian.pro deleted file mode 100644 index 59e22d24a1..0000000000 --- a/tests/auto/concurrent/qtconcurrentmedian/qtconcurrentmedian.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrentmedian -QT = core testlib concurrent -SOURCES = tst_qtconcurrentmedian.cpp diff --git a/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp b/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp index 22e555d2db..7eea013c8b 100644 --- a/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp +++ b/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp @@ -1,33 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + #include <qtconcurrentmedian.h> -#include <QtTest/QtTest> +#include <QTest> class tst_QtConcurrentMedian: public QObject { @@ -73,11 +49,11 @@ void tst_QtConcurrentMedian::median_data() void tst_QtConcurrentMedian::median() { - QFETCH(QList<double> , values); + QFETCH(const QList<double> , values); QFETCH(double, expectedMedian); QtConcurrent::Median m; - foreach (double value, values) + for (double value : values) m.addValue(value); QCOMPARE(m.median(), expectedMedian); } diff --git a/tests/auto/concurrent/qtconcurrentrun/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentrun/CMakeLists.txt index 07cb4d360f..a8b6792570 100644 --- a/tests/auto/concurrent/qtconcurrentrun/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrentrun/CMakeLists.txt @@ -1,14 +1,22 @@ -# Generated from qtconcurrentrun.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrentrun Test: ##################################################################### -qt_add_test(tst_qtconcurrentrun +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentrun LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentrun SOURCES tst_qtconcurrentrun.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent + Qt::TestPrivate ) ## Scopes: diff --git a/tests/auto/concurrent/qtconcurrentrun/qtconcurrentrun.pro b/tests/auto/concurrent/qtconcurrentrun/qtconcurrentrun.pro deleted file mode 100644 index 44891b7ba5..0000000000 --- a/tests/auto/concurrent/qtconcurrentrun/qtconcurrentrun.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrentrun -QT = core testlib concurrent -SOURCES = tst_qtconcurrentrun.cpp - -# Force C++17 if available -contains(QT_CONFIG, c++1z): CONFIG += c++1z diff --git a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp index e4b728f950..0bc2961903 100644 --- a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp +++ b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp @@ -1,34 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtconcurrentrun.h> -#include <qfuture.h> +#include <QFuture> +#include <QMutex> +#include <QMutexLocker> #include <QString> -#include <QtTest/QtTest> +#include <QWaitCondition> +#include <QTest> +#include <QTimer> +#include <QFutureSynchronizer> + +#include <QtTest/private/qemulationdetector_p.h> using namespace QtConcurrent; @@ -36,6 +18,7 @@ class tst_QtConcurrentRun: public QObject { Q_OBJECT private slots: + void initTestCase(); void runLightFunction(); void runHeavyFunction(); void returnValue(); @@ -48,14 +31,20 @@ private slots: void recursive(); #ifndef QT_NO_EXCEPTIONS void exceptions(); + void unhandledException(); #endif void functor(); void lambda(); void callableObjectWithState(); void withPromise(); void withPromiseInThreadPool(); + void withPromiseAndThen(); void moveOnlyType(); void crefFunction(); + void customPromise(); + void nonDefaultConstructibleValue(); + void nullThreadPool(); + void nullThreadPoolNoLeak(); }; void light() @@ -97,6 +86,13 @@ void heavy() qDebug("done function"); } +void tst_QtConcurrentRun::initTestCase() +{ + // proxy check for QEMU; catches slightly more though + if (QTestPrivate::isRunningArmOnX86()) + QSKIP("Runs into spurious crashes on QEMU -- QTBUG-106906"); +} + void tst_QtConcurrentRun::runLightFunction() { qDebug("starting function"); @@ -121,14 +117,14 @@ void tst_QtConcurrentRun::runLightFunction() void (*f3)(QPromise<int> &) = lightOverloaded; qDebug("starting function with promise"); - QFuture<void> future3 = runWithPromise(f3); + QFuture<int> future3 = run(f3); qDebug("waiting"); future3.waitForFinished(); qDebug("done"); void (*f4)(QPromise<double> &, int v) = lightOverloaded; qDebug("starting function with promise and with arg"); - QFuture<void> future4 = runWithPromise(f4, 2); + QFuture<double> future4 = run(f4, 2); qDebug("waiting"); future4.waitForFinished(); qDebug("done"); @@ -428,141 +424,141 @@ void tst_QtConcurrentRun::reportValueWithPromise() QThreadPool pool; QFuture<int> f; - f = runWithPromise(reportInt0); + f = run(reportInt0); QCOMPARE(f.result(), 0); - f = runWithPromise(&pool, reportInt0); + f = run(&pool, reportInt0); QCOMPARE(f.result(), 0); - f = runWithPromise(reportIntPlusOne, 5); + f = run(reportIntPlusOne, 5); QCOMPARE(f.result(), 6); - f = runWithPromise(&pool, reportIntPlusOne, 5); + f = run(&pool, reportIntPlusOne, 5); QCOMPARE(f.result(), 6); AWithPromise a; - f = runWithPromise(&AWithPromise::member0, &a); + f = run(&AWithPromise::member0, &a); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &AWithPromise::member0, &a); + f = run(&pool, &AWithPromise::member0, &a); QCOMPARE(f.result(), 10); - f = runWithPromise(&AWithPromise::member1, &a, 20); + f = run(&AWithPromise::member1, &a, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &AWithPromise::member1, &a, 20); + f = run(&pool, &AWithPromise::member1, &a, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&AWithPromise::member0, a); + f = run(&AWithPromise::member0, a); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &AWithPromise::member0, a); + f = run(&pool, &AWithPromise::member0, a); QCOMPARE(f.result(), 10); - f = runWithPromise(&AWithPromise::member1, a, 20); + f = run(&AWithPromise::member1, a, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &AWithPromise::member1, a, 20); + f = run(&pool, &AWithPromise::member1, a, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(a); + f = run(a); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, a); + f = run(&pool, a); QCOMPARE(f.result(), 10); - f = runWithPromise(std::ref(a)); + f = run(std::ref(a)); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, std::ref(a)); + f = run(&pool, std::ref(a)); QCOMPARE(f.result(), 10); const AConstWithPromise aConst = AConstWithPromise(); - f = runWithPromise(&AConstWithPromise::member0, &aConst); + f = run(&AConstWithPromise::member0, &aConst); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &AConstWithPromise::member0, &aConst); + f = run(&pool, &AConstWithPromise::member0, &aConst); QCOMPARE(f.result(), 10); - f = runWithPromise(&AConstWithPromise::member1, &aConst, 20); + f = run(&AConstWithPromise::member1, &aConst, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &AConstWithPromise::member1, &aConst, 20); + f = run(&pool, &AConstWithPromise::member1, &aConst, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&AConstWithPromise::member0, aConst); + f = run(&AConstWithPromise::member0, aConst); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &AConstWithPromise::member0, aConst); + f = run(&pool, &AConstWithPromise::member0, aConst); QCOMPARE(f.result(), 10); - f = runWithPromise(&AConstWithPromise::member1, aConst, 20); + f = run(&AConstWithPromise::member1, aConst, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &AConstWithPromise::member1, aConst, 20); + f = run(&pool, &AConstWithPromise::member1, aConst, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(aConst); + f = run(aConst); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, aConst); + f = run(&pool, aConst); QCOMPARE(f.result(), 10); - f = runWithPromise(std::ref(a)); + f = run(std::ref(a)); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, std::ref(a)); + f = run(&pool, std::ref(a)); QCOMPARE(f.result(), 10); ANoExceptWithPromise aNoExcept; - f = runWithPromise(&ANoExceptWithPromise::member0, &aNoExcept); + f = run(&ANoExceptWithPromise::member0, &aNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &ANoExceptWithPromise::member0, &aNoExcept); + f = run(&pool, &ANoExceptWithPromise::member0, &aNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&ANoExceptWithPromise::member1, &aNoExcept, 20); + f = run(&ANoExceptWithPromise::member1, &aNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &ANoExceptWithPromise::member1, &aNoExcept, 20); + f = run(&pool, &ANoExceptWithPromise::member1, &aNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&ANoExceptWithPromise::member0, aNoExcept); + f = run(&ANoExceptWithPromise::member0, aNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &ANoExceptWithPromise::member0, aNoExcept); + f = run(&pool, &ANoExceptWithPromise::member0, aNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&ANoExceptWithPromise::member1, aNoExcept, 20); + f = run(&ANoExceptWithPromise::member1, aNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &ANoExceptWithPromise::member1, aNoExcept, 20); + f = run(&pool, &ANoExceptWithPromise::member1, aNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(aNoExcept); + f = run(aNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, aNoExcept); + f = run(&pool, aNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(std::ref(aNoExcept)); + f = run(std::ref(aNoExcept)); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, std::ref(aNoExcept)); + f = run(&pool, std::ref(aNoExcept)); QCOMPARE(f.result(), 10); const AConstNoExceptWithPromise aConstNoExcept = AConstNoExceptWithPromise(); - f = runWithPromise(&AConstNoExceptWithPromise::member0, &aConstNoExcept); + f = run(&AConstNoExceptWithPromise::member0, &aConstNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &AConstNoExceptWithPromise::member0, &aConstNoExcept); + f = run(&pool, &AConstNoExceptWithPromise::member0, &aConstNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); + f = run(&AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); + f = run(&pool, &AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&AConstNoExceptWithPromise::member0, aConstNoExcept); + f = run(&AConstNoExceptWithPromise::member0, aConstNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, &AConstNoExceptWithPromise::member0, aConstNoExcept); + f = run(&pool, &AConstNoExceptWithPromise::member0, aConstNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&AConstNoExceptWithPromise::member1, aConstNoExcept, 20); + f = run(&AConstNoExceptWithPromise::member1, aConstNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(&pool, &AConstNoExceptWithPromise::member1, aConstNoExcept, 20); + f = run(&pool, &AConstNoExceptWithPromise::member1, aConstNoExcept, 20); QCOMPARE(f.result(), 20); - f = runWithPromise(aConstNoExcept); + f = run(aConstNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, aConstNoExcept); + f = run(&pool, aConstNoExcept); QCOMPARE(f.result(), 10); - f = runWithPromise(std::ref(aConstNoExcept)); + f = run(std::ref(aConstNoExcept)); QCOMPARE(f.result(), 10); - f = runWithPromise(&pool, std::ref(aConstNoExcept)); + f = run(&pool, std::ref(aConstNoExcept)); QCOMPARE(f.result(), 10); } @@ -670,10 +666,10 @@ void tst_QtConcurrentRun::implicitConvertibleTypes() { QThreadPool pool; - double d; + double d = 0.0; run(doubleFunction, d).waitForFinished(); run(&pool, doubleFunction, d).waitForFinished(); - int i; + int i = 0; run(doubleFunction, d).waitForFinished(); run(&pool, doubleFunction, d).waitForFinished(); run(doubleFunction, i).waitForFinished(); @@ -819,7 +815,7 @@ public: void run() override { int iter = 60; while (--iter && !cancel.loadRelaxed()) - QThread::currentThread()->msleep(25); + QThread::currentThread()->sleep(std::chrono::milliseconds{25}); } }; @@ -890,6 +886,25 @@ void tst_QtConcurrentRun::exceptions() QVERIFY2(caught, "did not get exception"); } + +void tst_QtConcurrentRun::unhandledException() +{ + struct Exception {}; + bool caught = false; + try { + auto f = QtConcurrent::run([] { throw Exception {}; }); + f.waitForFinished(); + } catch (const QUnhandledException &e) { + try { + if (e.exception()) + std::rethrow_exception(e.exception()); + } catch (const Exception &) { + caught = true; + } + } + + QVERIFY(caught); +} #endif // Compiler supports decltype @@ -940,14 +955,14 @@ void tst_QtConcurrentRun::functor() } FunctorWithPromise fWithPromise; { - QtConcurrent::runWithPromise(fWithPromise, 1.5).waitForFinished(); + QtConcurrent::run(fWithPromise, 1.5).waitForFinished(); } OverloadedFunctorWithPromise ofWithPromise; { - QtConcurrent::runWithPromise<int>(ofWithPromise).waitForFinished(); - QtConcurrent::runWithPromise<double>(ofWithPromise).waitForFinished(); - QtConcurrent::runWithPromise<int>(ofWithPromise, 1).waitForFinished(); - QtConcurrent::runWithPromise<double>(ofWithPromise, 1).waitForFinished(); + QtConcurrent::run<int>(ofWithPromise).waitForFinished(); + QtConcurrent::run<double>(ofWithPromise).waitForFinished(); + QtConcurrent::run<int>(ofWithPromise, 1).waitForFinished(); + QtConcurrent::run<double>(ofWithPromise, 1).waitForFinished(); } // and now with explicit pool: QThreadPool pool; @@ -975,13 +990,17 @@ void tst_QtConcurrentRun::functor() // Compiler supports lambda void tst_QtConcurrentRun::lambda() { - QCOMPARE(QtConcurrent::run([](){ return 45; }).result(), 45); - QCOMPARE(QtConcurrent::run([](int a){ return a+15; }, 12).result(), 12+15); - QCOMPARE(QtConcurrent::run([](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); - QCOMPARE(QtConcurrent::run([](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); + QCOMPARE(QtConcurrent::run([]() { return 45; }).result(), 45); + QCOMPARE(QtConcurrent::run([](int a) { return a+15; }, 12).result(), 12+15); + QCOMPARE(QtConcurrent::run([](int a, double b) { return a + b; }, 12, 15).result(), + double(12+15)); + QCOMPARE(QtConcurrent::run([](int a , int, int, int, int b) + { return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); - QCOMPARE(QtConcurrent::runWithPromise([](QPromise<int> &promise){ promise.addResult(45); }).result(), 45); - QCOMPARE(QtConcurrent::runWithPromise([](QPromise<int> &promise, double input){ promise.addResult(input / 2.0); }, 15.0).result(), 7); + QCOMPARE(QtConcurrent::run([](QPromise<int> &promise) + { promise.addResult(45); }).result(), 45); + QCOMPARE(QtConcurrent::run([](QPromise<int> &promise, double input) + { promise.addResult(input / 2.0); }, 15.0).result(), 7); { QString str { "Hello World Foo" }; @@ -992,10 +1011,12 @@ void tst_QtConcurrentRun::lambda() // and now with explicit pool: QThreadPool pool; - QCOMPARE(QtConcurrent::run(&pool, [](){ return 45; }).result(), 45); - QCOMPARE(QtConcurrent::run(&pool, [](int a){ return a+15; }, 12).result(), 12+15); - QCOMPARE(QtConcurrent::run(&pool, [](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); - QCOMPARE(QtConcurrent::run(&pool, [](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); + QCOMPARE(QtConcurrent::run(&pool, []() { return 45; }).result(), 45); + QCOMPARE(QtConcurrent::run(&pool, [](int a) { return a + 15; }, 12).result(), 12 + 15); + QCOMPARE(QtConcurrent::run(&pool, [](int a, double b) + { return a + b; }, 12, 15).result(), double(12 + 15)); + QCOMPARE(QtConcurrent::run(&pool, [](int a , int, int, int, int b) + { return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); { QString str { "Hello World Foo" }; @@ -1045,19 +1066,20 @@ void tst_QtConcurrentRun::callableObjectWithState() CallableWithStateWithPromise oWithPromise; // Run method setNewState explicitly - runWithPromise(&CallableWithStateWithPromise::setNewState, &oWithPromise, CallableWithStateWithPromise::defaultState() + 1).waitForFinished(); + run(&CallableWithStateWithPromise::setNewState, &oWithPromise, + CallableWithStateWithPromise::defaultState() + 1).waitForFinished(); QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 1); // Run operator()(int) explicitly - runWithPromise(std::ref(oWithPromise), CallableWithStateWithPromise::defaultState() + 2).waitForFinished(); + run(std::ref(oWithPromise), CallableWithStateWithPromise::defaultState() + 2).waitForFinished(); QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 2); // Run on a copy of object (original object remains unchanged) - runWithPromise(oWithPromise, CallableWithStateWithPromise::defaultState() + 3).waitForFinished(); + run(oWithPromise, CallableWithStateWithPromise::defaultState() + 3).waitForFinished(); QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 2); // Explicitly run on a temporary object - QCOMPARE(runWithPromise(CallableWithStateWithPromise(), 15).result(), 15); + QCOMPARE(run(CallableWithStateWithPromise(), 15).result(), 15); } void report3(QPromise<int> &promise) @@ -1067,12 +1089,25 @@ void report3(QPromise<int> &promise) promise.addResult(1); } +static void staticReport3(QPromise<int> &promise) +{ + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); +} + void reportN(QPromise<double> &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); } +static void staticReportN(QPromise<double> &promise, int n) +{ + for (int i = 0; i < n; ++i) + promise.addResult(0); +} + void reportString1(QPromise<QString> &promise, const QString &s) { promise.addResult(s); @@ -1141,40 +1176,50 @@ public: void tst_QtConcurrentRun::withPromise() { // free function pointer - QCOMPARE(runWithPromise(&report3).results(), + QCOMPARE(run(&report3).results(), QList<int>({0, 2, 1})); - QCOMPARE(runWithPromise(report3).results(), + QCOMPARE(run(report3).results(), QList<int>({0, 2, 1})); - QCOMPARE(runWithPromise(reportN, 4).results(), - QList<double>({0, 0, 0, 0})); - QCOMPARE(runWithPromise(reportN, 2).results(), + QCOMPARE(run(&staticReport3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(run(staticReport3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(run(&reportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(reportN, 2).results(), + QList<double>({0, 0})); + + QCOMPARE(run(&staticReportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(staticReportN, 2).results(), QList<double>({0, 0})); QString s = QLatin1String("string"); const QString &crs = QLatin1String("cr string"); const QString cs = QLatin1String("c string"); - QCOMPARE(runWithPromise(reportString1, s).results(), + QCOMPARE(run(reportString1, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(reportString1, crs).results(), + QCOMPARE(run(reportString1, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(reportString1, cs).results(), + QCOMPARE(run(reportString1, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(reportString1, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(reportString1, QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(runWithPromise(reportString2, s).results(), + QCOMPARE(run(reportString2, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(reportString2, crs).results(), + QCOMPARE(run(reportString2, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(reportString2, cs).results(), + QCOMPARE(run(reportString2, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(reportString2, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(reportString2, QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); // lambda - QCOMPARE(runWithPromise([](QPromise<double> &promise, int n) { + QCOMPARE(run([](QPromise<double> &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); }, 3).results(), @@ -1185,46 +1230,46 @@ void tst_QtConcurrentRun::withPromise() for (int i = 0; i < n; ++i) promise.addResult(0); }; - QCOMPARE(runWithPromise(fun, 2).results(), + QCOMPARE(run(fun, 2).results(), QList<double>({0, 0})); // operator() - QCOMPARE(runWithPromise(Callable(), 3).results(), + QCOMPARE(run(Callable(), 3).results(), QList<double>({0, 0, 0})); const Callable c{}; - QCOMPARE(runWithPromise(c, 2).results(), + QCOMPARE(run(c, 2).results(), QList<double>({0, 0})); // static member functions - QCOMPARE(runWithPromise(&MyObject::staticMember0).results(), + QCOMPARE(run(&MyObject::staticMember0).results(), QList<double>({0, 2, 1})); - QCOMPARE(runWithPromise(&MyObject::staticMember1, 2).results(), + QCOMPARE(run(&MyObject::staticMember1, 2).results(), QList<double>({0, 0})); // member functions const MyObject obj{}; - QCOMPARE(runWithPromise(&MyObject::member0, &obj).results(), + QCOMPARE(run(&MyObject::member0, &obj).results(), QList<double>({0, 2, 1})); - QCOMPARE(runWithPromise(&MyObject::member1, &obj, 4).results(), + QCOMPARE(run(&MyObject::member1, &obj, 4).results(), QList<double>({0, 0, 0, 0})); - QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, s).results(), + QCOMPARE(run(&MyObject::memberString1, &obj, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, crs).results(), + QCOMPARE(run(&MyObject::memberString1, &obj, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, cs).results(), + QCOMPARE(run(&MyObject::memberString1, &obj, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, s).results(), + QCOMPARE(run(&MyObject::memberString2, &obj, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, crs).results(), + QCOMPARE(run(&MyObject::memberString2, &obj, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, cs).results(), + QCOMPARE(run(&MyObject::memberString2, &obj, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); MyObject nonConstObj{}; - QCOMPARE(runWithPromise(&MyObject::nonConstMember, &nonConstObj).results(), + QCOMPARE(run(&MyObject::nonConstMember, &nonConstObj).results(), QList<double>({0, 2, 1})); } @@ -1232,40 +1277,50 @@ void tst_QtConcurrentRun::withPromiseInThreadPool() { QScopedPointer<QThreadPool> pool(new QThreadPool); // free function pointer - QCOMPARE(runWithPromise(pool.data(), &report3).results(), + QCOMPARE(run(pool.data(), &report3).results(), QList<int>({0, 2, 1})); - QCOMPARE(runWithPromise(pool.data(), report3).results(), + QCOMPARE(run(pool.data(), report3).results(), QList<int>({0, 2, 1})); - QCOMPARE(runWithPromise(pool.data(), reportN, 4).results(), - QList<double>({0, 0, 0, 0})); - QCOMPARE(runWithPromise(pool.data(), reportN, 2).results(), + QCOMPARE(run(pool.data(), &staticReport3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(run(pool.data(), staticReport3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(run(pool.data(), &reportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(pool.data(), reportN, 2).results(), + QList<double>({0, 0})); + + QCOMPARE(run(pool.data(), &staticReportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(pool.data(), staticReportN, 2).results(), QList<double>({0, 0})); QString s = QLatin1String("string"); const QString &crs = QLatin1String("cr string"); const QString cs = QLatin1String("c string"); - QCOMPARE(runWithPromise(pool.data(), reportString1, s).results(), + QCOMPARE(run(pool.data(), reportString1, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(pool.data(), reportString1, crs).results(), + QCOMPARE(run(pool.data(), reportString1, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(pool.data(), reportString1, cs).results(), + QCOMPARE(run(pool.data(), reportString1, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(runWithPromise(pool.data(), reportString2, s).results(), + QCOMPARE(run(pool.data(), reportString2, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(pool.data(), reportString2, crs).results(), + QCOMPARE(run(pool.data(), reportString2, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(pool.data(), reportString2, cs).results(), + QCOMPARE(run(pool.data(), reportString2, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); // lambda - QCOMPARE(runWithPromise(pool.data(), [](QPromise<double> &promise, int n) { + QCOMPARE(run(pool.data(), [](QPromise<double> &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); }, 3).results(), @@ -1276,46 +1331,153 @@ void tst_QtConcurrentRun::withPromiseInThreadPool() for (int i = 0; i < n; ++i) promise.addResult(0); }; - QCOMPARE(runWithPromise(pool.data(), fun, 2).results(), + QCOMPARE(run(pool.data(), fun, 2).results(), QList<double>({0, 0})); // operator() - QCOMPARE(runWithPromise(pool.data(), Callable(), 3).results(), + QCOMPARE(run(pool.data(), Callable(), 3).results(), QList<double>({0, 0, 0})); const Callable c{}; - QCOMPARE(runWithPromise(pool.data(), c, 2).results(), + QCOMPARE(run(pool.data(), c, 2).results(), QList<double>({0, 0})); // static member functions - QCOMPARE(runWithPromise(pool.data(), &MyObject::staticMember0).results(), + QCOMPARE(run(pool.data(), &MyObject::staticMember0).results(), QList<double>({0, 2, 1})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::staticMember1, 2).results(), + QCOMPARE(run(pool.data(), &MyObject::staticMember1, 2).results(), QList<double>({0, 0})); // member functions const MyObject obj{}; - QCOMPARE(runWithPromise(pool.data(), &MyObject::member0, &obj).results(), + QCOMPARE(run(pool.data(), &MyObject::member0, &obj).results(), QList<double>({0, 2, 1})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::member1, &obj, 4).results(), + QCOMPARE(run(pool.data(), &MyObject::member1, &obj, 4).results(), QList<double>({0, 0, 0, 0})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, s).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, crs).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, cs).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, + QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, s).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, s).results(), QList<QString>({s})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, crs).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, crs).results(), QList<QString>({crs})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, cs).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, cs).results(), QList<QString>({cs})); - QCOMPARE(runWithPromise(pool.data(), &MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, + QString(QLatin1String("rvalue"))).results(), QList<QString>({QString(QLatin1String("rvalue"))})); } +void tst_QtConcurrentRun::withPromiseAndThen() +{ + bool runExecuted = false; + bool cancelReceivedBeforeSync = false; + bool cancelReceivedAfterSync = false; + + bool syncBegin = false; + bool syncEnd = false; + + QMutex mutex; + QWaitCondition condition; + + auto reset = [&]() { + runExecuted = false; + cancelReceivedBeforeSync = false; + cancelReceivedAfterSync = false; + syncBegin = false; + syncEnd = false; + }; + + auto setFlag = [&mutex, &condition] (bool &flag) { + QMutexLocker locker(&mutex); + flag = true; + condition.wakeOne(); + }; + + auto waitForFlag = [&mutex, &condition] (const bool &flag) { + QMutexLocker locker(&mutex); + while (!flag) + condition.wait(&mutex); + }; + + auto report1WithCancel = [&](QPromise<int> &promise) { + runExecuted = true; + cancelReceivedBeforeSync = promise.isCanceled(); + + setFlag(syncBegin); + waitForFlag(syncEnd); + + cancelReceivedAfterSync = promise.isCanceled(); + if (cancelReceivedAfterSync) + return; + promise.addResult(1); + }; + + { + auto future = run(report1WithCancel); + + waitForFlag(syncBegin); + future.cancel(); + setFlag(syncEnd); + + future.waitForFinished(); + QCOMPARE(future.results().size(), 0); + QVERIFY(runExecuted); + QVERIFY(!cancelReceivedBeforeSync); + QVERIFY(cancelReceivedAfterSync); + } + + reset(); + + { + bool thenExecuted = false; + bool cancelExecuted = false; + auto future = run(report1WithCancel); + auto resultFuture = future.then(QtFuture::Launch::Async, [&](int) { thenExecuted = true; }) + .onCanceled([&]() { cancelExecuted = true; }); + + waitForFlag(syncBegin); + // no cancel this time + setFlag(syncEnd); + + resultFuture.waitForFinished(); + QCOMPARE(future.results().size(), 1); + QCOMPARE(future.result(), 1); + QVERIFY(runExecuted); + QVERIFY(thenExecuted); + QVERIFY(!cancelExecuted); + QVERIFY(!cancelReceivedBeforeSync); + QVERIFY(!cancelReceivedAfterSync); + } + + reset(); + + { + bool thenExecuted = false; + bool cancelExecuted = false; + auto future = run(report1WithCancel); + auto resultFuture = future.then(QtFuture::Launch::Async, [&](int) { thenExecuted = true; }) + .onCanceled([&]() { cancelExecuted = true; }); + + waitForFlag(syncBegin); + future.cancel(); + setFlag(syncEnd); + + resultFuture.waitForFinished(); + QCOMPARE(future.results().size(), 0); + QVERIFY(runExecuted); + QVERIFY(!thenExecuted); + QVERIFY(cancelExecuted); + QVERIFY(!cancelReceivedBeforeSync); + QVERIFY(cancelReceivedAfterSync); + } +} + class MoveOnlyType { public: @@ -1337,43 +1499,135 @@ public: void tst_QtConcurrentRun::moveOnlyType() { - QCOMPARE(runWithPromise(MoveOnlyCallable(), MoveOnlyType()).results(), + QCOMPARE(run(MoveOnlyCallable(), MoveOnlyType()).results(), QList<int>({1})); } void tst_QtConcurrentRun::crefFunction() { - // free function pointer with promise - auto fun = &report3; - QCOMPARE(runWithPromise(std::cref(fun)).results(), - QList<int>({0, 2, 1})); + { + // free function pointer with promise + auto fun = &returnInt0; + QCOMPARE(run(std::cref(fun)).result(), 10); + + // lambda with promise + auto lambda = [](int n) { + return 2 * n; + }; + QCOMPARE(run(std::cref(lambda), 3).result(), 6); + + // std::function with promise + const std::function<int(int)> funObj = [](int n) { + return 2 * n; + }; + QCOMPARE(run(std::cref(funObj), 2).result(), 4); + + // callable with promise + const AConst c{}; + QCOMPARE(run(std::cref(c), 2).result(), 2); + + // member functions with promise + auto member = &AConst::member0; + const AConst obj{}; + QCOMPARE(run(std::cref(member), &obj).result(), 10); + } - // lambda with promise - auto lambda = [](QPromise<double> &promise, int n) { - for (int i = 0; i < n; ++i) - promise.addResult(0); - }; - QCOMPARE(runWithPromise(std::cref(lambda), 3).results(), - QList<double>({0, 0, 0})); + { + // free function pointer with promise + auto fun = &report3; + QCOMPARE(run(std::cref(fun)).results(), + QList<int>({0, 2, 1})); + + // lambda with promise + auto lambda = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(run(std::cref(lambda), 3).results(), + QList<double>({0, 0, 0})); + + // std::function with promise + const std::function<void(QPromise<double> &, int)> funObj = + [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(run(std::cref(funObj), 2).results(), + QList<double>({0, 0})); + + // callable with promise + const Callable c{}; + QCOMPARE(run(std::cref(c), 2).results(), + QList<double>({0, 0})); + + // member functions with promise + auto member = &MyObject::member0; + const MyObject obj{}; + QCOMPARE(run(std::cref(member), &obj).results(), + QList<double>({0, 2, 1})); + } +} - // std::function with promise - const std::function<void(QPromise<double> &, int)> funObj = [](QPromise<double> &promise, int n) { - for (int i = 0; i < n; ++i) - promise.addResult(0); +int explicitPromise(QPromise<int> &promise, int &i) +{ + promise.setProgressRange(-10, 10); + ++i; + return i * 2; +} + +void tst_QtConcurrentRun::customPromise() +{ + QPromise<int> p; + int i = 4; + QCOMPARE(QtConcurrent::run(explicitPromise, std::ref(p), std::ref(i)).result(), 10); + QCOMPARE(i, 5); + QCOMPARE(p.future().progressMinimum(), -10); + QCOMPARE(p.future().progressMaximum(), 10); +} + +void tst_QtConcurrentRun::nonDefaultConstructibleValue() +{ + struct NonDefaultConstructible + { + explicit NonDefaultConstructible(int v) : value(v) { } + int value = 0; }; - QCOMPARE(runWithPromise(std::cref(funObj), 2).results(), - QList<double>({0, 0})); - // callable with promise - const Callable c{}; - QCOMPARE(runWithPromise(std::cref(c), 2).results(), - QList<double>({0, 0})); + auto future = QtConcurrent::run([] { return NonDefaultConstructible(42); }); + QCOMPARE(future.result().value, 42); +} - // member functions with promise - auto member = &MyObject::member0; - const MyObject obj{}; - QCOMPARE(runWithPromise(std::cref(member), &obj).results(), - QList<double>({0, 2, 1})); +// QTBUG-98901 +void tst_QtConcurrentRun::nullThreadPool() +{ + QThreadPool *pool = nullptr; + std::atomic<bool> isInvoked = false; + auto future = run(pool, [&] { isInvoked = true; }); + future.waitForFinished(); + QVERIFY(future.isCanceled()); + QVERIFY(!isInvoked); +} + +struct LifetimeChecker +{ + LifetimeChecker() { ++count; } + LifetimeChecker(const LifetimeChecker &) { ++count; } + ~LifetimeChecker() { --count; } + + void operator()() { } + + static std::atomic<int> count; +}; +std::atomic<int> LifetimeChecker::count = 0; + +void tst_QtConcurrentRun::nullThreadPoolNoLeak() +{ + { + QThreadPool *pool = nullptr; + auto future = run(pool, LifetimeChecker()); + future.waitForFinished(); + } + QCOMPARE(LifetimeChecker::count, 0); } QTEST_MAIN(tst_QtConcurrentRun) diff --git a/tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt b/tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt index 23a0d45e83..89226eaacc 100644 --- a/tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrenttask/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qtconcurrenttask.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrenttask Test: ##################################################################### -qt_add_test(tst_qtconcurrenttask +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrenttask LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrenttask SOURCES tst_qtconcurrenttask.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent ) diff --git a/tests/auto/concurrent/qtconcurrenttask/qtconcurrenttask.pro b/tests/auto/concurrent/qtconcurrenttask/qtconcurrenttask.pro deleted file mode 100644 index 4404efa0b3..0000000000 --- a/tests/auto/concurrent/qtconcurrenttask/qtconcurrenttask.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrenttask -QT = core testlib concurrent -SOURCES = tst_qtconcurrenttask.cpp diff --git a/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp b/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp index f4e6f99660..d570b0f974 100644 --- a/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp +++ b/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp @@ -1,34 +1,12 @@ -/**************************************************************************** -** -** 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 <qtconcurrenttask.h> -#include <QtTest/QtTest> +#include <QTest> +#include <QSemaphore> + +#include <random> class tst_QtConcurrentTask : public QObject { @@ -41,9 +19,11 @@ private Q_SLOTS: void taskWithLambda(); void taskWithArguments(); void useCustomThreadPool(); + void setPriority_data(); void setPriority(); void adjustAllSettings(); void ignoreFutureResult(); + void withPromise(); }; using namespace QtConcurrent; @@ -52,13 +32,14 @@ void tst_QtConcurrentTask::taskWithFreeFunction() { QVariant value(42); - auto result = task(&qvariant_cast<int>) + auto result = task([](const QVariant &var){ return qvariant_cast<int>(var); }) .withArguments(value) .spawn() .result(); QCOMPARE(result, 42); } + void tst_QtConcurrentTask::taskWithClassMethod() { QString result("foobar"); @@ -67,6 +48,7 @@ void tst_QtConcurrentTask::taskWithClassMethod() QCOMPARE(result, "foo"); } + void tst_QtConcurrentTask::taskWithCallableObject() { QCOMPARE(task(std::plus<int>()) @@ -100,8 +82,18 @@ void tst_QtConcurrentTask::useCustomThreadPool() QCOMPARE(result, 42); } +void tst_QtConcurrentTask::setPriority_data() +{ + QTest::addColumn<bool>("runWithPromise"); + + QTest::addRow("without promise") << false; + QTest::addRow("with promise") << true; +} + void tst_QtConcurrentTask::setPriority() { + QFETCH(bool, runWithPromise); + QThreadPool pool; pool.setMaxThreadCount(1); @@ -115,15 +107,25 @@ void tst_QtConcurrentTask::setPriority() const int tasksCount = 10; QList<int> priorities(tasksCount); std::iota(priorities.begin(), priorities.end(), 1); - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + auto seed = std::random_device {}(); std::shuffle(priorities.begin(), priorities.end(), std::default_random_engine(seed)); + qDebug() << "Generated priorities list" << priorities << "using seed" << seed; + QList<int> actual; - for (int priority : priorities) - futureResults << task([priority, &actual] { actual << priority; }) - .onThreadPool(pool) - .withPriority(priority) - .spawn(); + for (int priority : priorities) { + if (runWithPromise) { + futureResults << task([priority, &actual] (QPromise<void> &) { actual << priority; }) + .onThreadPool(pool) + .withPriority(priority) + .spawn(); + } else { + futureResults << task([priority, &actual] { actual << priority; }) + .onThreadPool(pool) + .withPriority(priority) + .spawn(); + } + } sem.release(); pool.waitForDone(); @@ -156,6 +158,7 @@ void tst_QtConcurrentTask::adjustAllSettings() QCOMPARE(result, QList<int>({ 1, 2, 3 })); } + void tst_QtConcurrentTask::ignoreFutureResult() { QThreadPool pool; @@ -171,5 +174,23 @@ void tst_QtConcurrentTask::ignoreFutureResult() QCOMPARE(value, 10); } +void incrementWithPromise(QPromise<int> &promise, int i) +{ + promise.addResult(i + 1); +} + +void return0WithPromise(QPromise<int> &promise) +{ + promise.addResult(0); +} + +void tst_QtConcurrentTask::withPromise() +{ + QCOMPARE(task(&return0WithPromise).spawn().result(), 0); + QCOMPARE(task(&return0WithPromise).withPriority(7).spawn().result(), 0); + QCOMPARE(task(&incrementWithPromise).withArguments(1).spawn().result(), 2); + QCOMPARE(task(&incrementWithPromise).withArguments(1).withPriority(7).spawn().result(), 2); +} + QTEST_MAIN(tst_QtConcurrentTask) #include "tst_qtconcurrenttask.moc" diff --git a/tests/auto/concurrent/qtconcurrentthreadengine/CMakeLists.txt b/tests/auto/concurrent/qtconcurrentthreadengine/CMakeLists.txt index 9c24dd1662..c3c8c9ea59 100644 --- a/tests/auto/concurrent/qtconcurrentthreadengine/CMakeLists.txt +++ b/tests/auto/concurrent/qtconcurrentthreadengine/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qtconcurrentthreadengine.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtconcurrentthreadengine Test: ##################################################################### -qt_add_test(tst_qtconcurrentthreadengine +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtconcurrentthreadengine LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtconcurrentthreadengine SOURCES tst_qtconcurrentthreadengine.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent ) diff --git a/tests/auto/concurrent/qtconcurrentthreadengine/qtconcurrentthreadengine.pro b/tests/auto/concurrent/qtconcurrentthreadengine/qtconcurrentthreadengine.pro deleted file mode 100644 index b98b8f56e8..0000000000 --- a/tests/auto/concurrent/qtconcurrentthreadengine/qtconcurrentthreadengine.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtconcurrentthreadengine -QT = core testlib concurrent -SOURCES = tst_qtconcurrentthreadengine.cpp diff --git a/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp b/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp index a39488fd62..0151b13693 100644 --- a/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp +++ b/tests/auto/concurrent/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp @@ -1,35 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + #include <qtconcurrentthreadengine.h> #include <qexception.h> #include <QThread> #include <QElapsedTimer> -#include <QtTest/QtTest> +#include <QTest> +#include <QSet> using namespace QtConcurrent; @@ -66,16 +45,9 @@ public: void tst_QtConcurrentThreadEngine::runDirectly() { - { - PrintUser engine; - engine.startSingleThreaded(); - engine.startBlocking(); - } - { - PrintUser *engine = new PrintUser(); - QFuture<void> f = engine->startAsynchronously(); - f.waitForFinished(); - } + PrintUser *engine = new PrintUser(); + QFuture<void> f = engine->startAsynchronously(); + f.waitForFinished(); } class StringResultUser : public ThreadEngine<QString> @@ -108,8 +80,10 @@ public: void tst_QtConcurrentThreadEngine::result() { - StringResultUser engine; - QCOMPARE(*engine.startBlocking(), QString("Foo")); + // ThreadEngine will delete 'engine' when it finishes + auto engine = new StringResultUser(); + auto future = engine->startAsynchronously(); + QCOMPARE(future.result(), QString("Foo")); } class VoidResultUser : public ThreadEngine<void> @@ -137,17 +111,9 @@ public: void tst_QtConcurrentThreadEngine::runThroughStarter() { - { - ThreadEngineStarter<QString> starter = startThreadEngine(new StringResultUser()); - QFuture<QString> f = starter.startAsynchronously(); - QCOMPARE(f.result(), QString("Foo")); - } - - { - ThreadEngineStarter<QString> starter = startThreadEngine(new StringResultUser()); - QString str = starter.startBlocking(); - QCOMPARE(str, QString("Foo")); - } + ThreadEngineStarter<QString> starter = startThreadEngine(new StringResultUser()); + QFuture<QString> f = starter.startAsynchronously(); + QCOMPARE(f.result(), QString("Foo")); } class CancelUser : public ThreadEngine<void> @@ -233,12 +199,6 @@ void tst_QtConcurrentThreadEngine::throttle() f.waitForFinished(); QCOMPARE(count.loadRelaxed(), 0); } - - for (int i = 0; i < repeats; ++i) { - ThrottleAlwaysUser t; - t.startBlocking(); - QCOMPARE(count.loadRelaxed(), 0); - } } QSet<QThread *> threads; @@ -274,40 +234,21 @@ public: void tst_QtConcurrentThreadEngine::threadCount() { - //QTBUG-23333: This test is unstable - const int repeats = 10; for (int i = 0; i < repeats; ++i) { - ThreadCountUser t; - t.startBlocking(); - int count = threads.count(); - int count_expected = QThreadPool::globalInstance()->maxThreadCount() + 1; // +1 for the main thread. - if (count != count_expected) - QEXPECT_FAIL("", "QTBUG-23333", Abort); - QCOMPARE(count, count_expected); - (new ThreadCountUser())->startAsynchronously().waitForFinished(); - count = threads.count(); - count_expected = QThreadPool::globalInstance()->maxThreadCount(); - if (count != count_expected) - QEXPECT_FAIL("", "QTBUG-23333", Abort); - QCOMPARE(count, count_expected); + const auto count = threads.size(); + const auto maxThreadCount = QThreadPool::globalInstance()->maxThreadCount(); + QVERIFY(count <= maxThreadCount); + QVERIFY(!threads.contains(QThread::currentThread())); } // Set the finish flag immediately, this should give us one thread only. for (int i = 0; i < repeats; ++i) { - ThreadCountUser t(true /*finishImmediately*/); - t.startBlocking(); - int count = threads.count(); - if (count != 1) - QEXPECT_FAIL("", "QTBUG-23333", Abort); - QCOMPARE(count, 1); - (new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished(); - count = threads.count(); - if (count != 1) - QEXPECT_FAIL("", "QTBUG-23333", Abort); + const auto count = threads.size(); QCOMPARE(count, 1); + QVERIFY(!threads.contains(QThread::currentThread())); } } @@ -333,7 +274,7 @@ void tst_QtConcurrentThreadEngine::multipleResults() { MultipleResultsUser *engine = new MultipleResultsUser(); QFuture<int> f = engine->startAsynchronously(); - QCOMPARE(f.results().count() , 10); + QCOMPARE(f.results().size() , 10); QCOMPARE(f.resultAt(0), 0); QCOMPARE(f.resultAt(5), 5); QCOMPARE(f.resultAt(9), 9); @@ -415,13 +356,13 @@ void tst_QtConcurrentThreadEngine::cancelQueuedSlowUser() class QtConcurrentExceptionThrower : public ThreadEngine<void> { public: - QtConcurrentExceptionThrower(QThread *blockThread = 0) + QtConcurrentExceptionThrower(QThread *blockThread = nullptr) : ThreadEngine(QThreadPool::globalInstance()) { this->blockThread = blockThread; } - ThreadFunctionResult threadFunction() + ThreadFunctionResult threadFunction() override { QTest::qSleep(50); throw QException(); @@ -430,16 +371,16 @@ public: QThread *blockThread; }; -class UnrelatedExceptionThrower : public ThreadEngine<void> +class IntExceptionThrower : public ThreadEngine<void> { public: - UnrelatedExceptionThrower(QThread *blockThread = 0) + IntExceptionThrower(QThread *blockThread = nullptr) : ThreadEngine(QThreadPool::globalInstance()) { this->blockThread = blockThread; } - ThreadFunctionResult threadFunction() + ThreadFunctionResult threadFunction() override { QTest::qSleep(50); throw int(); @@ -450,7 +391,6 @@ public: void tst_QtConcurrentThreadEngine::exceptions() { - // Asynchronous mode: { bool caught = false; try { @@ -463,65 +403,20 @@ void tst_QtConcurrentThreadEngine::exceptions() QVERIFY2(caught, "did not get exception"); } - // Blocking mode: - // test throwing the exception from a worker thread. - { - bool caught = false; - try { - QtConcurrentExceptionThrower e(QThread::currentThread()); - e.startBlocking(); - } catch (const QException &) { - caught = true; - } - QVERIFY2(caught, "did not get exception"); - } - - // test throwing the exception from the main thread (different code path) { bool caught = false; try { - QtConcurrentExceptionThrower e(0); - e.startBlocking(); - } catch (const QException &) { - caught = true; - } - QVERIFY2(caught, "did not get exception"); - } - - // Asynchronous mode: - { - bool caught = false; - try { - UnrelatedExceptionThrower *e = new UnrelatedExceptionThrower(); + IntExceptionThrower *e = new IntExceptionThrower(); QFuture<void> f = e->startAsynchronously(); f.waitForFinished(); - } catch (const QUnhandledException &) { - caught = true; - } - QVERIFY2(caught, "did not get exception"); - } - - // Blocking mode: - // test throwing the exception from a worker thread. - { - bool caught = false; - try { - UnrelatedExceptionThrower e(QThread::currentThread()); - e.startBlocking(); - } catch (const QUnhandledException &) { - caught = true; - } - QVERIFY2(caught, "did not get exception"); - } - - // test throwing the exception from the main thread (different code path) - { - bool caught = false; - try { - UnrelatedExceptionThrower e(0); - e.startBlocking(); - } catch (const QUnhandledException &) { - caught = true; + } catch (const QUnhandledException &ex) { + // Make sure the exception info is not lost + try { + if (ex.exception()) + std::rethrow_exception(ex.exception()); + } catch (int) { + caught = true; + } } QVERIFY2(caught, "did not get exception"); } diff --git a/tests/auto/concurrent/testhelper_functions.h b/tests/auto/concurrent/testhelper_functions.h new file mode 100644 index 0000000000..88c2e28910 --- /dev/null +++ b/tests/auto/concurrent/testhelper_functions.h @@ -0,0 +1,183 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#ifndef FUNCTIONS_H +#define FUNCTIONS_H + +#include <QList> + +#include <vector> + +bool keepEvenIntegers(const int &x) +{ + return (x & 1) == 0; +} + +class KeepEvenIntegers +{ +public: + bool operator()(const int &x) + { + return (x & 1) == 0; + } +}; + +class KeepEvenIntegersMoveOnly +{ +public: + KeepEvenIntegersMoveOnly() = default; + KeepEvenIntegersMoveOnly(KeepEvenIntegersMoveOnly &&) = default; + KeepEvenIntegersMoveOnly &operator=(KeepEvenIntegersMoveOnly &&other) = default; + + KeepEvenIntegersMoveOnly(const KeepEvenIntegersMoveOnly &) = delete; + KeepEvenIntegersMoveOnly &operator=(const KeepEvenIntegersMoveOnly &) = delete; + + bool operator()(int x) { return (x & 1) == 0; } +}; + +class Number +{ + int n; + +public: + Number() + : n(0) + { } + + Number(int n) + : n(n) + { } + + void multiplyBy2() + { + n *= 2; + } + + Number multipliedBy2() const + { + return n * 2; + } + + bool isEven() const + { + return (n & 1) == 0; + } + + int toInt() const + { + return n; + } + + QString toString() const + { + return QString::number(n); + } + + Number squared() const + { + return Number(n * n); + } + + bool operator==(const Number &other) const + { + return n == other.n; + } +}; + +bool keepEvenNumbers(const Number &x) +{ + return (x.toInt() & 1) == 0; +} + +class KeepEvenNumbers +{ +public: + bool operator()(const Number &x) + { + return (x.toInt() & 1) == 0; + } +}; + +void intSumReduce(int &sum, int x) +{ + sum += x; +} + +class IntSumReduce +{ +public: + void operator()(int &sum, int x) + { + sum += x; + } +}; + +class IntSumReduceMoveOnly +{ +public: + IntSumReduceMoveOnly() = default; + IntSumReduceMoveOnly(IntSumReduceMoveOnly &&) = default; + IntSumReduceMoveOnly &operator=(IntSumReduceMoveOnly &&other) = default; + + IntSumReduceMoveOnly(const IntSumReduceMoveOnly &) = delete; + IntSumReduceMoveOnly &operator=(const IntSumReduceMoveOnly &) = delete; + + void operator()(int &sum, int x) { sum += x; } +}; + +void numberSumReduce(int &sum, const Number &x) +{ + sum += x.toInt(); +} + +class NumberSumReduce +{ +public: + void operator()(int &sum, const Number &x) + { + sum += x.toInt(); + } +}; + +template<typename T> +class MoveOnlyVector +{ +public: + using value_type = T; + + // rule of six + MoveOnlyVector() = default; + ~MoveOnlyVector() = default; + MoveOnlyVector(MoveOnlyVector<T> &&other) = default; + MoveOnlyVector &operator=(MoveOnlyVector<T> &&other) = default; + + MoveOnlyVector(const MoveOnlyVector<T> &) = delete; + MoveOnlyVector &operator=(const MoveOnlyVector<T> &) = delete; + + // convenience for creation + explicit MoveOnlyVector(const std::vector<T> &v) : data(v) { } + void push_back(T &&el) { data.push_back(el); } + void push_back(const T &el) { data.push_back(el); } + + // minimal interface to be usable as a Sequence in QtConcurrent + typedef typename std::vector<T>::const_iterator const_iterator; + typedef typename std::vector<T>::iterator iterator; + const_iterator cbegin() const { return data.cbegin(); } + const_iterator cend() const { return data.cend(); } + iterator begin() { return data.begin(); } + iterator end() { return data.end(); } + const_iterator begin() const { return data.cbegin(); } + const_iterator end() const { return data.cend(); } + bool operator==(const MoveOnlyVector<T> &other) const { return data == other.data; } + +private: + std::vector<T> data; +}; + +struct NonTemplateSequence : public QList<int> +{ + NonTemplateSequence() = default; + + NonTemplateSequence(std::initializer_list<int> args) : QList(args) { } +}; + +#endif |