OSDN Git Service

add qmutex, qthread and qwaitcondition tests
authorIvailo Monev <xakepa10@laimg.moc>
Tue, 2 Jul 2019 18:15:55 +0000 (18:15 +0000)
committerIvailo Monev <xakepa10@laimg.moc>
Tue, 2 Jul 2019 18:15:55 +0000 (18:15 +0000)
Signed-off-by: Ivailo Monev <xakepa10@laimg.moc>
tests/auto/qmutex/CMakeLists.txt [new file with mode: 0644]
tests/auto/qmutex/tst_qmutex.cpp [new file with mode: 0644]
tests/auto/qthread/CMakeLists.txt [new file with mode: 0644]
tests/auto/qthread/tst_qthread.cpp [new file with mode: 0644]
tests/auto/qwaitcondition/CMakeLists.txt [new file with mode: 0644]
tests/auto/qwaitcondition/tst_qwaitcondition.cpp [new file with mode: 0644]

diff --git a/tests/auto/qmutex/CMakeLists.txt b/tests/auto/qmutex/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0256e8f
--- /dev/null
@@ -0,0 +1,3 @@
+katie_test(tst_qmutex
+    ${CMAKE_CURRENT_SOURCE_DIR}/tst_qmutex.cpp
+)
diff --git a/tests/auto/qmutex/tst_qmutex.cpp b/tests/auto/qmutex/tst_qmutex.cpp
new file mode 100644 (file)
index 0000000..37e4d06
--- /dev/null
@@ -0,0 +1,580 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016-2019 Ivailo Monev
+**
+** This file is part of the test suite of the Katie Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qatomic.h>
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include <qsemaphore.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QMutex : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_QMutex();
+    virtual ~tst_QMutex();
+
+private slots:
+    void tryLock();
+    void lock_unlock_locked_tryLock();
+    void stressTest();
+    void tryLockRace();
+    void qtbug16115_trylock();
+};
+
+static const int iterations = 100;
+
+tst_QMutex::tst_QMutex()
+
+{
+}
+
+tst_QMutex::~tst_QMutex()
+{
+}
+
+QAtomicInt lockCount(0);
+QMutex normalMutex, recursiveMutex(QMutex::Recursive);
+QSemaphore testsTurn;
+QSemaphore threadsTurn;
+
+void tst_QMutex::tryLock()
+{
+    // test non-recursive mutex
+    {
+        class Thread : public QThread
+        {
+        public:
+            void run()
+            {
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QVERIFY(!normalMutex.tryLock());
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QVERIFY(normalMutex.tryLock());
+                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+                QVERIFY(!normalMutex.tryLock());
+                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+                normalMutex.unlock();
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QTime timer;
+                timer.start();
+                QVERIFY(!normalMutex.tryLock(1000));
+                QVERIFY(timer.elapsed() >= 1000);
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                timer.start();
+                QVERIFY(normalMutex.tryLock(1000));
+                QVERIFY(timer.elapsed() <= 1000);
+                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+                timer.start();
+                QVERIFY(!normalMutex.tryLock(1000));
+                QVERIFY(timer.elapsed() >= 1000);
+                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+                normalMutex.unlock();
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QVERIFY(!normalMutex.tryLock(0));
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                timer.start();
+                QVERIFY(normalMutex.tryLock(0));
+                QVERIFY(timer.elapsed() < 1000);
+                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+                QVERIFY(!normalMutex.tryLock(0));
+                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+                normalMutex.unlock();
+                testsTurn.release();
+
+                threadsTurn.acquire();
+            }
+        };
+
+        Thread thread;
+        thread.start();
+
+        // thread can't acquire lock
+        testsTurn.acquire();
+        normalMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+        threadsTurn.release();
+
+        // thread can acquire lock
+        testsTurn.acquire();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+        normalMutex.unlock();
+        threadsTurn.release();
+
+        // thread can't acquire lock, timeout = 1000
+        testsTurn.acquire();
+        normalMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+        threadsTurn.release();
+
+        // thread can acquire lock, timeout = 1000
+        testsTurn.acquire();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+        normalMutex.unlock();
+        threadsTurn.release();
+
+        // thread can't acquire lock, timeout = 0
+        testsTurn.acquire();
+        normalMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+        threadsTurn.release();
+
+        // thread can acquire lock, timeout = 0
+        testsTurn.acquire();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+        normalMutex.unlock();
+        threadsTurn.release();
+
+        // wait for thread to finish
+        testsTurn.acquire();
+        threadsTurn.release();
+        thread.wait();
+    }
+
+    // test recursive mutex
+    {
+        class Thread : public QThread
+        {
+        public:
+            void run()
+            {
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QVERIFY(!recursiveMutex.tryLock());
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QVERIFY(recursiveMutex.tryLock());
+                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+                QVERIFY(recursiveMutex.tryLock());
+                QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+                QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+                recursiveMutex.unlock();
+                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+                recursiveMutex.unlock();
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QTime timer;
+                timer.start();
+                QVERIFY(!recursiveMutex.tryLock(1000));
+                QVERIFY(timer.elapsed() >= 1000);
+                QVERIFY(!recursiveMutex.tryLock(0));
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                timer.start();
+                QVERIFY(recursiveMutex.tryLock(1000));
+                QVERIFY(timer.elapsed() <= 1000);
+                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+                QVERIFY(recursiveMutex.tryLock(1000));
+                QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+                QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+                recursiveMutex.unlock();
+                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+                recursiveMutex.unlock();
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                QVERIFY(!recursiveMutex.tryLock(0));
+                QVERIFY(!recursiveMutex.tryLock(0));
+                testsTurn.release();
+
+                threadsTurn.acquire();
+                timer.start();
+                QVERIFY(recursiveMutex.tryLock(0));
+                QVERIFY(timer.elapsed() < 1000);
+                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+                QVERIFY(recursiveMutex.tryLock(0));
+                QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+                QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+                recursiveMutex.unlock();
+                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+                recursiveMutex.unlock();
+                testsTurn.release();
+
+                threadsTurn.acquire();
+            }
+        };
+
+        Thread thread;
+        thread.start();
+
+        // thread can't acquire lock
+        testsTurn.acquire();
+        recursiveMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+        recursiveMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+        threadsTurn.release();
+
+        // thread can acquire lock
+        testsTurn.acquire();
+        QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+        recursiveMutex.unlock();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+        recursiveMutex.unlock();
+        threadsTurn.release();
+
+        // thread can't acquire lock, timeout = 1000
+        testsTurn.acquire();
+        recursiveMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+        recursiveMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+        threadsTurn.release();
+
+        // thread can acquire lock, timeout = 1000
+        testsTurn.acquire();
+        QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+        recursiveMutex.unlock();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+        recursiveMutex.unlock();
+        threadsTurn.release();
+
+        // thread can't acquire lock, timeout = 0
+        testsTurn.acquire();
+        recursiveMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+        recursiveMutex.lock();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+        threadsTurn.release();
+
+        // thread can acquire lock, timeout = 0
+        testsTurn.acquire();
+        QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+        recursiveMutex.unlock();
+        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+        recursiveMutex.unlock();
+        threadsTurn.release();
+
+        // stop thread
+        testsTurn.acquire();
+        threadsTurn.release();
+        thread.wait();
+    }
+}
+
+class mutex_Thread : public QThread
+{
+public:
+    QMutex mutex;
+    QWaitCondition cond;
+
+    QMutex &test_mutex;
+
+    inline mutex_Thread(QMutex &m) : test_mutex(m) { }
+
+    void run()
+    {
+       test_mutex.lock();
+
+       mutex.lock();
+       for (int i = 0; i < iterations; ++i) {
+           cond.wakeOne();
+           cond.wait(&mutex);
+       }
+       mutex.unlock();
+
+       test_mutex.unlock();
+    }
+};
+
+class rmutex_Thread : public QThread
+{
+public:
+    QMutex mutex;
+    QWaitCondition cond;
+
+    QMutex &test_mutex;
+
+    inline rmutex_Thread(QMutex &m) : test_mutex(m) { }
+
+    void run()
+    {
+       test_mutex.lock();
+       test_mutex.lock();
+       test_mutex.lock();
+       test_mutex.lock();
+
+       mutex.lock();
+       for (int i = 0; i < iterations; ++i) {
+           cond.wakeOne();
+           cond.wait(&mutex);
+       }
+       mutex.unlock();
+
+       test_mutex.unlock();
+       test_mutex.unlock();
+       test_mutex.unlock();
+       test_mutex.unlock();
+    }
+};
+
+#ifdef QT3_SUPPORT
+#define VERIFY_LOCKED(x) QVERIFY((x).locked())
+#define VERIFY_NLOCKED(x) QVERIFY(!(x).locked())
+#else
+#define VERIFY_LOCKED(x)
+#define VERIFY_NLOCKED(x)
+#endif // QT3_SUPPORT
+
+void tst_QMutex::lock_unlock_locked_tryLock()
+{
+    // normal mutex
+    QMutex mutex;
+    mutex_Thread thread(mutex);
+
+    QMutex rmutex(QMutex::Recursive);
+    rmutex_Thread rthread(rmutex);
+
+    for (int i = 0; i < iterations; ++i) {
+       // normal mutex
+       VERIFY_NLOCKED(mutex);
+       QVERIFY(mutex.tryLock());
+       mutex.unlock();
+
+       thread.mutex.lock();
+       thread.start();
+
+       for (int j = 0; j < iterations; ++j) {
+           QVERIFY(thread.cond.wait(&thread.mutex, 10000));
+           VERIFY_LOCKED(mutex);
+           QVERIFY(!mutex.tryLock());
+
+           thread.cond.wakeOne();
+       }
+
+       thread.mutex.unlock();
+
+       QVERIFY(thread.wait(10000));
+        VERIFY_NLOCKED(mutex);
+       QVERIFY(mutex.tryLock());
+
+       mutex.unlock();
+
+       // recursive mutex
+        VERIFY_NLOCKED(rmutex);
+       QVERIFY(rmutex.tryLock());
+       QVERIFY(rmutex.tryLock());
+       QVERIFY(rmutex.tryLock());
+       QVERIFY(rmutex.tryLock());
+
+       rmutex.unlock();
+       rmutex.unlock();
+       rmutex.unlock();
+       rmutex.unlock();
+
+       rthread.mutex.lock();
+       rthread.start();
+
+       for (int k = 0; k < iterations; ++k) {
+           QVERIFY(rthread.cond.wait(&rthread.mutex, 10000));
+            VERIFY_LOCKED(rmutex);
+           QVERIFY(!rmutex.tryLock());
+
+           rthread.cond.wakeOne();
+       }
+
+       rthread.mutex.unlock();
+
+       QVERIFY(rthread.wait(10000));
+        VERIFY_NLOCKED(rmutex);
+       QVERIFY(rmutex.tryLock());
+       QVERIFY(rmutex.tryLock());
+       QVERIFY(rmutex.tryLock());
+       QVERIFY(rmutex.tryLock());
+
+       rmutex.unlock();
+       rmutex.unlock();
+       rmutex.unlock();
+       rmutex.unlock();
+    }
+}
+
+enum { one_minute = 60 * 1000, threadCount = 10 };
+
+class StressTestThread : public QThread
+{
+    QTime t;
+public:
+    static QAtomicInt lockCount;
+    static QAtomicInt sentinel;
+    static QMutex mutex;
+    static int errorCount;
+    void start()
+    {
+        t.start();
+        QThread::start();
+    }
+    void run()
+    {
+        while (t.elapsed() < one_minute) {
+            mutex.lock();
+            if (sentinel.ref()) ++errorCount;
+            if (!sentinel.deref()) ++errorCount;
+            lockCount.ref();
+            mutex.unlock();
+            if (mutex.tryLock()) {
+                if (sentinel.ref()) ++errorCount;
+                if (!sentinel.deref()) ++errorCount;
+                lockCount.ref();
+                mutex.unlock();
+            }
+        }
+    }
+};
+QMutex StressTestThread::mutex;
+QAtomicInt StressTestThread::lockCount = QAtomicInt(0);
+QAtomicInt StressTestThread::sentinel = QAtomicInt(-1);
+int StressTestThread::errorCount = 0;
+
+void tst_QMutex::stressTest()
+{
+    StressTestThread threads[threadCount];
+    for (int i = 0; i < threadCount; ++i)
+        threads[i].start();
+    QVERIFY(threads[0].wait(one_minute + 10000));
+    for (int i = 1; i < threadCount; ++i)
+        QVERIFY(threads[i].wait(10000));
+    QCOMPARE(StressTestThread::errorCount, 0);
+    qDebug("locked %d times", int(StressTestThread::lockCount));
+}
+
+class TryLockRaceThread : public QThread
+{
+public:
+    static QMutex mutex;
+
+    void run()
+    {
+        QTime t;
+        t.start();
+        do {
+            if (mutex.tryLock())
+                mutex.unlock();
+        } while (t.elapsed() < 20000);
+    }
+};
+QMutex TryLockRaceThread::mutex;
+
+void tst_QMutex::tryLockRace()
+{
+    // mutex not in use, should be able to lock it
+    QVERIFY(TryLockRaceThread::mutex.tryLock());
+    TryLockRaceThread::mutex.unlock();
+
+    // try to break tryLock
+    TryLockRaceThread thread[threadCount];
+    for (int i = 0; i < threadCount; ++i)
+        thread[i].start();
+    for (int i = 0; i < threadCount; ++i)
+        QVERIFY(thread[i].wait());
+
+    // mutex not in use, should be able to lock it
+    QVERIFY(TryLockRaceThread::mutex.tryLock());
+    TryLockRaceThread::mutex.unlock();
+}
+
+// Variable that will be protected by the mutex. Volatile so that the
+// the optimiser doesn't mess with it based on the increment-then-decrement
+// usage pattern.
+static volatile int qtbug16115_trylock_counter;
+// Counter for how many times the protected variable has an incorrect value.
+static int qtbug16115_failure_count = 0;
+
+void tst_QMutex::qtbug16115_trylock()
+{
+    //Used to deadlock on unix
+    struct TrylockThread : QThread {
+        TrylockThread(QMutex &mut) : mut(mut) {}
+        QMutex &mut;
+        void run() {
+            for (int i = 0; i < 1000000; ++i) {
+                if (mut.tryLock(0)) {
+                    if ((++qtbug16115_trylock_counter) != 1)
+                        ++qtbug16115_failure_count;
+                    if ((--qtbug16115_trylock_counter) != 0)
+                        ++qtbug16115_failure_count;
+                    mut.unlock();
+                }
+            }
+        }
+    };
+    QMutex mut;
+    TrylockThread t1(mut);
+    TrylockThread t2(mut);
+    TrylockThread t3(mut);
+    t1.start();
+    t2.start();
+    t3.start();
+
+    for (int i = 0; i < 1000000; ++i) {
+        mut.lock();
+        if ((++qtbug16115_trylock_counter) != 1)
+            ++qtbug16115_failure_count;
+        if ((--qtbug16115_trylock_counter) != 0)
+            ++qtbug16115_failure_count;
+        mut.unlock();
+    }
+    t1.wait();
+    t2.wait();
+    t3.wait();
+    QCOMPARE(qtbug16115_failure_count, 0);
+}
+
+QTEST_MAIN(tst_QMutex)
+
+#include "moc_tst_qmutex.cpp"
diff --git a/tests/auto/qthread/CMakeLists.txt b/tests/auto/qthread/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4101dda
--- /dev/null
@@ -0,0 +1,3 @@
+katie_test(tst_qthread
+    ${CMAKE_CURRENT_SOURCE_DIR}/tst_qthread.cpp
+)
diff --git a/tests/auto/qthread/tst_qthread.cpp b/tests/auto/qthread/tst_qthread.cpp
new file mode 100644 (file)
index 0000000..d0716aa
--- /dev/null
@@ -0,0 +1,1450 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016-2019 Ivailo Monev
+**
+** This file is part of the test suite of the Katie Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qtimer.h>
+#include <qwaitcondition.h>
+#include <qdebug.h>
+#include <qmetaobject.h>
+#include <qsemaphore.h>
+
+#ifdef Q_OS_UNIX
+#include <pthread.h>
+#endif
+#if defined(Q_OS_WINCE)
+#include <windows.h>
+#elif defined(Q_OS_WIN)
+#include <process.h>
+#include <windows.h>
+#endif
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QThread : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_QThread();
+    virtual ~tst_QThread();
+
+private slots:
+    void currentThreadId();
+    void currentThread();
+    void idealThreadCount();
+    void isFinished();
+    void isRunning();
+    void setPriority();
+    void priority();
+    void setStackSize();
+    void stackSize();
+    void exit();
+    void start();
+    void terminate();
+    void quit();
+    void wait();
+    void started();
+    void finished();
+    void terminated();
+    void run();
+    void exec();
+    void setTerminationEnabled();
+    void sleep();
+    void msleep();
+    void usleep();
+
+    void nativeThreadAdoption();
+    void adoptedThreadAffinity();
+    void adoptedThreadSetPriority();
+    void adoptedThreadExit();
+    void adoptedThreadExec();
+    void adoptThreadExitWithActiveTimer();
+    void adoptedThreadFinished();
+    void adoptedThreadExecFinished();
+    void adoptMultipleThreads();
+    void adoptMultipleThreadsOverlap();
+
+    void QTBUG13810_exitAndStart();
+    void QTBUG15378_exitAndExec();
+
+    void connectThreadFinishedSignalToObjectDeleteLaterSlot();
+    void wait2();
+    void wait3_slowDestructor();
+    void destroyFinishRace();
+    void startFinishRace();
+    void startAndQuitCustomEventLoop();
+    void isRunningInFinished();
+
+    void stressTest();
+#ifdef Q_OS_SYMBIAN
+    void threadNameTest();
+#endif
+};
+
+enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
+
+class SignalRecorder : public QObject
+{
+    Q_OBJECT
+public:
+    QAtomicInt activationCount;
+
+    inline SignalRecorder()
+    { activationCount = 0; }
+
+    bool wasActivated()
+    { return activationCount > 0; }
+
+public slots:
+    void slot();
+};
+
+void SignalRecorder::slot()
+{ activationCount.ref(); }
+
+class Current_Thread : public QThread
+{
+public:
+    Qt::HANDLE id;
+    QThread *thread;
+
+    void run()
+    {
+        id = QThread::currentThreadId();
+        thread = QThread::currentThread();
+    }
+};
+
+class Simple_Thread : public QThread
+{
+public:
+    QMutex mutex;
+    QWaitCondition cond;
+
+    void run()
+    {
+        QMutexLocker locker(&mutex);
+        cond.wakeOne();
+    }
+};
+
+class Exit_Object : public QObject
+{
+    Q_OBJECT
+public:
+    QThread *thread;
+    int code;
+public slots:
+    void slot()
+    { thread->exit(code); }
+};
+
+class Exit_Thread : public Simple_Thread
+{
+public:
+    Exit_Object *object;
+    int code;
+    int result;
+
+    void run()
+    {
+        Simple_Thread::run();
+        if (object) {
+            object->thread = this;
+            object->code = code;
+            QTimer::singleShot(100, object, SLOT(slot()));
+        }
+        result = exec();
+    }
+};
+
+class Terminate_Thread : public Simple_Thread
+{
+public:
+    void run()
+    {
+        setTerminationEnabled(false);
+        {
+            QMutexLocker locker(&mutex);
+            cond.wakeOne();
+            cond.wait(&mutex, five_minutes);
+        }
+        setTerminationEnabled(true);
+        qFatal("tst_QThread: test case hung");
+    }
+};
+
+class Quit_Object : public QObject
+{
+    Q_OBJECT
+public:
+    QThread *thread;
+public slots:
+    void slot()
+    { thread->quit(); }
+};
+
+class Quit_Thread : public Simple_Thread
+{
+public:
+    Quit_Object *object;
+    int result;
+
+    void run()
+    {
+        Simple_Thread::run();
+        if (object) {
+            object->thread = this;
+            QTimer::singleShot(100, object, SLOT(slot()));
+        }
+        result = exec();
+    }
+};
+
+class Sleep_Thread : public Simple_Thread
+{
+public:
+    enum SleepType { Second, Millisecond, Microsecond };
+
+    SleepType sleepType;
+    int interval;
+
+    int elapsed; // result, in *MILLISECONDS*
+
+    void run()
+    {
+        QMutexLocker locker(&mutex);
+
+        elapsed = 0;
+        QTime time;
+        time.start();
+        switch (sleepType) {
+        case Second:
+            sleep(interval);
+            break;
+        case Millisecond:
+            msleep(interval);
+            break;
+        case Microsecond:
+            usleep(interval);
+            break;
+        }
+        elapsed = time.elapsed();
+
+        cond.wakeOne();
+    }
+};
+
+tst_QThread::tst_QThread()
+
+{
+}
+
+tst_QThread::~tst_QThread()
+{
+
+}
+
+void tst_QThread::currentThreadId()
+{
+    Current_Thread thread;
+    thread.id = 0;
+    thread.thread = 0;
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(thread.id != 0);
+    QVERIFY(thread.id != QThread::currentThreadId());
+}
+
+void tst_QThread::currentThread()
+{
+    QVERIFY(QThread::currentThread() != 0);
+    QCOMPARE(QThread::currentThread(), thread());
+
+    Current_Thread thread;
+    thread.id = 0;
+    thread.thread = 0;
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+    QCOMPARE(thread.thread, (QThread *)&thread);
+}
+
+void tst_QThread::idealThreadCount()
+{
+    QVERIFY(QThread::idealThreadCount() > 0);
+    qDebug() << "Available cpu cores:" << QThread::idealThreadCount();
+}
+
+void tst_QThread::isFinished()
+{
+    Simple_Thread thread;
+    QVERIFY(!thread.isFinished());
+    QMutexLocker locker(&thread.mutex);
+    thread.start();
+    QVERIFY(!thread.isFinished());
+    thread.cond.wait(locker.mutex());
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(thread.isFinished());
+}
+
+void tst_QThread::isRunning()
+{
+    Simple_Thread thread;
+    QVERIFY(!thread.isRunning());
+    QMutexLocker locker(&thread.mutex);
+    thread.start();
+    QVERIFY(thread.isRunning());
+    thread.cond.wait(locker.mutex());
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(!thread.isRunning());
+}
+
+void tst_QThread::setPriority()
+{
+    Simple_Thread thread;
+
+    // cannot change the priority, since the thread is not running
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::IdlePriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::LowestPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::LowPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::NormalPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::HighPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::HighestPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::TimeCriticalPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QMutexLocker locker(&thread.mutex);
+    thread.start();
+
+    // change the priority of a running thread
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    thread.setPriority(QThread::IdlePriority);
+    QCOMPARE(thread.priority(), QThread::IdlePriority);
+    thread.setPriority(QThread::LowestPriority);
+    QCOMPARE(thread.priority(), QThread::LowestPriority);
+    thread.setPriority(QThread::LowPriority);
+    QCOMPARE(thread.priority(), QThread::LowPriority);
+    thread.setPriority(QThread::NormalPriority);
+    QCOMPARE(thread.priority(), QThread::NormalPriority);
+    thread.setPriority(QThread::HighPriority);
+    QCOMPARE(thread.priority(), QThread::HighPriority);
+    thread.setPriority(QThread::HighestPriority);
+    QCOMPARE(thread.priority(), QThread::HighestPriority);
+    thread.setPriority(QThread::TimeCriticalPriority);
+    QCOMPARE(thread.priority(), QThread::TimeCriticalPriority);
+    thread.cond.wait(locker.mutex());
+    QVERIFY(thread.wait(five_minutes));
+
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::IdlePriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::LowestPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::LowPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::NormalPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::HighPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::HighestPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+    QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+    thread.setPriority(QThread::TimeCriticalPriority);
+    QCOMPARE(thread.priority(), QThread::InheritPriority);
+}
+
+void tst_QThread::priority()
+{ DEPENDS_ON("setPriority"); }
+
+void tst_QThread::setStackSize()
+{
+    Simple_Thread thread;
+    QCOMPARE(thread.stackSize(), 0u);
+    thread.setStackSize(8192u);
+    QCOMPARE(thread.stackSize(), 8192u);
+    thread.setStackSize(0u);
+    QCOMPARE(thread.stackSize(), 0u);
+}
+
+void tst_QThread::stackSize()
+{
+    DEPENDS_ON("setStackSize");
+}
+
+void tst_QThread::exit()
+{
+    Exit_Thread thread;
+    thread.object = new Exit_Object;
+    thread.object->moveToThread(&thread);
+    thread.code = 42;
+    thread.result = 0;
+    QVERIFY(!thread.isFinished());
+    QVERIFY(!thread.isRunning());
+    QMutexLocker locker(&thread.mutex);
+    thread.start();
+    QVERIFY(thread.isRunning());
+    QVERIFY(!thread.isFinished());
+    thread.cond.wait(locker.mutex());
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(thread.isFinished());
+    QVERIFY(!thread.isRunning());
+    QCOMPARE(thread.result, thread.code);
+    delete thread.object;
+
+    Exit_Thread thread2;
+    thread2.object = 0;
+    thread2.code = 53;
+    thread2.result = 0;
+    QMutexLocker locker2(&thread2.mutex);
+    thread2.start();
+    thread2.exit(thread2.code);
+    thread2.cond.wait(locker2.mutex());
+    QVERIFY(thread2.wait(five_minutes));
+    QCOMPARE(thread2.result, thread2.code);
+}
+
+void tst_QThread::start()
+{
+    QThread::Priority priorities[] = {
+        QThread::IdlePriority,
+        QThread::LowestPriority,
+        QThread::LowPriority,
+        QThread::NormalPriority,
+        QThread::HighPriority,
+        QThread::HighestPriority,
+        QThread::TimeCriticalPriority,
+        QThread::InheritPriority
+    };
+    const int prio_count = sizeof(priorities) / sizeof(QThread::Priority);
+
+    for (int i = 0; i < prio_count; ++i) {
+        Simple_Thread thread;
+        QVERIFY(!thread.isFinished());
+        QVERIFY(!thread.isRunning());
+        QMutexLocker locker(&thread.mutex);
+        thread.start(priorities[i]);
+        QVERIFY(thread.isRunning());
+        QVERIFY(!thread.isFinished());
+        thread.cond.wait(locker.mutex());
+        QVERIFY(thread.wait(five_minutes));
+        QVERIFY(thread.isFinished());
+        QVERIFY(!thread.isRunning());
+    }
+}
+
+void tst_QThread::terminate()
+{
+    Terminate_Thread thread;
+    {
+        QMutexLocker locker(&thread.mutex);
+        thread.start();
+        QVERIFY(thread.cond.wait(locker.mutex(), five_minutes));
+        thread.terminate();
+        thread.cond.wakeOne();
+    }
+    QVERIFY(thread.wait(five_minutes));
+}
+
+void tst_QThread::quit()
+{
+    Quit_Thread thread;
+    thread.object = new Quit_Object;
+    thread.object->moveToThread(&thread);
+    thread.result = -1;
+    QVERIFY(!thread.isFinished());
+    QVERIFY(!thread.isRunning());
+    QMutexLocker locker(&thread.mutex);
+    thread.start();
+    QVERIFY(thread.isRunning());
+    QVERIFY(!thread.isFinished());
+    thread.cond.wait(locker.mutex());
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(thread.isFinished());
+    QVERIFY(!thread.isRunning());
+    QCOMPARE(thread.result, 0);
+    delete thread.object;
+
+    Quit_Thread thread2;
+    thread2.object = 0;
+    thread2.result = -1;
+    QMutexLocker locker2(&thread2.mutex);
+    thread2.start();
+    thread2.quit();
+    thread2.cond.wait(locker2.mutex());
+    QVERIFY(thread2.wait(five_minutes));
+    QCOMPARE(thread2.result, 0);
+}
+
+void tst_QThread::wait()
+{
+    DEPENDS_ON("isRunning");
+    DEPENDS_ON("isFinished");
+}
+
+void tst_QThread::started()
+{
+    SignalRecorder recorder;
+    Simple_Thread thread;
+    connect(&thread, SIGNAL(started()), &recorder, SLOT(slot()), Qt::DirectConnection);
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::finished()
+{
+    SignalRecorder recorder;
+    Simple_Thread thread;
+    connect(&thread, SIGNAL(finished()), &recorder, SLOT(slot()), Qt::DirectConnection);
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::terminated()
+{
+    SignalRecorder recorder;
+    Terminate_Thread thread;
+    connect(&thread, SIGNAL(terminated()), &recorder, SLOT(slot()), Qt::DirectConnection);
+    {
+        QMutexLocker locker(&thread.mutex);
+        thread.start();
+        thread.cond.wait(locker.mutex());
+        thread.terminate();
+        thread.cond.wakeOne();
+    }
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::run()
+{ DEPENDS_ON("wait()"); }
+
+void tst_QThread::exec()
+{
+    DEPENDS_ON("exit()");
+    DEPENDS_ON("quit()");
+
+    class MultipleExecThread : public QThread
+    {
+    public:
+        int res1, res2;
+
+        MultipleExecThread() : res1(-2), res2(-2) { }
+
+        void run()
+        {
+            {
+                Exit_Object o;
+                o.thread = this;
+                o.code = 1;
+                QTimer::singleShot(100, &o, SLOT(slot()));
+                res1 = exec();
+            }
+            {
+                Exit_Object o;
+                o.thread = this;
+                o.code = 2;
+                QTimer::singleShot(100, &o, SLOT(slot()));
+                res2 = exec();
+            }
+        }
+    };
+
+    MultipleExecThread thread;
+    thread.start();
+    QVERIFY(thread.wait());
+
+    QCOMPARE(thread.res1, 1);
+    QCOMPARE(thread.res2, 2);
+}
+
+void tst_QThread::setTerminationEnabled()
+{ DEPENDS_ON("terminate"); }
+
+void tst_QThread::sleep()
+{
+    Sleep_Thread thread;
+    thread.sleepType = Sleep_Thread::Second;
+    thread.interval = 2;
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+    QVERIFY(thread.elapsed >= 2000);
+}
+
+void tst_QThread::msleep()
+{
+    Sleep_Thread thread;
+    thread.sleepType = Sleep_Thread::Millisecond;
+    thread.interval = 120;
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+#if defined (Q_OS_WIN)
+    // Since the resolution of QTime is so coarse...
+    QVERIFY(thread.elapsed >= 100);
+#else
+    QVERIFY(thread.elapsed >= 120);
+#endif
+}
+
+void tst_QThread::usleep()
+{
+    Sleep_Thread thread;
+    thread.sleepType = Sleep_Thread::Microsecond;
+    thread.interval = 120000;
+    thread.start();
+    QVERIFY(thread.wait(five_minutes));
+#if defined (Q_OS_WIN)
+    // Since the resolution of QTime is so coarse...
+    QVERIFY(thread.elapsed >= 100);
+#else
+    QVERIFY(thread.elapsed >= 120);
+#endif
+}
+
+typedef void (*FunctionPointer)(void *);
+void noop(void*) { }
+
+#ifdef Q_OS_SYMBIAN
+typedef RThread ThreadHandle;
+#elif defined Q_OS_UNIX
+    typedef pthread_t ThreadHandle;
+#elif defined Q_OS_WIN
+    typedef HANDLE ThreadHandle;
+#endif
+
+#ifdef Q_OS_WIN
+#define WIN_FIX_STDCALL __stdcall
+#else
+#define WIN_FIX_STDCALL
+#endif
+
+class NativeThreadWrapper
+{
+public:
+    NativeThreadWrapper() : qthread(0), waitForStop(false) {}
+    void start(FunctionPointer functionPointer = noop, void *data = 0);
+    void startAndWait(FunctionPointer functionPointer = noop, void *data = 0);
+    void join();
+    void setWaitForStop() { waitForStop = true; }
+    void stop();
+
+    ThreadHandle nativeThreadHandle;
+    QThread *qthread;
+    QWaitCondition startCondition;
+    QMutex mutex;
+    bool waitForStop;
+    QWaitCondition stopCondition;
+protected:
+    static void *runUnix(void *data);
+    static unsigned WIN_FIX_STDCALL runWin(void *data);
+    static int runSymbian(void *data);
+
+    FunctionPointer functionPointer;
+    void *data;
+};
+
+void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data)
+{
+    this->functionPointer = functionPointer;
+    this->data = data;
+#ifdef Q_OS_SYMBIAN
+    qt_symbian_throwIfError(nativeThreadHandle.Create(KNullDesC(), NativeThreadWrapper::runSymbian, 1024, &User::Allocator(), this));
+    nativeThreadHandle.Resume();
+#elif defined Q_OS_UNIX
+    const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this);
+    Q_UNUSED(state);
+#elif defined(Q_OS_WINCE)
+        nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL);
+#elif defined Q_OS_WIN
+    unsigned thrdid = 0;
+    nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid);
+#endif
+}
+
+void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *data)
+{
+    QMutexLocker locker(&mutex);
+    start(functionPointer, data);
+    startCondition.wait(locker.mutex());
+}
+
+void NativeThreadWrapper::join()
+{
+#ifdef Q_OS_SYMBIAN
+    TRequestStatus stat;
+    nativeThreadHandle.Logon(stat);
+    User::WaitForRequest(stat);
+    nativeThreadHandle.Close();
+#elif defined Q_OS_UNIX
+    pthread_join(nativeThreadHandle, 0);
+#elif defined Q_OS_WIN
+    WaitForSingleObject(nativeThreadHandle, INFINITE);
+    CloseHandle(nativeThreadHandle);
+#endif
+}
+
+void *NativeThreadWrapper::runUnix(void *that)
+{
+    NativeThreadWrapper *nativeThreadWrapper = reinterpret_cast<NativeThreadWrapper*>(that);
+
+    // Adopt thread, create QThread object.
+    nativeThreadWrapper->qthread = QThread::currentThread();
+
+    // Release main thread.
+    {
+        QMutexLocker lock(&nativeThreadWrapper->mutex);
+        nativeThreadWrapper->startCondition.wakeOne();
+    }
+
+    // Run function.
+    nativeThreadWrapper->functionPointer(nativeThreadWrapper->data);
+
+    // Wait for stop.
+    {
+        QMutexLocker lock(&nativeThreadWrapper->mutex);
+        if (nativeThreadWrapper->waitForStop)
+            nativeThreadWrapper->stopCondition.wait(lock.mutex());
+    }
+
+    return 0;
+}
+
+unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data)
+{
+    runUnix(data);
+    return 0;
+}
+
+int NativeThreadWrapper::runSymbian(void *data)
+{
+    runUnix(data);
+    return 0;
+}
+
+void NativeThreadWrapper::stop()
+{
+    QMutexLocker lock(&mutex);
+    waitForStop = false;
+    stopCondition.wakeOne();
+}
+
+bool threadAdoptedOk = false;
+QThread *mainThread;
+void testNativeThreadAdoption(void *)
+{
+    threadAdoptedOk = (QThread::currentThreadId() != 0
+                       && QThread::currentThread() != 0
+                       && QThread::currentThread() != mainThread);
+}
+void tst_QThread::nativeThreadAdoption()
+{
+    threadAdoptedOk = false;
+    mainThread = QThread::currentThread();
+    NativeThreadWrapper nativeThread;
+    nativeThread.setWaitForStop();
+    nativeThread.startAndWait(testNativeThreadAdoption);
+    QVERIFY(nativeThread.qthread);
+
+    nativeThread.stop();
+    nativeThread.join();
+
+    QVERIFY(threadAdoptedOk);
+}
+
+void adoptedThreadAffinityFunction(void *arg)
+{
+    QThread **affinity = reinterpret_cast<QThread **>(arg);
+    QThread *current = QThread::currentThread();
+    affinity[0] = current;
+    affinity[1] = current->thread();
+}
+
+void tst_QThread::adoptedThreadAffinity()
+{
+    QThread *affinity[2] = { 0, 0 };
+
+    NativeThreadWrapper thread;
+    thread.startAndWait(adoptedThreadAffinityFunction, affinity);
+    thread.join();
+
+    // adopted thread should have affinity to itself
+    QCOMPARE(affinity[0], affinity[1]);
+}
+
+void tst_QThread::adoptedThreadSetPriority()
+{
+
+    NativeThreadWrapper nativeThread;
+    nativeThread.setWaitForStop();
+    nativeThread.startAndWait();
+
+    // change the priority of a running thread
+    QCOMPARE(nativeThread.qthread->priority(), QThread::InheritPriority);
+    nativeThread.qthread->setPriority(QThread::IdlePriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::IdlePriority);
+    nativeThread.qthread->setPriority(QThread::LowestPriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::LowestPriority);
+    nativeThread.qthread->setPriority(QThread::LowPriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::LowPriority);
+    nativeThread.qthread->setPriority(QThread::NormalPriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::NormalPriority);
+    nativeThread.qthread->setPriority(QThread::HighPriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::HighPriority);
+    nativeThread.qthread->setPriority(QThread::HighestPriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::HighestPriority);
+    nativeThread.qthread->setPriority(QThread::TimeCriticalPriority);
+    QCOMPARE(nativeThread.qthread->priority(), QThread::TimeCriticalPriority);
+
+    nativeThread.stop();
+    nativeThread.join();
+}
+
+void tst_QThread::adoptedThreadExit()
+{
+    NativeThreadWrapper nativeThread;
+    nativeThread.setWaitForStop();
+
+    nativeThread.startAndWait();
+    QVERIFY(nativeThread.qthread);
+    QVERIFY(nativeThread.qthread->isRunning());
+    QVERIFY(!nativeThread.qthread->isFinished());
+
+    nativeThread.stop();
+    nativeThread.join();
+}
+
+void adoptedThreadExecFunction(void *)
+{
+    QThread  * const adoptedThread = QThread::currentThread();
+    QEventLoop eventLoop(adoptedThread);
+
+    const int code = 1;
+    Exit_Object o;
+    o.thread = adoptedThread;
+    o.code = code;
+    QTimer::singleShot(100, &o, SLOT(slot()));
+
+    const int result = eventLoop.exec();
+    QCOMPARE(result, code);
+}
+
+void tst_QThread::adoptedThreadExec()
+{
+    NativeThreadWrapper nativeThread;
+    nativeThread.start(adoptedThreadExecFunction);
+    nativeThread.join();
+}
+
+/*
+    Test that you get the finished signal when an adopted thread exits.
+*/
+void tst_QThread::adoptedThreadFinished()
+{
+    NativeThreadWrapper nativeThread;
+    nativeThread.setWaitForStop();
+    nativeThread.startAndWait();
+
+    QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+    nativeThread.stop();
+    nativeThread.join();
+
+    QTestEventLoop::instance().enterLoop(5);
+    QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QThread::adoptedThreadExecFinished()
+{
+    NativeThreadWrapper nativeThread;
+    nativeThread.setWaitForStop();
+    nativeThread.startAndWait(adoptedThreadExecFunction);
+
+    QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+    nativeThread.stop();
+    nativeThread.join();
+
+    QTestEventLoop::instance().enterLoop(5);
+    QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QThread::adoptMultipleThreads()
+{
+#if defined(Q_OS_WIN)
+    // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already.
+#  if defined(Q_OS_WINCE)
+    const int numThreads = 20;
+#  else
+    // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher()
+    const int numThreads = 200;
+#  endif
+#else
+    const int numThreads = 5;
+#endif
+    QVector<NativeThreadWrapper*> nativeThreads;
+
+    SignalRecorder recorder;
+
+    for (int i = 0; i < numThreads; ++i) {
+        nativeThreads.append(new NativeThreadWrapper());
+        nativeThreads.at(i)->setWaitForStop();
+        nativeThreads.at(i)->startAndWait();
+        QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot()));
+    }
+
+    QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+    for (int i = 0; i < numThreads; ++i) {
+        nativeThreads.at(i)->stop();
+        nativeThreads.at(i)->join();
+        delete nativeThreads.at(i);
+    }
+
+    QTestEventLoop::instance().enterLoop(5);
+    QVERIFY(!QTestEventLoop::instance().timeout());
+    QCOMPARE(int(recorder.activationCount), numThreads);
+}
+
+void tst_QThread::adoptMultipleThreadsOverlap()
+{
+#if defined(Q_OS_WIN)
+    // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already.
+#  if defined(Q_OS_WINCE)
+    const int numThreads = 20;
+#  else
+    // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher()
+    const int numThreads = 200;
+#  endif
+#elif defined(Q_OS_SYMBIAN)
+    // stress the monitoring thread's add function
+    const int numThreads = 100;
+#else
+    const int numThreads = 5;
+#endif
+    QVector<NativeThreadWrapper*> nativeThreads;
+
+    SignalRecorder recorder;
+
+    for (int i = 0; i < numThreads; ++i) {
+        nativeThreads.append(new NativeThreadWrapper());
+        nativeThreads.at(i)->setWaitForStop();
+        nativeThreads.at(i)->mutex.lock();
+        nativeThreads.at(i)->start();
+    }
+    for (int i = 0; i < numThreads; ++i) {
+        nativeThreads.at(i)->startCondition.wait(&nativeThreads.at(i)->mutex);
+        QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot()));
+        nativeThreads.at(i)->mutex.unlock();
+    }
+
+    QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+    for (int i = 0; i < numThreads; ++i) {
+        nativeThreads.at(i)->stop();
+        nativeThreads.at(i)->join();
+        delete nativeThreads.at(i);
+    }
+
+    QTestEventLoop::instance().enterLoop(5);
+    QVERIFY(!QTestEventLoop::instance().timeout());
+    QCOMPARE(int(recorder.activationCount), numThreads);
+}
+
+void adoptedThreadActiveTimerFunction(void *)
+{
+    QThread  * const adoptedThread = QThread::currentThread();
+    QEventLoop eventLoop(adoptedThread);
+
+    const int code = 1;
+    Exit_Object o;
+    o.thread = adoptedThread;
+    o.code = code;
+    QTimer::singleShot(100, &o, SLOT(slot()));
+
+    // create a timer that will still be active when the thread finishes
+    QTimer::singleShot(3000, &o, SLOT(slot()));
+
+    const int result = eventLoop.exec();
+    QCOMPARE(result, code);
+}
+
+void tst_QThread::adoptThreadExitWithActiveTimer()
+{
+    NativeThreadWrapper nativeThread;
+    nativeThread.start(adoptedThreadActiveTimerFunction);
+    nativeThread.join();
+}
+
+void tst_QThread::stressTest()
+{
+#if defined(Q_OS_WINCE)
+    QSKIP("Disconnects on WinCE, skipping...", SkipAll);
+#endif
+    QTime t;
+    t.start();
+    while (t.elapsed() < one_minute) {
+        Current_Thread t;
+        t.start();
+        t.wait(one_minute);
+    }
+}
+
+class Syncronizer : public QObject
+{ Q_OBJECT
+public slots:
+    void setProp(int p) {
+        if(m_prop != p) {
+            m_prop = p;
+            emit propChanged(p);
+        }
+    }
+signals:
+    void propChanged(int);
+public:
+    Syncronizer() : m_prop(42) {}
+    int m_prop;
+};
+
+void tst_QThread::QTBUG13810_exitAndStart()
+{
+    QThread thread;
+    thread.exit(555); //should do nothing
+
+    thread.start();
+
+    //test that the thread is running by executing queued connected signal there
+    Syncronizer sync1;
+    sync1.moveToThread(&thread);
+    Syncronizer sync2;
+    sync2.moveToThread(&thread);
+    connect(&sync2, SIGNAL(propChanged(int)), &sync1, SLOT(setProp(int)), Qt::QueuedConnection);
+    connect(&sync1, SIGNAL(propChanged(int)), &thread, SLOT(quit()), Qt::QueuedConnection);
+    QMetaObject::invokeMethod(&sync2, "setProp", Qt::QueuedConnection , Q_ARG(int, 89));
+    QTest::qWait(50);
+    while(!thread.wait(10))
+        QTest::qWait(10);
+    QCOMPARE(sync2.m_prop, 89);
+    QCOMPARE(sync1.m_prop, 89);
+}
+
+void tst_QThread::QTBUG15378_exitAndExec()
+{
+    class Thread : public QThread {
+    public:
+        QSemaphore sem1;
+        QSemaphore sem2;
+        volatile int value;
+        void run() {
+            sem1.acquire();
+            value = exec();  //First entrence
+            sem2.release();
+            value = exec(); // Second loop
+        }
+    };
+    Thread thread;
+    thread.value = 0;
+    thread.start();
+    thread.exit(556);
+    thread.sem1.release(); //should exit the first loop
+    thread.sem2.acquire();
+    int v = thread.value;
+    QCOMPARE(v, 556);
+    
+    //test that the thread is running by executing queued connected signal there
+    Syncronizer sync1;
+    sync1.moveToThread(&thread);
+    Syncronizer sync2;
+    sync2.moveToThread(&thread);
+    connect(&sync2, SIGNAL(propChanged(int)), &sync1, SLOT(setProp(int)), Qt::QueuedConnection);
+    connect(&sync1, SIGNAL(propChanged(int)), &thread, SLOT(quit()), Qt::QueuedConnection);
+    QMetaObject::invokeMethod(&sync2, "setProp", Qt::QueuedConnection , Q_ARG(int, 89));
+    QTest::qWait(50);
+    while(!thread.wait(10))
+        QTest::qWait(10);
+    QCOMPARE(sync2.m_prop, 89);
+    QCOMPARE(sync1.m_prop, 89);
+}
+
+void tst_QThread::connectThreadFinishedSignalToObjectDeleteLaterSlot()
+{
+    QThread thread;
+    QObject *object = new QObject;
+    QWeakPointer<QObject> p = object;
+    QVERIFY(!p.isNull());
+    connect(&thread, SIGNAL(started()), &thread, SLOT(quit()), Qt::DirectConnection);
+    connect(&thread, SIGNAL(finished()), object, SLOT(deleteLater()));
+    object->moveToThread(&thread);
+    thread.start();
+    QVERIFY(thread.wait(30000));
+    QVERIFY(p.isNull());
+}
+
+class Waiting_Thread : public QThread
+{
+public:
+    enum { WaitTime = 800 };
+    QMutex mutex;
+    QWaitCondition cond1;
+    QWaitCondition cond2;
+
+    void run()
+    {
+        QMutexLocker locker(&mutex);
+        cond1.wait(&mutex);
+        cond2.wait(&mutex, WaitTime);
+    }
+};
+
+void tst_QThread::wait2()
+{
+    QElapsedTimer timer;
+    Waiting_Thread thread;
+    thread.start();
+    timer.start();
+    QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
+    qint64 elapsed = timer.elapsed();
+
+    QVERIFY(elapsed >= Waiting_Thread::WaitTime);
+    //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4);
+
+    timer.start();
+    thread.cond1.wakeOne();
+    QVERIFY(thread.wait(/*Waiting_Thread::WaitTime * 1.4*/));
+    elapsed = timer.elapsed();
+    QVERIFY(elapsed >= Waiting_Thread::WaitTime);
+    //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4);
+}
+
+
+class SlowSlotObject : public QObject {
+    Q_OBJECT
+public:
+    QMutex mutex;
+    QWaitCondition cond;
+public slots:
+    void slowSlot() {
+        QMutexLocker locker(&mutex);
+        cond.wait(&mutex);
+    }
+};
+
+void tst_QThread::wait3_slowDestructor()
+{
+    SlowSlotObject slow;
+    QThread thread;
+    QObject::connect(&thread, SIGNAL(finished()), &slow, SLOT(slowSlot()), Qt::DirectConnection);
+
+    enum { WaitTime = 1800 };
+    QElapsedTimer timer;
+
+    thread.start();
+    thread.quit();
+    //the quit function will cause the thread to finish and enter the slowSlot that is blocking
+
+    timer.start();
+    QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
+    qint64 elapsed = timer.elapsed();
+
+    QVERIFY(elapsed >= Waiting_Thread::WaitTime);
+    //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4);
+
+    slow.cond.wakeOne();
+    //now the thread should finish quickly
+    QVERIFY(thread.wait(one_minute));
+}
+
+void tst_QThread::destroyFinishRace()
+{
+    class Thread : public QThread { void run() {} };
+    for (int i = 0; i < 15; i++) {
+        Thread *thr = new Thread;
+        connect(thr, SIGNAL(finished()), thr, SLOT(deleteLater()));
+        QWeakPointer<QThread> weak(static_cast<QThread*>(thr));
+        thr->start();
+        while (weak) {
+            qApp->processEvents();
+            qApp->processEvents();
+            qApp->processEvents();
+            qApp->processEvents();
+        }
+    }
+}
+
+void tst_QThread::startFinishRace()
+{
+    class Thread : public QThread {
+    public:
+        Thread() : i (50) {}
+        void run() {
+            i--;
+            if (!i) disconnect(this, SIGNAL(finished()), 0, 0);
+        }
+        int i;
+    };
+    for (int i = 0; i < 15; i++) {
+        Thread thr;
+        connect(&thr, SIGNAL(finished()), &thr, SLOT(start()));
+        thr.start();
+        while (!thr.isFinished() || thr.i != 0) {
+            qApp->processEvents();
+            qApp->processEvents();
+            qApp->processEvents();
+            qApp->processEvents();
+        }
+        QCOMPARE(thr.i, 0);
+    }
+}
+
+void tst_QThread::startAndQuitCustomEventLoop()
+{
+    struct Thread : QThread {
+        void run() { QEventLoop().exec(); }
+    };
+
+   for (int i = 0; i < 5; i++) {
+       Thread t;
+       t.start();
+       t.quit();
+       t.wait();
+   }
+}
+
+#ifdef Q_OS_SYMBIAN
+#include <QRegExp>
+
+namespace testNamespace {
+class TestThread : public QThread
+{
+    Q_OBJECT
+public:
+    TestThread();
+    void run();
+public:
+    bool runCalled;
+    QString threadName;
+};
+
+TestThread::TestThread() : runCalled(false)
+{
+}
+
+void TestThread::run()
+{
+    runCalled = true;
+    RThread t;
+    TName name = t.Name();
+    threadName = QString((QChar*)name.Ptr(), name.Length());
+}
+}
+
+void tst_QThread::threadNameTest()
+{
+// On Symbian thread name consist of objectName, className and variable part.
+// RThread::Create sets limitations on what are allowed characters in thread name.
+// Allowed characters include chars in 0x20 - 0x7e range but not '*','?',':'
+// If thread thread name contains not allowed characters RThread::Create fails.
+// In addition, max thread name length is 80 chars on Symbian.
+
+    // Reqular expression used in QThread::start for removing not allowed characters
+    const QRegExp notAllowedChars(QLatin1String("[^\\x20-\\x7e]|\\*|\\?|\\:"));
+
+    // objectName contains all allowed characters
+    {
+        testNamespace::TestThread thread;
+        QString name;
+        for (int i = 0x20; i < 0x7f; i++) {
+            if (i != '*' && i != '?' && i != ':') {
+                name.append(QLatin1Char(i));
+            }
+        }
+
+        thread.setObjectName(name);
+        thread.start();
+        thread.wait();
+        QCOMPARE(thread.runCalled, true);
+
+        QString expectedResult = name;
+        QString result = name.replace(notAllowedChars, QLatin1String("_"));
+        QCOMPARE(result, expectedResult);
+        // objectName part can be max 72 chars in thread name
+        QCOMPARE(thread.threadName.left(72), expectedResult.left(72));
+    }
+
+    // objectName contains all characters from range including characters deemed
+    // not valid by RThread::Create (*?:)
+    {
+        testNamespace::TestThread thread;
+        QString name;
+        for (int i = 0x20; i < 0x7f; i++) {
+            name.append(QLatin1Char(i));
+        }
+
+        thread.setObjectName(name);
+        thread.start();
+        thread.wait();
+        QCOMPARE(thread.runCalled, true);
+
+        QString expectedResult = name;
+        expectedResult = expectedResult.replace(QLatin1Char('*'), QLatin1Char('_'));
+        expectedResult = expectedResult.replace(QLatin1Char('?'), QLatin1Char('_'));
+        expectedResult = expectedResult.replace(QLatin1Char(':'), QLatin1Char('_'));
+        QString result = name.replace(notAllowedChars, QLatin1String("_"));
+        QCOMPARE(result, expectedResult);
+
+        // objectName part can be max 72 chars in thread name
+        QCOMPARE(thread.threadName.left(72), expectedResult.left(72));
+    }
+
+    // objectName contains only invalid characters
+    {
+        testNamespace::TestThread thread;
+        QString name;
+        for (int i = 0; i < 0x20; i++) {
+            name.append(QLatin1Char(i));
+        }
+        for (int i = 0x7f; i < 0xff; i++) {
+            name.append(QLatin1Char(i));
+        }
+
+        thread.setObjectName(name);
+        thread.start();
+        thread.wait();
+        QCOMPARE(thread.runCalled, true);
+
+        QString expectedResult;
+        expectedResult.fill(QLatin1Char('_'), name.size());
+        QString result = name.replace(notAllowedChars, QLatin1String("_"));
+        QCOMPARE(result, expectedResult);
+
+        // objectName part can be max 72 chars in thread name
+        QCOMPARE(thread.threadName.left(72), expectedResult.left(72));
+    }
+
+    // objectName longer than max thread name length (80 chars)
+    {
+        testNamespace::TestThread thread;
+        QString name;
+        for (int i = 0; i < 0xff; i++) {
+            name.append(QLatin1Char(i));
+        }
+
+        thread.setObjectName(name);
+        thread.start();
+        thread.wait();
+        QCOMPARE(thread.runCalled, true);
+    }
+
+    // className contains not allowed characters (':')
+    {
+        testNamespace::TestThread thread;
+        thread.start();
+        thread.wait();
+        QCOMPARE(thread.runCalled, true);
+        QString className(QLatin1String(thread.metaObject()->className()));
+        QCOMPARE(className, QLatin1String("testNamespace::TestThread"));
+
+        QString expectedResult = className;
+        expectedResult = className.replace(QLatin1Char(':'), QLatin1Char('_'));
+        QString result = className.replace(notAllowedChars, QLatin1String("_"));
+        QCOMPARE(result, expectedResult);
+
+        QVERIFY(thread.threadName.contains(expectedResult));
+    }
+}
+#endif // Q_OS_SYMBIAN
+
+class FinishedTestObject : public QObject {
+    Q_OBJECT
+public:
+    FinishedTestObject() : ok(false) {}
+    bool ok;
+public slots:
+    void slotFinished() {
+        QThread *t = qobject_cast<QThread *>(sender());
+        ok = t && t->isFinished() && !t->isRunning();
+    }
+};
+
+void tst_QThread::isRunningInFinished()
+{
+    for (int i = 0; i < 15; i++) {
+        QThread thread;
+        thread.start();
+        FinishedTestObject localObject;
+        FinishedTestObject inThreadObject;
+        localObject.setObjectName("...");
+        inThreadObject.moveToThread(&thread);
+        connect(&thread, SIGNAL(finished()), &localObject, SLOT(slotFinished()));
+        connect(&thread, SIGNAL(finished()), &inThreadObject, SLOT(slotFinished()));
+        QEventLoop loop;
+        connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
+        QMetaObject::invokeMethod(&thread, "quit", Qt::QueuedConnection);
+        loop.exec();
+        QVERIFY(!thread.isRunning());
+        QVERIFY(thread.isFinished());
+        QVERIFY(localObject.ok);
+        QVERIFY(inThreadObject.ok);
+    }
+}
+
+QTEST_MAIN(tst_QThread)
+#include "moc_tst_qthread.cpp"
diff --git a/tests/auto/qwaitcondition/CMakeLists.txt b/tests/auto/qwaitcondition/CMakeLists.txt
new file mode 100644 (file)
index 0000000..442efc1
--- /dev/null
@@ -0,0 +1,3 @@
+katie_test(tst_qwaitcondition
+    ${CMAKE_CURRENT_SOURCE_DIR}/tst_qwaitcondition.cpp
+)
diff --git a/tests/auto/qwaitcondition/tst_qwaitcondition.cpp b/tests/auto/qwaitcondition/tst_qwaitcondition.cpp
new file mode 100644 (file)
index 0000000..81ddf87
--- /dev/null
@@ -0,0 +1,839 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016-2019 Ivailo Monev
+**
+** This file is part of the test suite of the Katie Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include <qreadwritelock.h>
+
+#if defined(Q_OS_SYMBIAN)
+// Symbian Open C has a bug that causes very short waits to fail sometimes
+#define COND_WAIT_TIME 50
+#else
+#define COND_WAIT_TIME 1
+#endif
+
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QWaitCondition : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_QWaitCondition();
+
+private slots:
+    void wait_QMutex();
+    void wait_QReadWriteLock();
+    void wakeOne();
+    void wakeAll();
+    void wait_RaceCondition();
+};
+
+static const int iterations = 10;
+
+// Note: some tests rely on ThreadCount being multiple of 2
+#if defined(Q_OS_SOLARIS) || ( defined(Q_OS_LINUX) && defined(QT_ARCH_ARMV6) )
+static const int ThreadCount = 4;
+#else
+static const int ThreadCount = 10;
+#endif
+
+tst_QWaitCondition::tst_QWaitCondition()
+
+{
+}
+
+class wait_QMutex_Thread_1 : public QThread
+{
+public:
+    QMutex mutex;
+    QWaitCondition cond;
+
+    inline wait_QMutex_Thread_1()
+    { }
+
+    void run()
+    {
+    mutex.lock();
+    cond.wakeOne();
+    cond.wait(&mutex);
+    mutex.unlock();
+    }
+};
+
+class wait_QMutex_Thread_2 : public QThread
+{
+public:
+    QWaitCondition started;
+
+    QMutex *mutex;
+    QWaitCondition *cond;
+
+    inline wait_QMutex_Thread_2()
+    : mutex(0), cond(0)
+    { }
+
+    void run()
+    {
+    mutex->lock();
+    started.wakeOne();
+    cond->wait(mutex);
+    mutex->unlock();
+    }
+};
+
+class wait_QReadWriteLock_Thread_1 : public QThread
+{
+public:
+    QReadWriteLock readWriteLock;
+    QWaitCondition cond;
+
+    inline wait_QReadWriteLock_Thread_1()
+    { }
+
+    void run()
+    {
+    readWriteLock.lockForWrite();
+    cond.wakeOne();
+    cond.wait(&readWriteLock);
+    readWriteLock.unlock();
+    }
+};
+
+class wait_QReadWriteLock_Thread_2 : public QThread
+{
+public:
+    QWaitCondition started;
+
+    QReadWriteLock *readWriteLock;
+    QWaitCondition *cond;
+
+    inline wait_QReadWriteLock_Thread_2()
+    : readWriteLock(0), cond(0)
+    { }
+
+    void run()
+    {
+    readWriteLock->lockForRead();
+    started.wakeOne();
+    cond->wait(readWriteLock);
+    readWriteLock->unlock();
+    }
+};
+
+void tst_QWaitCondition::wait_QMutex()
+{
+    int x;
+    for (int i = 0; i < iterations; ++i) {
+    {
+        QMutex mutex;
+        QWaitCondition cond;
+
+        mutex.lock();
+
+        cond.wakeOne();
+        QVERIFY(!cond.wait(&mutex, 1));
+
+        cond.wakeAll();
+        QVERIFY(!cond.wait(&mutex, 1));
+
+        mutex.unlock();
+    }
+
+    {
+        // test multiple threads waiting on separate wait conditions
+        wait_QMutex_Thread_1 thread[ThreadCount];
+
+        for (x = 0; x < ThreadCount; ++x) {
+        thread[x].mutex.lock();
+        thread[x].start();
+        // wait for thread to start
+        QVERIFY(thread[x].cond.wait(&thread[x].mutex, 1000));
+        thread[x].mutex.unlock();
+        }
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].isRunning());
+        QVERIFY(!thread[x].isFinished());
+        }
+
+        for (x = 0; x < ThreadCount; ++x) {
+        thread[x].mutex.lock();
+        thread[x].cond.wakeOne();
+        thread[x].mutex.unlock();
+        }
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].wait(1000));
+        }
+    }
+
+    {
+        // test multiple threads waiting on a wait condition
+        QMutex mutex;
+        QWaitCondition cond1, cond2;
+        wait_QMutex_Thread_2 thread[ThreadCount];
+
+        mutex.lock();
+        for (x = 0; x < ThreadCount; ++x) {
+        thread[x].mutex = &mutex;
+        thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
+        thread[x].start();
+        // wait for thread to start
+        QVERIFY(thread[x].started.wait(&mutex, 1000));
+        }
+        mutex.unlock();
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].isRunning());
+        QVERIFY(!thread[x].isFinished());
+        }
+
+        mutex.lock();
+        cond1.wakeAll();
+        cond2.wakeAll();
+        mutex.unlock();
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].wait(1000));
+        }
+    }
+    }
+}
+
+void tst_QWaitCondition::wait_QReadWriteLock()
+{
+    {
+        QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
+        QWaitCondition waitCondition;
+
+        // ensure that the lockForRead is correctly restored
+        readWriteLock.lockForRead();
+
+        QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+        QVERIFY(!readWriteLock.tryLockForWrite());
+        QVERIFY(readWriteLock.tryLockForRead());
+        readWriteLock.unlock();
+        QVERIFY(!readWriteLock.tryLockForWrite());
+        readWriteLock.unlock();
+
+        QVERIFY(readWriteLock.tryLockForWrite());
+        readWriteLock.unlock();
+    }
+
+    {
+        QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
+        QWaitCondition waitCondition;
+
+        // ensure that the lockForWrite is correctly restored
+        readWriteLock.lockForWrite();
+
+        QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+        QVERIFY(!readWriteLock.tryLockForRead());
+        QVERIFY(readWriteLock.tryLockForWrite());
+        readWriteLock.unlock();
+        QVERIFY(!readWriteLock.tryLockForRead());
+        readWriteLock.unlock();
+
+        QVERIFY(readWriteLock.tryLockForRead());
+        readWriteLock.unlock();
+    }
+
+
+    int x;
+    for (int i = 0; i < iterations; ++i) {
+    {
+        QReadWriteLock readWriteLock;
+        QWaitCondition waitCondition;
+
+        readWriteLock.lockForRead();
+
+        waitCondition.wakeOne();
+        QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+        waitCondition.wakeAll();
+        QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+        readWriteLock.unlock();
+    }
+
+    {
+        QReadWriteLock readWriteLock;
+        QWaitCondition waitCondition;
+
+        readWriteLock.lockForWrite();
+
+        waitCondition.wakeOne();
+        QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+        waitCondition.wakeAll();
+        QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+        readWriteLock.unlock();
+    }
+
+    {
+        // test multiple threads waiting on separate wait conditions
+        wait_QReadWriteLock_Thread_1 thread[ThreadCount];
+
+        for (x = 0; x < ThreadCount; ++x) {
+        thread[x].readWriteLock.lockForRead();
+        thread[x].start();
+        // wait for thread to start
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_WINSCW)
+        // Symbian emulator startup simultaneously with this thread causes additional delay
+        QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 10000));
+#else
+        QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 1000));
+#endif
+        thread[x].readWriteLock.unlock();
+        }
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].isRunning());
+        QVERIFY(!thread[x].isFinished());
+        }
+
+        for (x = 0; x < ThreadCount; ++x) {
+        thread[x].readWriteLock.lockForRead();
+        thread[x].cond.wakeOne();
+        thread[x].readWriteLock.unlock();
+        }
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].wait(1000));
+        }
+    }
+
+    {
+        // test multiple threads waiting on a wait condition
+        QReadWriteLock readWriteLock;
+        QWaitCondition cond1, cond2;
+        wait_QReadWriteLock_Thread_2 thread[ThreadCount];
+
+        readWriteLock.lockForWrite();
+        for (x = 0; x < ThreadCount; ++x) {
+        thread[x].readWriteLock = &readWriteLock;
+        thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
+        thread[x].start();
+        // wait for thread to start
+        QVERIFY(thread[x].started.wait(&readWriteLock, 1000));
+        }
+        readWriteLock.unlock();
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].isRunning());
+        QVERIFY(!thread[x].isFinished());
+        }
+
+        readWriteLock.lockForWrite();
+        cond1.wakeAll();
+        cond2.wakeAll();
+        readWriteLock.unlock();
+
+        for (x = 0; x < ThreadCount; ++x) {
+        QVERIFY(thread[x].wait(1000));
+        }
+    }
+    }
+
+}
+
+class wake_Thread : public QThread
+{
+public:
+    static int count;
+
+    QWaitCondition started;
+    QWaitCondition dummy;
+
+    QMutex *mutex;
+    QWaitCondition *cond;
+
+    inline wake_Thread()
+    : mutex(0), cond(0)
+    { }
+
+    static inline void sleep(ulong s)
+    { QThread::sleep(s); }
+
+    void run()
+    {
+    mutex->lock();
+    ++count;
+        dummy.wakeOne(); // this wakeup should be lost
+           started.wakeOne();
+        dummy.wakeAll(); // this one too
+    cond->wait(mutex);
+        --count;
+    mutex->unlock();
+    }
+};
+
+int wake_Thread::count = 0;
+
+class wake_Thread_2 : public QThread
+{
+public:
+    static int count;
+
+    QWaitCondition started;
+    QWaitCondition dummy;
+
+    QReadWriteLock *readWriteLock;
+    QWaitCondition *cond;
+
+    inline wake_Thread_2()
+    : readWriteLock(0), cond(0)
+    { }
+
+    static inline void sleep(ulong s)
+    { QThread::sleep(s); }
+
+    void run()
+    {
+    readWriteLock->lockForWrite();
+    ++count;
+        dummy.wakeOne(); // this wakeup should be lost
+        started.wakeOne();
+        dummy.wakeAll(); // this one too
+    cond->wait(readWriteLock);
+        --count;
+    readWriteLock->unlock();
+    }
+};
+
+int wake_Thread_2::count = 0;
+
+void tst_QWaitCondition::wakeOne()
+{
+    int x;
+    // wake up threads, one at a time
+    for (int i = 0; i < iterations; ++i) {
+    QMutex mutex;
+    QWaitCondition cond;
+
+    // QMutex
+    wake_Thread thread[ThreadCount];
+    bool thread_exited[ThreadCount];
+
+    mutex.lock();
+    for (x = 0; x < ThreadCount; ++x) {
+        thread[x].mutex = &mutex;
+        thread[x].cond = &cond;
+        thread_exited[x] = false;
+        thread[x].start();
+        // wait for thread to start
+        QVERIFY(thread[x].started.wait(&mutex, 1000));
+        // make sure wakeups are not queued... if nothing is
+        // waiting at the time of the wakeup, nothing happens
+        QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+    }
+    mutex.unlock();
+
+    QCOMPARE(wake_Thread::count, ThreadCount);
+
+    // wake up threads one at a time
+    for (x = 0; x < ThreadCount; ++x) {
+        mutex.lock();
+        cond.wakeOne();
+        QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
+        QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+        mutex.unlock();
+
+        int exited = 0;
+        for (int y = 0; y < ThreadCount; ++y) {
+            if (thread_exited[y])
+                        continue;
+            if (thread[y].wait(exited > 0 ? 3 : 1000)) {
+                thread_exited[y] = true;
+                ++exited;
+            }
+        }
+
+        QCOMPARE(exited, 1);
+        QCOMPARE(wake_Thread::count, ThreadCount - (x + 1));
+    }
+
+    QCOMPARE(wake_Thread::count, 0);
+
+    // QReadWriteLock
+    QReadWriteLock readWriteLock;
+    wake_Thread_2 rwthread[ThreadCount];
+
+    readWriteLock.lockForWrite();
+    for (x = 0; x < ThreadCount; ++x) {
+        rwthread[x].readWriteLock = &readWriteLock;
+        rwthread[x].cond = &cond;
+        thread_exited[x] = false;
+        rwthread[x].start();
+        // wait for thread to start
+        QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
+        // make sure wakeups are not queued... if nothing is
+        // waiting at the time of the wakeup, nothing happens
+        QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+    }
+    readWriteLock.unlock();
+
+    QCOMPARE(wake_Thread_2::count, ThreadCount);
+
+    // wake up threads one at a time
+    for (x = 0; x < ThreadCount; ++x) {
+        readWriteLock.lockForWrite();
+        cond.wakeOne();
+        QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
+        QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+        readWriteLock.unlock();
+
+        int exited = 0;
+        for (int y = 0; y < ThreadCount; ++y) {
+            if (thread_exited[y])
+                        continue;
+            if (rwthread[y].wait(exited > 0 ? 3 : 1000)) {
+                thread_exited[y] = true;
+                ++exited;
+            }
+        }
+
+        QCOMPARE(exited, 1);
+        QCOMPARE(wake_Thread_2::count, ThreadCount - (x + 1));
+    }
+
+    QCOMPARE(wake_Thread_2::count, 0);
+    }
+
+    // wake up threads, two at a time
+    for (int i = 0; i < iterations; ++i) {
+    QMutex mutex;
+    QWaitCondition cond;
+
+        // QMutex
+    wake_Thread thread[ThreadCount];
+    bool thread_exited[ThreadCount];
+
+    mutex.lock();
+    for (x = 0; x < ThreadCount; ++x) {
+        thread[x].mutex = &mutex;
+        thread[x].cond = &cond;
+        thread_exited[x] = false;
+        thread[x].start();
+        // wait for thread to start
+        QVERIFY(thread[x].started.wait(&mutex, 1000));
+        // make sure wakeups are not queued... if nothing is
+        // waiting at the time of the wakeup, nothing happens
+        QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+    }
+    mutex.unlock();
+
+    QCOMPARE(wake_Thread::count, ThreadCount);
+
+    // wake up threads one at a time
+    for (x = 0; x < ThreadCount; x += 2) {
+        mutex.lock();
+        cond.wakeOne();
+        cond.wakeOne();
+        QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
+        QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+        QVERIFY(!thread[x + 1].dummy.wait(&mutex, 1));
+        mutex.unlock();
+
+        int exited = 0;
+        for (int y = 0; y < ThreadCount; ++y) {
+            if (thread_exited[y])
+                        continue;
+            if (thread[y].wait(exited > 0 ? 3 : 1000)) {
+                thread_exited[y] = true;
+                ++exited;
+            }
+        }
+
+        QCOMPARE(exited, 2);
+        QCOMPARE(wake_Thread::count, ThreadCount - (x + 2));
+    }
+
+    QCOMPARE(wake_Thread::count, 0);
+
+        // QReadWriteLock
+        QReadWriteLock readWriteLock;
+        wake_Thread_2 rwthread[ThreadCount];
+
+    readWriteLock.lockForWrite();
+    for (x = 0; x < ThreadCount; ++x) {
+        rwthread[x].readWriteLock = &readWriteLock;
+        rwthread[x].cond = &cond;
+        thread_exited[x] = false;
+        rwthread[x].start();
+        // wait for thread to start
+        QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
+        // make sure wakeups are not queued... if nothing is
+        // waiting at the time of the wakeup, nothing happens
+        QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+    }
+    readWriteLock.unlock();
+
+    QCOMPARE(wake_Thread_2::count, ThreadCount);
+
+    // wake up threads one at a time
+    for (x = 0; x < ThreadCount; x += 2) {
+        readWriteLock.lockForWrite();
+        cond.wakeOne();
+        cond.wakeOne();
+        QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
+        QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+        QVERIFY(!rwthread[x + 1].dummy.wait(&readWriteLock, 1));
+        readWriteLock.unlock();
+
+        int exited = 0;
+        for (int y = 0; y < ThreadCount; ++y) {
+            if (thread_exited[y])
+                        continue;
+            if (rwthread[y].wait(exited > 0 ? 3 : 1000)) {
+                thread_exited[y] = true;
+                ++exited;
+            }
+        }
+
+        QCOMPARE(exited, 2);
+        QCOMPARE(wake_Thread_2::count, ThreadCount - (x + 2));
+    }
+
+    QCOMPARE(wake_Thread_2::count, 0);
+}
+}
+
+void tst_QWaitCondition::wakeAll()
+{
+    int x;
+    for (int i = 0; i < iterations; ++i) {
+    QMutex mutex;
+    QWaitCondition cond;
+
+    // QMutex
+    wake_Thread thread[ThreadCount];
+
+    mutex.lock();
+    for (x = 0; x < ThreadCount; ++x) {
+        thread[x].mutex = &mutex;
+        thread[x].cond = &cond;
+        thread[x].start();
+        // wait for thread to start
+        QVERIFY(thread[x].started.wait(&mutex, 1000));
+    }
+    mutex.unlock();
+
+    QCOMPARE(wake_Thread::count, ThreadCount);
+
+    // wake up all threads at once
+    mutex.lock();
+    cond.wakeAll();
+    QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
+    mutex.unlock();
+
+    int exited = 0;
+    for (x = 0; x < ThreadCount; ++x) {
+        if (thread[x].wait(1000))
+        ++exited;
+    }
+
+    QCOMPARE(exited, ThreadCount);
+    QCOMPARE(wake_Thread::count, 0);
+
+    // QReadWriteLock
+    QReadWriteLock readWriteLock;
+    wake_Thread_2 rwthread[ThreadCount];
+
+    readWriteLock.lockForWrite();
+    for (x = 0; x < ThreadCount; ++x) {
+        rwthread[x].readWriteLock = &readWriteLock;
+        rwthread[x].cond = &cond;
+        rwthread[x].start();
+        // wait for thread to start
+        QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
+    }
+    readWriteLock.unlock();
+
+    QCOMPARE(wake_Thread_2::count, ThreadCount);
+
+    // wake up all threads at once
+    readWriteLock.lockForWrite();
+    cond.wakeAll();
+    QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
+    readWriteLock.unlock();
+
+    exited = 0;
+    for (x = 0; x < ThreadCount; ++x) {
+        if (rwthread[x].wait(1000))
+        ++exited;
+    }
+
+    QCOMPARE(exited, ThreadCount);
+    QCOMPARE(wake_Thread_2::count, 0);
+    }
+}
+
+class wait_RaceConditionThread : public QThread
+{
+public:
+    wait_RaceConditionThread(QMutex *mutex, QWaitCondition *startup, QWaitCondition *waitCondition,
+                             ulong timeout = ULONG_MAX)
+        : timeout(timeout), returnValue(false), ready(false),
+          mutex(mutex), startup(startup), waitCondition(waitCondition) {}
+
+    unsigned long timeout;
+    bool returnValue;
+
+    bool ready;
+
+    QMutex *mutex;
+    QWaitCondition *startup;
+    QWaitCondition *waitCondition;
+
+    void run() {
+        mutex->lock();
+
+        ready = true;
+        startup->wakeOne();
+
+        returnValue = waitCondition->wait(mutex, timeout);
+
+        mutex->unlock();
+    }
+};
+
+class wait_RaceConditionThread_2 : public QThread
+{
+public:
+    wait_RaceConditionThread_2(QReadWriteLock *readWriteLock,
+                               QWaitCondition *startup,
+                               QWaitCondition *waitCondition,
+                               ulong timeout = ULONG_MAX)
+        : timeout(timeout), returnValue(false), ready(false),
+          readWriteLock(readWriteLock), startup(startup), waitCondition(waitCondition)
+    { }
+
+    unsigned long timeout;
+    bool returnValue;
+
+    bool ready;
+
+    QReadWriteLock *readWriteLock;
+    QWaitCondition *startup;
+    QWaitCondition *waitCondition;
+
+    void run() {
+        readWriteLock->lockForWrite();
+
+        ready = true;
+        startup->wakeOne();
+
+        returnValue = waitCondition->wait(readWriteLock, timeout);
+
+        readWriteLock->unlock();
+    }
+};
+
+void tst_QWaitCondition::wait_RaceCondition()
+{
+    {
+        QMutex mutex;
+        QWaitCondition startup;
+        QWaitCondition waitCondition;
+
+        wait_RaceConditionThread timeoutThread(&mutex, &startup, &waitCondition, 1000),
+            waitingThread1(&mutex, &startup, &waitCondition);
+
+        timeoutThread.start();
+        waitingThread1.start();
+        mutex.lock();
+
+        // wait for the threads to start up
+        while (!timeoutThread.ready
+               || !waitingThread1.ready) {
+            startup.wait(&mutex);
+        }
+
+        QTest::qWait(2000);
+
+        waitCondition.wakeOne();
+
+        mutex.unlock();
+
+        QVERIFY(timeoutThread.wait(5000));
+        QVERIFY(!timeoutThread.returnValue);
+        QVERIFY(waitingThread1.wait(5000));
+        QVERIFY(waitingThread1.returnValue);
+    }
+
+    {
+        QReadWriteLock readWriteLock;
+        QWaitCondition startup;
+        QWaitCondition waitCondition;
+
+        wait_RaceConditionThread_2 timeoutThread(&readWriteLock, &startup, &waitCondition, 1000),
+            waitingThread1(&readWriteLock, &startup, &waitCondition);
+
+        timeoutThread.start();
+        waitingThread1.start();
+        readWriteLock.lockForRead();
+
+        // wait for the threads to start up
+        while (!timeoutThread.ready
+               || !waitingThread1.ready) {
+            startup.wait(&readWriteLock);
+        }
+
+        QTest::qWait(2000);
+
+        waitCondition.wakeOne();
+
+        readWriteLock.unlock();
+
+        QVERIFY(timeoutThread.wait(5000));
+        QVERIFY(!timeoutThread.returnValue);
+        QVERIFY(waitingThread1.wait(5000));
+        QVERIFY(waitingThread1.returnValue);
+    }
+}
+
+QTEST_MAIN(tst_QWaitCondition)
+
+#include "moc_tst_qwaitcondition.cpp"