summaryrefslogtreecommitdiffstats
path: root/tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp')
-rw-r--r--tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp536
1 files changed, 536 insertions, 0 deletions
diff --git a/tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp b/tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp
new file mode 100644
index 0000000..82b70f6
--- /dev/null
+++ b/tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp
@@ -0,0 +1,536 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.0, 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.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtconcurrentthreadengine.h>
+#include <qtconcurrentexception.h>
+#include <QThread>
+#include <QtTest/QtTest>
+#include "../qfuture/versioncheck.h"
+
+#ifndef QT_NO_CONCURRENT_TEST
+
+using namespace QtConcurrent;
+
+class tst_threadengine: public QObject
+{
+ Q_OBJECT
+public:
+ void threadCount();
+private slots:
+ void runDirectly();
+ void result();
+ void runThroughStarter();
+ void cancel();
+ void throttle();
+ void multipleResults();
+ void stresstest();
+ void cancelQueuedSlowUser();
+#ifndef QT_NO_EXCEPTIONS
+ void exceptions();
+#endif
+};
+
+
+class PrintUser : public ThreadEngine<void>
+{
+public:
+ ThreadFunctionResult threadFunction()
+ {
+ QTest::qSleep(50);
+ QTest::qSleep(100);
+ return ThreadFinished;
+ }
+};
+
+void tst_threadengine::runDirectly()
+{
+ {
+ PrintUser engine;
+ engine.startSingleThreaded();
+ engine.startBlocking();
+ }
+ {
+ PrintUser *engine = new PrintUser();
+ QFuture<void> f = engine->startAsynchronously();
+ f.waitForFinished();
+ }
+}
+
+class StringResultUser : public ThreadEngine<QString>
+{
+public:
+ typedef QString ResultType;
+ StringResultUser()
+ : done(false) { }
+
+ bool shouldStartThread()
+ {
+ return !done;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ done = true;
+ return ThreadFinished;
+ }
+
+ QString *result()
+ {
+ foo = "Foo";
+ return &foo;
+ }
+ QString foo;
+ bool done;
+};
+
+void tst_threadengine::result()
+{
+ StringResultUser engine;
+ QCOMPARE(*engine.startBlocking(), QString("Foo"));
+}
+
+class VoidResultUser : public ThreadEngine<void>
+{
+public:
+ bool shouldStartThread()
+ {
+ return !done;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ done = true;
+ return ThreadFinished;
+ }
+
+ void *result()
+ {
+ return 0;
+ }
+ bool done;
+};
+
+void tst_threadengine::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"));
+ }
+}
+
+class CancelUser : public ThreadEngine<void>
+{
+public:
+ void *result()
+ {
+ return 0;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ while (this->isCanceled() == false)
+ {
+ QTest::qSleep(10);
+ }
+ return ThreadFinished;
+ }
+};
+
+void tst_threadengine::cancel()
+{
+ {
+ CancelUser *engine = new CancelUser();
+ QFuture<void> f = engine->startAsynchronously();
+ f.cancel();
+ f.waitForFinished();
+ }
+ {
+ CancelUser *engine = new CancelUser();
+ QFuture<void> f = engine->startAsynchronously();
+ QTest::qSleep(10);
+ f.cancel();
+ f.waitForFinished();
+ }
+}
+
+QAtomicInt count;
+class ThrottleAlwaysUser : public ThreadEngine<void>
+{
+public:
+ ThrottleAlwaysUser()
+ {
+ count = initialCount = 100;
+ finishing = false;
+ }
+
+ bool shouldStartThread()
+ {
+ return !finishing;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ forever {
+ const int local = count;
+ if (local == 0) {
+ finishing = true;
+ return ThreadFinished;
+ }
+
+ if (count.testAndSetOrdered(local, local - 1))
+ break;
+ }
+ return ThrottleThread;
+ }
+
+ bool finishing;
+ int initialCount;
+};
+
+// Test that a user task with a thread function that always
+// want to be throttled still completes. The thread engine
+// should make keep one thread running at all times.
+void tst_threadengine::throttle()
+{
+ const int repeats = 10;
+ for (int i = 0; i < repeats; ++i) {
+ QFuture<void> f = (new ThrottleAlwaysUser())->startAsynchronously();
+ f.waitForFinished();
+ QCOMPARE(int(count), 0);
+ }
+
+ for (int i = 0; i < repeats; ++i) {
+ ThrottleAlwaysUser t;
+ t.startBlocking();
+ QCOMPARE(int(count), 0);
+ }
+}
+
+QSet<QThread *> threads;
+QMutex mutex;
+class ThreadCountUser : public ThreadEngine<void>
+{
+public:
+ ThreadCountUser(bool finishImmediately = false)
+ {
+ threads.clear();
+ finishing = finishImmediately;
+ }
+
+ bool shouldStartThread()
+ {
+ return !finishing;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ {
+ QMutexLocker lock(&mutex);
+ threads.insert(QThread::currentThread());
+ }
+ QTest::qSleep(10);
+ finishing = true;
+ return ThreadFinished;
+ }
+
+ bool finishing;
+};
+
+void tst_threadengine::threadCount()
+{
+ const int repeats = 10;
+ for (int i = 0; i < repeats; ++i) {
+ ThreadCountUser t;
+ t.startBlocking();
+ QCOMPARE(threads.count(), QThreadPool::globalInstance()->maxThreadCount() + 1); // +1 for the main thread.
+
+ (new ThreadCountUser())->startAsynchronously().waitForFinished();
+ QCOMPARE(threads.count(), QThreadPool::globalInstance()->maxThreadCount());
+ }
+
+ // 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();
+ QCOMPARE(threads.count(), 1);
+
+ (new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished();
+ QCOMPARE(threads.count(), 1);
+ }
+}
+
+class MultipleResultsUser : public ThreadEngine<int>
+{
+public:
+ bool shouldStartThread()
+ {
+ return false;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ for (int i = 0; i < 10; ++i)
+ this->reportResult(&i);
+ return ThreadFinished;
+ }
+};
+
+
+void tst_threadengine::multipleResults()
+{
+ MultipleResultsUser *engine = new MultipleResultsUser();
+ QFuture<int> f = engine->startAsynchronously();
+ QCOMPARE(f.results().count() , 10);
+ QCOMPARE(f.resultAt(0), 0);
+ QCOMPARE(f.resultAt(5), 5);
+ QCOMPARE(f.resultAt(9), 9);
+ f.waitForFinished();
+}
+
+
+class NoThreadsUser : public ThreadEngine<void>
+{
+public:
+ bool shouldStartThread()
+ {
+ return false;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ return ThreadFinished;
+ }
+
+ void *result()
+ {
+ return 0;
+ }
+};
+
+void tst_threadengine::stresstest()
+{
+ const int times = 20000;
+
+ for (int i = 0; i < times; ++i) {
+ VoidResultUser *engine = new VoidResultUser();
+ engine->startAsynchronously().waitForFinished();
+ }
+
+ for (int i = 0; i < times; ++i) {
+ VoidResultUser *engine = new VoidResultUser();
+ engine->startAsynchronously();
+ }
+
+ for (int i = 0; i < times; ++i) {
+ VoidResultUser *engine = new VoidResultUser();
+ engine->startAsynchronously().waitForFinished();
+ }
+}
+
+const int sleepTime = 20;
+class SlowUser : public ThreadEngine<void>
+{
+public:
+ bool shouldStartThread() { return false; }
+ ThreadFunctionResult threadFunction() { QTest::qSleep(sleepTime); return ThreadFinished; }
+};
+
+void tst_threadengine::cancelQueuedSlowUser()
+{
+ const int times = 100;
+
+ QTime t;
+ t.start();
+
+ {
+ QList<QFuture<void> > futures;
+ for (int i = 0; i < times; ++i) {
+ SlowUser *engine = new SlowUser();
+ futures.append(engine->startAsynchronously());
+ }
+
+ foreach(QFuture<void> future, futures)
+ future.cancel();
+ }
+
+ QVERIFY(t.elapsed() < (sleepTime * times) / 2);
+}
+
+#ifndef QT_NO_EXCEPTIONS
+
+class QtConcurrentExceptionThrower : public ThreadEngine<void>
+{
+public:
+ QtConcurrentExceptionThrower(QThread *blockThread = 0)
+ {
+ this->blockThread = blockThread;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ QTest::qSleep(50);
+ throw QtConcurrent::Exception();
+ return ThreadFinished;
+ }
+ QThread *blockThread;
+};
+
+class UnrelatedExceptionThrower : public ThreadEngine<void>
+{
+public:
+ UnrelatedExceptionThrower(QThread *blockThread = 0)
+ {
+ this->blockThread = blockThread;
+ }
+
+ ThreadFunctionResult threadFunction()
+ {
+ QTest::qSleep(50);
+ throw int();
+ return ThreadFinished;
+ }
+ QThread *blockThread;
+};
+
+void tst_threadengine::exceptions()
+{
+ // Asynchronous mode:
+ {
+ bool caught = false;
+ try {
+ QtConcurrentExceptionThrower *e = new QtConcurrentExceptionThrower();
+ QFuture<void> f = e->startAsynchronously();
+ f.waitForFinished();
+ } catch (Exception &e) {
+ caught = true;
+ }
+ if (!caught)
+ QFAIL("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 (Exception &e) {
+ caught = true;
+ }
+
+ if (!caught)
+ QFAIL("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 (Exception &e) {
+ caught = true;
+ }
+
+ if (!caught)
+ QFAIL("did not get exception");
+ }
+
+ // Asynchronous mode:
+ {
+ bool caught = false;
+ try {
+ UnrelatedExceptionThrower *e = new UnrelatedExceptionThrower();
+ QFuture<void> f = e->startAsynchronously();
+ f.waitForFinished();
+ } catch (QtConcurrent::UnhandledException &e) {
+ caught = true;
+ }
+ if (!caught)
+ QFAIL("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 (QtConcurrent::UnhandledException &e) {
+ caught = true;
+ }
+
+ if (!caught)
+ QFAIL("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 (QtConcurrent::UnhandledException &e) {
+ caught = true;
+ }
+
+ if (!caught)
+ QFAIL("did not get exception");
+ }
+}
+
+#endif
+
+QTEST_MAIN(tst_threadengine)
+
+#include "tst_qtconcurrentthreadengine.moc"
+
+#else
+QTEST_NOOP_MAIN
+#endif