/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** 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. ** ****************************************************************************/ #include #include #if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 // MSVC2015 #define SUPPORTS_MOVE #endif class tst_RunExtensions : public QObject { Q_OBJECT private slots: void runAsync(); void runInThreadPool(); #ifdef SUPPORTS_MOVE void moveOnlyType(); #endif void threadPriority(); void runAsyncNoFutureInterface(); void crefFunction(); void onResultReady(); }; void report3(QFutureInterface &fi) { fi.reportResults({0, 2, 1}); } void reportN(QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); } void reportString1(QFutureInterface &fi, const QString &s) { fi.reportResult(s); } void reportString2(QFutureInterface &fi, QString s) { fi.reportResult(s); } class Callable { public: void operator()(QFutureInterface &fi, int n) const { fi.reportResults(QVector(n, 0)); } }; class MyObject { public: static void staticMember0(QFutureInterface &fi) { fi.reportResults({0, 2, 1}); } static void staticMember1(QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); } void member0(QFutureInterface &fi) const { fi.reportResults({0, 2, 1}); } void member1(QFutureInterface &fi, int n) const { fi.reportResults(QVector(n, 0)); } void memberString1(QFutureInterface &fi, const QString &s) const { fi.reportResult(s); } void memberString2(QFutureInterface &fi, QString s) const { fi.reportResult(s); } void nonConstMember(QFutureInterface &fi) { fi.reportResults({0, 2, 1}); } }; void voidFunction(bool *value) // can be useful to get QFuture for watching when it is finished { *value = true; } int one() { return 1; } int identity(int input) { return input; } QString stringIdentity1(const QString &s) { return s; } QString stringIdentity2(QString s) { return s; } class CallableWithoutQFutureInterface { public: void operator()(bool *value) const { *value = true; } }; class MyObjectWithoutQFutureInterface { public: static void staticMember0(bool *value) { *value = true; } static double staticMember1(int n) { return n; } void member0(bool *value) const { *value = true; } double member1(int n) const { return n; } QString memberString1(const QString &s) const { return s; } QString memberString2(QString s) const { return s; } double nonConstMember(int n) { return n; } }; void tst_RunExtensions::runAsync() { // free function pointer QCOMPARE(Utils::runAsync(&report3).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(report3).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(reportN, 4).results(), QList({0, 0, 0, 0})); QCOMPARE(Utils::runAsync(reportN, 2).results(), QList({0, 0})); QString s = QLatin1String("string"); const QString &crs = QLatin1String("cr string"); const QString cs = QLatin1String("c string"); QCOMPARE(Utils::runAsync(reportString1, s).results(), QList({s})); QCOMPARE(Utils::runAsync(reportString1, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(reportString1, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(reportString1, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); QCOMPARE(Utils::runAsync(reportString2, s).results(), QList({s})); QCOMPARE(Utils::runAsync(reportString2, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(reportString2, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(reportString2, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); // lambda QCOMPARE(Utils::runAsync([](QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); }, 3).results(), QList({0, 0, 0})); // std::function const std::function&,int)> fun = [](QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); }; QCOMPARE(Utils::runAsync(fun, 2).results(), QList({0, 0})); // operator() QCOMPARE(Utils::runAsync(Callable(), 3).results(), QList({0, 0, 0})); const Callable c{}; QCOMPARE(Utils::runAsync(c, 2).results(), QList({0, 0})); // static member functions QCOMPARE(Utils::runAsync(&MyObject::staticMember0).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(&MyObject::staticMember1, 2).results(), QList({0, 0})); // member functions const MyObject obj{}; QCOMPARE(Utils::runAsync(&MyObject::member0, &obj).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(&MyObject::member1, &obj, 4).results(), QList({0, 0, 0, 0})); QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, s).results(), QList({s})); QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, s).results(), QList({s})); QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); MyObject nonConstObj{}; QCOMPARE(Utils::runAsync(&MyObject::nonConstMember, &nonConstObj).results(), QList({0, 2, 1})); } void tst_RunExtensions::runInThreadPool() { QScopedPointer pool(new QThreadPool); // free function pointer QCOMPARE(Utils::runAsync(pool.data(), &report3).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(pool.data(), report3).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(pool.data(), reportN, 4).results(), QList({0, 0, 0, 0})); QCOMPARE(Utils::runAsync(pool.data(), reportN, 2).results(), QList({0, 0})); QString s = QLatin1String("string"); const QString &crs = QLatin1String("cr string"); const QString cs = QLatin1String("c string"); QCOMPARE(Utils::runAsync(pool.data(), reportString1, s).results(), QList({s})); QCOMPARE(Utils::runAsync(pool.data(), reportString1, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(pool.data(), reportString1, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); QCOMPARE(Utils::runAsync(pool.data(), reportString2, s).results(), QList({s})); QCOMPARE(Utils::runAsync(pool.data(), reportString2, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(pool.data(), reportString2, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); // lambda QCOMPARE(Utils::runAsync(pool.data(), [](QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); }, 3).results(), QList({0, 0, 0})); // std::function const std::function&,int)> fun = [](QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); }; QCOMPARE(Utils::runAsync(pool.data(), fun, 2).results(), QList({0, 0})); // operator() QCOMPARE(Utils::runAsync(pool.data(), Callable(), 3).results(), QList({0, 0, 0})); const Callable c{}; QCOMPARE(Utils::runAsync(pool.data(), c, 2).results(), QList({0, 0})); // static member functions QCOMPARE(Utils::runAsync(pool.data(), &MyObject::staticMember0).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::staticMember1, 2).results(), QList({0, 0})); // member functions const MyObject obj{}; QCOMPARE(Utils::runAsync(pool.data(), &MyObject::member0, &obj).results(), QList({0, 2, 1})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::member1, &obj, 4).results(), QList({0, 0, 0, 0})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, s).results(), QList({s})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, s).results(), QList({s})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); } #ifdef SUPPORTS_MOVE class MoveOnlyType { public: MoveOnlyType() = default; MoveOnlyType(const MoveOnlyType &) = delete; MoveOnlyType(MoveOnlyType &&) = default; MoveOnlyType &operator=(const MoveOnlyType &) = delete; MoveOnlyType &operator=(MoveOnlyType &&) = default; }; class MoveOnlyCallable : public MoveOnlyType { public: void operator()(QFutureInterface &fi, const MoveOnlyType &) { fi.reportResult(1); } }; void tst_RunExtensions::moveOnlyType() { QCOMPARE(Utils::runAsync(MoveOnlyCallable(), MoveOnlyType()).results(), QList({1})); } #endif void tst_RunExtensions::threadPriority() { QScopedPointer pool(new QThreadPool); // with pool QCOMPARE(Utils::runAsync(pool.data(), QThread::LowestPriority, &report3).results(), QList({0, 2, 1})); // without pool QCOMPARE(Utils::runAsync(QThread::LowestPriority, report3).results(), QList({0, 2, 1})); } void tst_RunExtensions::runAsyncNoFutureInterface() { // free function pointer bool value = false; Utils::runAsync(voidFunction, &value).waitForFinished(); QCOMPARE(value, true); QCOMPARE(Utils::runAsync(one).results(), QList({1})); QCOMPARE(Utils::runAsync(identity, 5).results(), QList({5})); QString s = QLatin1String("string"); const QString &crs = QLatin1String("cr string"); const QString cs = QLatin1String("c string"); QCOMPARE(Utils::runAsync(stringIdentity1, s).results(), QList({s})); QCOMPARE(Utils::runAsync(stringIdentity1, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(stringIdentity1, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(stringIdentity1, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); QCOMPARE(Utils::runAsync(stringIdentity2, s).results(), QList({s})); QCOMPARE(Utils::runAsync(stringIdentity2, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(stringIdentity2, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(stringIdentity2, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); // lambda QCOMPARE(Utils::runAsync([](int n) -> double { return n + 1; }, 3).results(), QList({4})); // std::function const std::function fun = [](int n) { return n + 1; }; QCOMPARE(Utils::runAsync(fun, 2).results(), QList({3})); // operator() value = false; Utils::runAsync(CallableWithoutQFutureInterface(), &value).waitForFinished(); QCOMPARE(value, true); value = false; const CallableWithoutQFutureInterface c{}; Utils::runAsync(c, &value).waitForFinished(); QCOMPARE(value, true); // static member functions value = false; Utils::runAsync(&MyObjectWithoutQFutureInterface::staticMember0, &value).waitForFinished(); QCOMPARE(value, true); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::staticMember1, 2).results(), QList({2})); // member functions const MyObjectWithoutQFutureInterface obj{}; value = false; Utils::runAsync(&MyObjectWithoutQFutureInterface::member0, &obj, &value).waitForFinished(); QCOMPARE(value, true); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::member1, &obj, 4).results(), QList({4})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, s).results(), QList({s})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, s).results(), QList({s})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, crs).results(), QList({crs})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, cs).results(), QList({cs})); QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), QList({QString(QLatin1String("rvalue"))})); MyObjectWithoutQFutureInterface nonConstObj{}; QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::nonConstMember, &nonConstObj, 4).results(), QList({4})); } void tst_RunExtensions::crefFunction() { // free function pointer with future interface auto fun = &report3; QCOMPARE(Utils::runAsync(std::cref(fun)).results(), QList({0, 2, 1})); // lambda with future interface auto lambda = [](QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); }; QCOMPARE(Utils::runAsync(std::cref(lambda), 3).results(), QList({0, 0, 0})); // std::function with future interface const std::function&,int)> funObj = [](QFutureInterface &fi, int n) { fi.reportResults(QVector(n, 0)); }; QCOMPARE(Utils::runAsync(std::cref(funObj), 2).results(), QList({0, 0})); // callable with future interface const Callable c{}; QCOMPARE(Utils::runAsync(std::cref(c), 2).results(), QList({0, 0})); // member functions with future interface auto member = &MyObject::member0; const MyObject obj{}; QCOMPARE(Utils::runAsync(std::cref(member), &obj).results(), QList({0, 2, 1})); // free function pointer without future interface bool value = false; auto voidFun = &voidFunction; Utils::runAsync(std::cref(voidFun), &value).waitForFinished(); QCOMPARE(value, true); auto oneFun = &one; QCOMPARE(Utils::runAsync(std::cref(oneFun)).results(), QList({1})); // lambda without future interface auto lambda2 = [](int n) -> double { return n + 1; }; QCOMPARE(Utils::runAsync(std::cref(lambda2), 3).results(), QList({4})); // std::function const std::function funObj2 = [](int n) { return n + 1; }; QCOMPARE(Utils::runAsync(std::cref(funObj2), 2).results(), QList({3})); // callable without future interface const CallableWithoutQFutureInterface c2{}; Utils::runAsync(std::cref(c2), &value).waitForFinished(); QCOMPARE(value, true); // member functions without future interface const MyObjectWithoutQFutureInterface obj2{}; auto member2 = &MyObjectWithoutQFutureInterface::member0; value = false; Utils::runAsync(std::cref(member2), &obj2, &value).waitForFinished(); QCOMPARE(value, true); } class ObjWithProperty : public QObject { Q_OBJECT public slots: void setValue(const QString &s) { value = s; } public: QString value; }; void tst_RunExtensions::onResultReady() { { // lambda QFuture f = Utils::runAsync([](QFutureInterface &fi) { fi.reportResult("Hi"); fi.reportResult("there"); }); int count = 0; QString res; Utils::onResultReady(f, [&count, &res](const QString &s) { ++count; res = s; }); f.waitForFinished(); QCoreApplication::processEvents(); QCOMPARE(count, 2); QCOMPARE(res, QString("there")); } { // lambda with guard QFuture f = Utils::runAsync([](QFutureInterface &fi) { fi.reportResult("Hi"); fi.reportResult("there"); }); int count = 0; ObjWithProperty obj; Utils::onResultReady(f, &obj, [&count, &obj](const QString &s) { ++count; obj.setValue(s); }); f.waitForFinished(); QCoreApplication::processEvents(); QCOMPARE(count, 2); QCOMPARE(obj.value, QString("there")); } { // member QFuture f = Utils::runAsync([]() { return QString("Hi"); }); ObjWithProperty obj; Utils::onResultReady(f, &obj, &ObjWithProperty::setValue); f.waitForFinished(); QCoreApplication::processEvents(); QCOMPARE(obj.value, QString("Hi")); } } QTEST_MAIN(tst_RunExtensions) #include "tst_runextensions.moc"