From 0547598a28c3c1dd5c5f4c3ec9888b0de499199b Mon Sep 17 00:00:00 2001 From: Holger Ihrig Date: Wed, 24 Aug 2011 10:51:34 +0200 Subject: Moving relevant tests to corelib/concurrent Adding tests for QFutureSynchronizer and QtConcurrentResultStore Added minor things in QFutureSynchronizer and QtConcurrentResultStore and removed tests for destruction Task-number: QTBUG-21066 Change-Id: I9f088b89463340f339c914bcb37fb2f9d3b62057 Reviewed-on: http://codereview.qt.nokia.com/3477 Reviewed-by: Qt Sanity Bot Reviewed-by: Rohan McGovern Reviewed-by: Jason McDonald --- .../corelib/concurrent/qfuture/tst_qfuture.cpp | 1469 ++++++++++++++++++++ 1 file changed, 1469 insertions(+) create mode 100644 tests/auto/corelib/concurrent/qfuture/tst_qfuture.cpp (limited to 'tests/auto/corelib/concurrent/qfuture/tst_qfuture.cpp') diff --git a/tests/auto/corelib/concurrent/qfuture/tst_qfuture.cpp b/tests/auto/corelib/concurrent/qfuture/tst_qfuture.cpp new file mode 100644 index 0000000000..2178489bd9 --- /dev/null +++ b/tests/auto/corelib/concurrent/qfuture/tst_qfuture.cpp @@ -0,0 +1,1469 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +#define QFUTURE_TEST + +#include +#include +#include "versioncheck.h" +#include +#include +#include + +#ifndef QT_NO_CONCURRENT_TEST +#include + +using namespace QtConcurrent; + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QFuture: public QObject +{ + Q_OBJECT +private slots: + void resultStore(); + void future(); + void futureInterface(); + void refcounting(); + void cancel(); + void statePropagation(); + void multipleResults(); + void indexedResults(); + void progress(); + void progressText(); + void resultsAfterFinished(); + void resultsAsList(); + void implicitConversions(); + void iterators(); + void pause(); + void throttling(); + void voidConversions(); +#ifndef QT_NO_EXCEPTIONS + void exceptions(); + void exceptions_QTBUG18149(); +#endif +}; + +QTEST_MAIN(tst_QFuture) + +void tst_QFuture::resultStore() +{ + int int0 = 0; + int int1 = 1; + int int2 = 2; + + { + ResultStore store; + ResultIteratorBase it = store.begin(); + QVERIFY(store.begin() == store.end()); + QVERIFY(store.resultAt(0) == store.end()); + QVERIFY(store.resultAt(1) == store.end()); + } + + + { + ResultStoreBase store; + store.addResult(-1, &int0); // note to self: adding a pointer to the stack here is ok since + store.addResult(1, &int1); // ResultStoreBase does not take ownership, only ResultStore<> does. + ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QVERIFY(it != store.begin()); + QVERIFY(it == store.end()); + } + + QVector vec0 = QVector() << 2 << 3; + QVector vec1 = QVector() << 4 << 5; + + { + ResultStoreBase store; + store.addResults(-1, &vec0, 2, 2); + store.addResults(-1, &vec1, 2, 2); + ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 2); + + ++it; + QCOMPARE(it.resultIndex(), 3); + + ++it; + QVERIFY(it == store.end()); + } + { + ResultStoreBase store; + store.addResult(-1, &int0); + store.addResults(-1, &vec1, 2, 2); + store.addResult(-1, &int1); + + ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 2); + QVERIFY(it != store.end()); + ++it; + QCOMPARE(it.resultIndex(), 3); + QVERIFY(it != store.end()); + ++it; + QVERIFY(it == store.end()); + + QCOMPARE(store.resultAt(0).resultIndex(), 0); + QCOMPARE(store.resultAt(1).resultIndex(), 1); + QCOMPARE(store.resultAt(2).resultIndex(), 2); + QCOMPARE(store.resultAt(3).resultIndex(), 3); + QCOMPARE(store.resultAt(4), store.end()); + } + { + ResultStore store; + store.addResult(-1, &int0); + store.addResults(-1, &vec0); + store.addResult(-1, &int1); + + ResultIteratorBase it = store.begin(); + QCOMPARE(it.resultIndex(), 0); + QVERIFY(it == store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 1); + QVERIFY(it != store.begin()); + QVERIFY(it != store.end()); + + ++it; + QCOMPARE(it.resultIndex(), 2); + QVERIFY(it != store.end()); + ++it; + QCOMPARE(it.resultIndex(), 3); + QVERIFY(it != store.end()); + ++it; + QVERIFY(it == store.end()); + + QCOMPARE(store.resultAt(0).value(), int0); + QCOMPARE(store.resultAt(1).value(), vec0[0]); + QCOMPARE(store.resultAt(2).value(), vec0[1]); + QCOMPARE(store.resultAt(3).value(), int1); + } + { + ResultStore store; + store.addResult(-1, &int0); + store.addResults(-1, &vec0); + store.addResult(200, &int1); + + QCOMPARE(store.resultAt(0).value(), int0); + QCOMPARE(store.resultAt(1).value(), vec0[0]); + QCOMPARE(store.resultAt(2).value(), vec0[1]); + QCOMPARE(store.resultAt(200).value(), int1); + } + + { + ResultStore store; + store.addResult(1, &int1); + store.addResult(0, &int0); + store.addResult(-1, &int2); + + QCOMPARE(store.resultAt(0).value(), int0); + QCOMPARE(store.resultAt(1).value(), int1); + QCOMPARE(store.resultAt(2).value(), int2); + } + + { + ResultStore store; + QCOMPARE(store.contains(0), false); + QCOMPARE(store.contains(1), false); + QCOMPARE(store.contains(INT_MAX), false); + } + + { + // Test filter mode, where "gaps" in the result array aren't allowed. + ResultStore store; + store.setFilterMode(true); + + store.addResult(0, &int0); + QCOMPARE(store.contains(0), true); + + store.addResult(2, &int2); // add result at index 2 + QCOMPARE(store.contains(2), false); // but 1 is missing, so this 2 won't be reported yet. + + store.addResult(1, &int1); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), true); // 2 should be visible now. + + store.addResult(4, &int0); + store.addResult(5, &int0); + store.addResult(7, &int0); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(7), false); + + store.addResult(3, &int0); // adding 3 makes 4 and 5 visible + QCOMPARE(store.contains(4), true); + QCOMPARE(store.contains(5), true); + QCOMPARE(store.contains(7), false); + + store.addResult(6, &int0); // adding 6 makes 7 visible + + QCOMPARE(store.contains(6), true); + QCOMPARE(store.contains(7), true); + QCOMPARE(store.contains(8), false); + } + + { + // test canceled results + ResultStore store; + store.setFilterMode(true); + + store.addResult(0, &int0); + QCOMPARE(store.contains(0), true); + + store.addResult(2, &int0); + QCOMPARE(store.contains(2), false); + + store.addCanceledResult(1); // report no result at 1 + + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), true); // 2 gets renamed to 1 + QCOMPARE(store.contains(2), false); + + store.addResult(3, &int0); + QCOMPARE(store.contains(2), true); //3 gets renamed to 2 + + store.addResult(6, &int0); + store.addResult(7, &int0); + QCOMPARE(store.contains(3), false); + + store.addCanceledResult(4); + store.addCanceledResult(5); + + QCOMPARE(store.contains(3), true); //6 gets renamed to 3 + QCOMPARE(store.contains(4), true); //7 gets renamed to 4 + + store.addResult(8, &int0); + QCOMPARE(store.contains(5), true); //8 gets renamed to 4 + + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + } + + { + // test addResult return value + ResultStore store; + store.setFilterMode(true); + + store.addResult(0, &int0); + QCOMPARE(store.count(), 1); // result 0 becomes available + QCOMPARE(store.contains(0), true); + + store.addResult(2, &int0); + QCOMPARE(store.count(), 1); + QCOMPARE(store.contains(2), false); + + store.addCanceledResult(1); + QCOMPARE(store.count(), 2); // result 2 is renamed to 1 and becomes available + + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), false); + + store.addResult(3, &int0); + QCOMPARE(store.count(), 3); + QCOMPARE(store.contains(2), true); + + store.addResult(6, &int0); + QCOMPARE(store.count(), 3); + store.addResult(7, &int0); + QCOMPARE(store.count(), 3); + QCOMPARE(store.contains(3), false); + + store.addCanceledResult(4); + store.addCanceledResult(5); + QCOMPARE(store.count(), 5); // 6 and 7 is renamed to 3 and 4 and becomes available + + QCOMPARE(store.contains(3), true); + QCOMPARE(store.contains(4), true); + + store.addResult(8, &int0); + QCOMPARE(store.contains(5), true); + QCOMPARE(store.count(), 6); + + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + } + + { + // test resultCount in non-filtered mode. It should always be possible + // to iterate through the results 0 to resultCount. + ResultStore store; + store.addResult(0, &int0); + + QCOMPARE(store.count(), 1); + + store.addResult(2, &int0); + + QCOMPARE(store.count(), 1); + + store.addResult(1, &int0); + QCOMPARE(store.count(), 3); + } + + { + ResultStore store; + store.addResult(2, &int0); + QCOMPARE(store.count(), 0); + + store.addResult(1, &int0); + QCOMPARE(store.count(), 0); + + store.addResult(0, &int0); + QCOMPARE(store.count(), 3); + } + + { + ResultStore store; + store.addResults(2, &vec1); + QCOMPARE(store.count(), 0); + + store.addResult(1, &int0); + QCOMPARE(store.count(), 0); + + store.addResult(0, &int0); + QCOMPARE(store.count(), 4); + } + + { + ResultStore store; + store.addResults(2, &vec1); + QCOMPARE(store.count(), 0); + + store.addResults(0, &vec0); + QCOMPARE(store.count(), 4); + } + { + ResultStore store; + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addResults(0, &vec0); + QCOMPARE(store.count(), 2); + + store.addResult(2, &int0); + QCOMPARE(store.count(), 5); + } + + { + ResultStore store; + store.setFilterMode(true); + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addResults(0, &vec0); + QCOMPARE(store.count(), 2); + + store.addCanceledResult(2); + QCOMPARE(store.count(), 4); + } + + { + ResultStore store; + store.setFilterMode(true); + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addCanceledResults(0, 3); + QCOMPARE(store.count(), 2); + } + + { + ResultStore store; + store.setFilterMode(true); + store.addResults(3, &vec1); + QCOMPARE(store.count(), 0); + + store.addCanceledResults(0, 3); + QCOMPARE(store.count(), 2); // results at 3 and 4 become available at index 0, 1 + + store.addResult(5, &int0); + QCOMPARE(store.count(), 3);// result 5 becomes available at index 2 + } + + { + ResultStore store; + store.addResult(1, &int0); + store.addResult(3, &int0); + store.addResults(6, &vec0); + QCOMPARE(store.contains(0), false); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), false); + QCOMPARE(store.contains(3), true); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(6), true); + QCOMPARE(store.contains(7), true); + } + + { + ResultStore store; + store.setFilterMode(true); + store.addResult(1, &int0); + store.addResult(3, &int0); + store.addResults(6, &vec0); + QCOMPARE(store.contains(0), false); + QCOMPARE(store.contains(1), false); + QCOMPARE(store.contains(2), false); + QCOMPARE(store.contains(3), false); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + + store.addCanceledResult(0); + store.addCanceledResult(2); + store.addCanceledResults(4, 2); + + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), true); + QCOMPARE(store.contains(2), true); + QCOMPARE(store.contains(3), true); + QCOMPARE(store.contains(4), false); + QCOMPARE(store.contains(5), false); + QCOMPARE(store.contains(6), false); + QCOMPARE(store.contains(7), false); + } + { + ResultStore store; + store.setFilterMode(true); + store.addCanceledResult(0); + QCOMPARE(store.contains(0), false); + + store.addResult(1, &int0); + QCOMPARE(store.contains(0), true); + QCOMPARE(store.contains(1), false); + } +} + +void tst_QFuture::future() +{ + // default constructors + QFuture intFuture; + intFuture.waitForFinished(); + QFuture stringFuture; + stringFuture.waitForFinished(); + QFuture voidFuture; + voidFuture.waitForFinished(); + QFuture defaultVoidFuture; + defaultVoidFuture.waitForFinished(); + + // copy constructor + QFuture intFuture2(intFuture); + QFuture voidFuture2(defaultVoidFuture); + + // assigmnent operator + intFuture2 = QFuture(); + voidFuture2 = QFuture(); + + // state + QCOMPARE(intFuture2.isStarted(), true); + QCOMPARE(intFuture2.isFinished(), true); +} + +class IntResult : public QFutureInterface +{ +public: + QFuture run() + { + this->reportStarted(); + QFuture future = QFuture(this); + + int res = 10; + reportFinished(&res); + return future; + } +}; + +int value = 10; + +class VoidResult : public QFutureInterfaceBase +{ +public: + QFuture run() + { + this->reportStarted(); + QFuture future = QFuture(this); + reportFinished(); + return future; + } +}; + +void tst_QFuture::futureInterface() +{ + { + QFuture future; + { + QFutureInterface i; + i.reportStarted(); + future = i.future(); + i.reportFinished(); + } + } + { + QFuture future; + { + QFutureInterface i; + i.reportStarted(); + i.reportResult(10); + future = i.future(); + i.reportFinished(); + } + QCOMPARE(future.resultAt(0), 10); + } + + { + QFuture intFuture; + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), true); + + IntResult result; + + result.reportStarted(); + intFuture = result.future(); + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), false); + + result.reportFinished(&value); + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), true); + + int e = intFuture.result(); + + QCOMPARE(intFuture.isStarted(), true); + QCOMPARE(intFuture.isFinished(), true); + QCOMPARE(intFuture.isCanceled(), false); + + QCOMPARE(e, value); + intFuture.waitForFinished(); + + IntResult intAlgo; + intFuture = intAlgo.run(); + QFuture intFuture2(intFuture); + QCOMPARE(intFuture.result(), value); + QCOMPARE(intFuture2.result(), value); + intFuture.waitForFinished(); + + VoidResult a; + a.run().waitForFinished(); + } +} + +template +void testRefCounting() +{ + QFutureInterface interface; + QCOMPARE(int(interface.d->refCount), 1); + + { + interface.reportStarted(); + + QFuture f = interface.future(); + QCOMPARE(int(interface.d->refCount), 2); + + QFuture f2(f); + QCOMPARE(int(interface.d->refCount), 3); + + QFuture f3; + f3 = f2; + QCOMPARE(int(interface.d->refCount), 4); + + interface.reportFinished(0); + QCOMPARE(int(interface.d->refCount), 4); + } + + QCOMPARE(int(interface.d->refCount), 1); +} + +void tst_QFuture::refcounting() +{ + testRefCounting(); +} + +void tst_QFuture::cancel() +{ + { + QFuture f; + QFutureInterface result; + + result.reportStarted(); + f = result.future(); + QVERIFY(f.isCanceled() == false); + result.reportCanceled(); + QVERIFY(f.isCanceled()); + result.reportFinished(); + QVERIFY(f.isCanceled()); + f.waitForFinished(); + QVERIFY(f.isCanceled()); + } + + // Cancel from the QFuture side and test if the result + // interface detects it. + { + QFutureInterface result; + + QFuture f; + QVERIFY(f.isStarted() == true); + + result.reportStarted(); + f = result.future(); + + QVERIFY(f.isStarted() == true); + + QVERIFY(result.isCanceled() == false); + f.cancel(); + + QVERIFY(result.isCanceled()); + + result.reportFinished(); + } + + // Test that finished futures can be canceled. + { + QFutureInterface result; + + QFuture f; + QVERIFY(f.isStarted() == true); + + result.reportStarted(); + f = result.future(); + + QVERIFY(f.isStarted() == true); + + result.reportFinished(); + + f.cancel(); + + QVERIFY(result.isCanceled()); + QVERIFY(f.isCanceled()); + } + + // Results reported after canceled is called should not be propagated. + { + + QFutureInterface futureInterface; + futureInterface.reportStarted(); + QFuture f = futureInterface.future(); + + int result = 0; + futureInterface.reportResult(&result); + result = 1; + futureInterface.reportResult(&result); + f.cancel(); + result = 2; + futureInterface.reportResult(&result); + result = 3; + futureInterface.reportResult(&result); + futureInterface.reportFinished(); + QCOMPARE(f.results(), QList()); + } +} + +void tst_QFuture::statePropagation() +{ + QFuture f1; + QFuture f2; + + QCOMPARE(f1.isStarted(), true); + + QFutureInterface result; + result.reportStarted(); + f1 = result.future(); + + f2 = f1; + + QCOMPARE(f2.isStarted(), true); + + result.reportCanceled(); + + QCOMPARE(f2.isStarted(), true); + QCOMPARE(f2.isCanceled(), true); + + QFuture f3 = f2; + + QCOMPARE(f3.isStarted(), true); + QCOMPARE(f3.isCanceled(), true); + + result.reportFinished(); + + QCOMPARE(f2.isStarted(), true); + QCOMPARE(f2.isCanceled(), true); + + QCOMPARE(f3.isStarted(), true); + QCOMPARE(f3.isCanceled(), true); +} + +/* + Tests that a QFuture can return multiple results. +*/ +void tst_QFuture::multipleResults() +{ + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + + QFuture copy = f; + int result; + + result = 1; + a.reportResult(&result); + QCOMPARE(f.resultAt(0), 1); + + result = 2; + a.reportResult(&result); + QCOMPARE(f.resultAt(1), 2); + + result = 3; + a.reportResult(&result); + + result = 4; + a.reportFinished(&result); + + QCOMPARE(f.results(), QList() << 1 << 2 << 3 << 4); + + // test foreach + QList fasit = QList() << 1 << 2 << 3 << 4; + { + QList results; + foreach(int result, f) + results.append(result); + QCOMPARE(results, fasit); + } + { + QList results; + foreach(int result, copy) + results.append(result); + QCOMPARE(results, fasit); + } +} + +/* + Test out-of-order result reporting using indexes +*/ +void tst_QFuture::indexedResults() +{ + { + QFutureInterface Interface; + QFuture f; + QVERIFY(f.isStarted() == true); + + Interface.reportStarted(); + f = Interface.future(); + + QVERIFY(f.isStarted() == true); + + QChar result; + + result = 'B'; + Interface.reportResult(&result, 1); + + QCOMPARE(f.resultAt(1), result); + + result = 'A'; + Interface.reportResult(&result, 0); + QCOMPARE(f.resultAt(0), result); + + result = 'C'; + Interface.reportResult(&result); // no index + QCOMPARE(f.resultAt(2), result); + + Interface.reportFinished(); + + QCOMPARE(f.results(), QList() << 'A' << 'B' << 'C'); + } + + { + // Test result reporting with a missing result in the middle + QFutureInterface Interface; + Interface.reportStarted(); + QFuture f = Interface.future(); + int result; + + result = 0; + Interface.reportResult(&result, 0); + QVERIFY(f.isResultReadyAt(0)); + QCOMPARE(f.resultAt(0), 0); + + result = 3; + Interface.reportResult(&result, 3); + QVERIFY(f.isResultReadyAt(3)); + QCOMPARE(f.resultAt(3), 3); + + result = 2; + Interface.reportResult(&result, 2); + QVERIFY(f.isResultReadyAt(2)); + QCOMPARE(f.resultAt(2), 2); + + result = 4; + Interface.reportResult(&result); // no index + QVERIFY(f.isResultReadyAt(4)); + QCOMPARE(f.resultAt(4), 4); + + Interface.reportFinished(); + + QCOMPARE(f.results(), QList() << 0 << 2 << 3 << 4); + } +} + +void tst_QFuture::progress() +{ + QFutureInterface result; + QFuture f; + + QCOMPARE (f.progressValue(), 0); + + result.reportStarted(); + f = result.future(); + + QCOMPARE (f.progressValue(), 0); + + result.setProgressValue(50); + + QCOMPARE (f.progressValue(), 50); + + result.reportFinished(); + + QCOMPARE (f.progressValue(), 50); +} + +void tst_QFuture::progressText() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + + QCOMPARE(f.progressText(), QLatin1String("")); + i.setProgressValueAndText(1, QLatin1String("foo")); + QCOMPARE(f.progressText(), QLatin1String("foo")); + i.reportFinished(); +} + +/* + Test that results reported after finished are ignored. +*/ +void tst_QFuture::resultsAfterFinished() +{ + { + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + int result; + + QCOMPARE(f.resultCount(), 0); + + result = 1; + a.reportResult(&result); + QCOMPARE(f.resultAt(0), 1); + + a.reportFinished(); + + QCOMPARE(f.resultAt(0), 1); + QCOMPARE(f.resultCount(), 1); + result = 2; + a.reportResult(&result); + QCOMPARE(f.resultCount(), 1); + } + // cancel it + { + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + int result; + + QCOMPARE(f.resultCount(), 0); + + result = 1; + a.reportResult(&result); + QCOMPARE(f.resultAt(0), 1); + QCOMPARE(f.resultCount(), 1); + + a.reportCanceled(); + + QCOMPARE(f.resultAt(0), 1); + QCOMPARE(f.resultCount(), 1); + + result = 2; + a.reportResult(&result); + a.reportFinished(); + } +} + +void tst_QFuture::resultsAsList() +{ + IntResult a; + a.reportStarted(); + QFuture f = a.future(); + + int result; + result = 1; + a.reportResult(&result); + result = 2; + a.reportResult(&result); + + a.reportFinished(); + + QList results = f.results(); + QCOMPARE(results, QList() << 1 << 2); +} + +/* + Test that QFuture can be implicitly converted to T +*/ +void tst_QFuture::implicitConversions() +{ + QFutureInterface iface; + iface.reportStarted(); + + QFuture f(&iface); + + const QString input("FooBar 2000"); + iface.reportFinished(&input); + + const QString result = f; + QCOMPARE(result, input); + QCOMPARE(QString(f), input); + QCOMPARE(static_cast(f), input); +} + +void tst_QFuture::iterators() +{ + { + QFutureInterface e; + e.reportStarted(); + QFuture f = e.future(); + + int result; + result = 1; + e.reportResult(&result); + result = 2; + e.reportResult(&result); + result = 3; + e.reportResult(&result); + e.reportFinished(); + + QList results; + QFutureIterator i(f); + while (i.hasNext()) { + results.append(i.next()); + } + + QCOMPARE(results, f.results()); + + QFuture::const_iterator i1 = f.begin(), i2 = i1 + 1; + QFuture::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 == i1); + QVERIFY(i1 == c1); + QVERIFY(c1 == i1); + QVERIFY(c1 == c1); + QVERIFY(i2 == i2); + QVERIFY(i2 == c2); + QVERIFY(c2 == i2); + QVERIFY(c2 == c2); + + QVERIFY(i1 != i2); + QVERIFY(i1 != c2); + QVERIFY(c1 != i2); + QVERIFY(c1 != c2); + QVERIFY(i2 != i1); + QVERIFY(i2 != c1); + QVERIFY(c2 != i1); + QVERIFY(c2 != c1); + + int x1 = *i1; + Q_UNUSED(x1); + int x2 = *i2; + Q_UNUSED(x2); + int y1 = *c1; + Q_UNUSED(y1); + int y2 = *c2; + Q_UNUSED(y2); + } + + { + QFutureInterface e; + e.reportStarted(); + QFuture f = e.future(); + + e.reportResult(QString("one")); + e.reportResult(QString("two")); + e.reportResult(QString("three")); + e.reportFinished(); + + QList results; + QFutureIterator i(f); + while (i.hasNext()) { + results.append(i.next()); + } + + QCOMPARE(results, f.results()); + + QFuture::const_iterator i1 = f.begin(), i2 = i1 + 1; + QFuture::const_iterator c1 = i1, c2 = c1 + 1; + + QVERIFY(i1 == i1); + QVERIFY(i1 == c1); + QVERIFY(c1 == i1); + QVERIFY(c1 == c1); + QVERIFY(i2 == i2); + QVERIFY(i2 == c2); + QVERIFY(c2 == i2); + QVERIFY(c2 == c2); + + QVERIFY(i1 != i2); + QVERIFY(i1 != c2); + QVERIFY(c1 != i2); + QVERIFY(c1 != c2); + QVERIFY(i2 != i1); + QVERIFY(i2 != c1); + QVERIFY(c2 != i1); + QVERIFY(c2 != c1); + + QString x1 = *i1; + QString x2 = *i2; + QString y1 = *c1; + QString y2 = *c2; + + QCOMPARE(x1, y1); + QCOMPARE(x2, y2); + + int i1Size = i1->size(); + int i2Size = i2->size(); + int c1Size = c1->size(); + int c2Size = c2->size(); + + QCOMPARE(i1Size, c1Size); + QCOMPARE(i2Size, c2Size); + } + + { + const int resultCount = 20; + + QFutureInterface e; + e.reportStarted(); + QFuture f = e.future(); + + for (int i = 0; i < resultCount; ++i) { + e.reportResult(i); + } + + e.reportFinished(); + + { + QFutureIterator it(f); + QFutureIterator it2(it); + } + + { + QFutureIterator it(f); + + for (int i = 0; i < resultCount - 1; ++i) { + QVERIFY(it.hasNext()); + QCOMPARE(it.peekNext(), i); + QCOMPARE(it.next(), i); + } + + QVERIFY(it.hasNext()); + QCOMPARE(it.peekNext(), resultCount - 1); + QCOMPARE(it.next(), resultCount - 1); + QVERIFY(it.hasNext() == false); + } + + { + QFutureIterator it(f); + QVERIFY(it.hasNext()); + it.toBack(); + QVERIFY(it.hasNext() == false); + it.toFront(); + QVERIFY(it.hasNext()); + } + } +} + +class SignalSlotObject : public QObject +{ +Q_OBJECT +public: + SignalSlotObject() + : finishedCalled(false), + canceledCalled(false), + rangeBegin(0), + rangeEnd(0) { } + +public slots: + void finished() + { +// qDebug() << "finished called"; + finishedCalled = true; + } + + void canceled() + { +// qDebug() << "canceled called"; + canceledCalled = true; + } + + void resultReady(int index) + { +// qDebug() << "result" << index << "ready"; + results.insert(index); + } + + void progressRange(int begin, int end) + { +// qDebug() << "progress range" << begin << end; + rangeBegin = begin; + rangeEnd = end; + } + + void progress(int progress) + { +// qDebug() << "progress" << progress; + reportedProgress.insert(progress); + } +public: + bool finishedCalled; + bool canceledCalled; + QSet results; + int rangeBegin; + int rangeEnd; + QSet reportedProgress; +}; + +void tst_QFuture::pause() +{ + QFutureInterface Interface; + + Interface.reportStarted(); + QFuture f = Interface.future(); + + QVERIFY(Interface.isPaused() == false); + f.pause(); + QVERIFY(Interface.isPaused() == true); + f.resume(); + QVERIFY(Interface.isPaused() == false); + f.togglePaused(); + QVERIFY(Interface.isPaused() == true); + f.togglePaused(); + QVERIFY(Interface.isPaused() == false); + + Interface.reportFinished(); +} + +const int resultCount = 1000; + +class ResultObject : public QObject +{ +Q_OBJECT +public slots: + void resultReady(int) + { + + } +public: +}; + +// Test that that the isPaused() on future result interface returns true +// if we report a lot of results that are not handled. +void tst_QFuture::throttling() +{ + { + QFutureInterface i; + + i.reportStarted(); + QFuture f = i.future(); + + QVERIFY(i.isThrottled() == false); + + i.setThrottled(true); + QVERIFY(i.isThrottled()); + + i.setThrottled(false); + QVERIFY(i.isThrottled() == false); + + i.setThrottled(true); + QVERIFY(i.isThrottled()); + + i.reportFinished(); + } +/* + QFutureInterface *Interface = new QFutureInterface(); + Interface.reportStarted(); + QFuture f = QFuture(Interface); + + ResultObject object; + f.connectTo(ThrottledResultReadyAtSignal, &object, SLOT(resultReady(int)), Qt::DirectConnection); + + for (int i = 0; i < 100; ++i) + Interface.reportResult(&i); + + QVERIFY(Interface.isPaused() == true); + + // read the results, this should resume the task. + for (int i = 0; i < 100; ++i) + f.throttledResult(i); + + QVERIFY(Interface.isPaused() == false); + Interface.reportFinished(); +*/ +} + +void tst_QFuture::voidConversions() +{ + QFutureInterface iface; + iface.reportStarted(); + + QFuture intFuture(&iface); + + int value = 10; + iface.reportFinished(&value); + + QFuture voidFuture(intFuture); + voidFuture = intFuture; + + QVERIFY(voidFuture == intFuture); +} + + +#ifndef QT_NO_EXCEPTIONS + +QFuture createExceptionFuture() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + + Exception e; + i.reportException(e); + i.reportFinished(); + return f; +} + +QFuture createExceptionResultFuture() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + int r = 0; + i.reportResult(r); + + Exception e; + i.reportException(e); + i.reportFinished(); + return f; +} + +class DerivedException : public Exception +{ +public: + void raise() const { throw *this; } + Exception *clone() const { return new DerivedException(*this); } +}; + +QFuture createDerivedExceptionFuture() +{ + QFutureInterface i; + i.reportStarted(); + QFuture f = i.future(); + + DerivedException e; + i.reportException(e); + i.reportFinished(); + return f; +} + +void tst_QFuture::exceptions() +{ + +// test throwing from waitForFinished +{ + QFuture f = createExceptionFuture(); + bool caught = false; + try { + f.waitForFinished(); + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); +} + +// test result() +{ + QFuture f = createExceptionResultFuture(); + bool caught = false; + try { + f.result(); + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); +} + +// test result() and destroy +{ + bool caught = false; + try { + createExceptionResultFuture().result(); + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); +} + +// test results() +{ + QFuture f = createExceptionResultFuture(); + bool caught = false; + try { + f.results(); + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); +} + +// test foreach +{ + QFuture f = createExceptionResultFuture(); + bool caught = false; + try { + foreach (int e, f.results()) { + Q_UNUSED(e); + QFAIL("did not get exception"); + } + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); +} + +// catch derived exceptions +{ + bool caught = false; + try { + createDerivedExceptionFuture().waitForFinished(); + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); +} + +{ + bool caught = false; + try { + createDerivedExceptionFuture().waitForFinished(); + } catch (DerivedException &) { + caught = true; + } + QVERIFY(caught); +} + +} + + +void tst_QFuture::exceptions_QTBUG18149() +{ + class MyClass + { + public: + ~MyClass() + { + QFuture f = createExceptionFuture(); + bool caught = false; + try { + f.waitForFinished(); + } catch (Exception &) { + caught = true; + } + QVERIFY(caught); + } + }; + + try { + MyClass m; + Q_UNUSED(m); + throw 0; + } catch (int) {} + +} + + +#endif // QT_NO_EXCEPTIONS + +#include "tst_qfuture.moc" + +#else +QTEST_NOOP_MAIN +#endif -- cgit v1.2.3