tests/auto/qsystemreadwritelock_oop/tst_qsystemreadwritelock_oop.cpp
changeset 0 876b1a06bc25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qsystemreadwritelock_oop/tst_qsystemreadwritelock_oop.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,491 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QDir>
+#include <QProcess>
+#include <QIODevice>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include "qsystemreadwritelock_p.h"
+#include "common.h"
+
+QTM_USE_NAMESPACE
+class tst_QSystemReadWriteLock_oop : public QObject{
+    Q_OBJECT
+private slots:
+    void initTestCase();
+    void readLockBlockRelease();
+    void writeLockBlockRelease();
+    void multipleReadersBlockRelease();
+    void multipleReadersLoop();
+    void multipleWritersLoop();
+    void exclusiveWriteTest();
+    void writerPrecedence();
+
+private:
+    bool waitForLine(QIODevice *device, const QString &expectedLine,
+                    bool print = false, int timeout=5000);
+
+};
+
+void tst_QSystemReadWriteLock_oop::initTestCase()
+{
+    // if an old writeLockExcl.tmp exists at the beginning of this test case, try to remove it,
+    // otherwise test functions may fail
+    QDir cwd;
+    if (cwd.exists("writeLockExcl.tmp")) {
+        QVERIFY(cwd.remove("writeLockExcl.tmp"));
+    }
+}
+
+/*
+    A writer aquires a write-lock, a reader tries to lock
+    the writer releases the lock, the reader gets the lock
+*/
+void tst_QSystemReadWriteLock_oop::readLockBlockRelease()
+{
+    bool print = false;
+    QSystemReadWriteLock testLock("Viper", QSystemReadWriteLock::Create);
+    if (print)
+        qDebug() << "Main process: About to lock for writing";
+    testLock.lockForWrite();
+    if (print)
+        qDebug() << "Main process: After lock for writing";
+
+    QLocalServer server;
+    QString connectionName = "readLockBlockRelease";
+    QVERIFY(QLocalServer::removeServer(connectionName));
+    QVERIFY(server.listen(connectionName));
+
+    QProcess reader;
+    reader.setReadChannel(QProcess::StandardError);
+    QStringList args;
+    args << connectionName;
+    args << "ReadLock";
+
+    reader.start("./lackey", args);
+    QVERIFY(reader.waitForStarted());
+
+    QVERIFY(server.waitForNewConnection(5000));
+    QLocalSocket *oopSocket = server.nextPendingConnection();
+
+    QVERIFY(waitForLine(oopSocket, Lackey::BeforeLockForRead,print));
+    QVERIFY(!waitForLine(oopSocket, Lackey::AfterLockForRead,false,1000));
+
+    testLock.unlock();
+    if (print)
+        qDebug() << "Main process: After unlock(write)";
+
+    QVERIFY(waitForLine(oopSocket, Lackey::AfterLockForRead, print));
+    QVERIFY(waitForLine(oopSocket, Lackey::AfterUnlockForRead, print));
+    QVERIFY(reader.waitForFinished());
+}
+
+/*
+    writer1(main process) aquires a write-lock, writer2(lackey) blocks,
+    writer1 releases the lock, writer2 gets the lock
+*/
+void tst_QSystemReadWriteLock_oop::writeLockBlockRelease()
+{
+    bool print = false;
+    QSystemReadWriteLock testLock("Viper", QSystemReadWriteLock::Create);
+    if (print)
+        qDebug() << "Main process: About to lock for writing";
+    testLock.lockForWrite();
+    if (print)
+        qDebug() << "Main process: After lock for writing";
+
+    QLocalServer server;
+    QString connectionName = "writeLockBlockRelease";
+    QVERIFY(QLocalServer::removeServer(connectionName));
+    QVERIFY(server.listen(connectionName));
+
+    QProcess writer;
+    writer.setReadChannel(QProcess::StandardError);
+    QStringList args;
+    args << connectionName;
+    args << "WriteLock";
+    writer.start("./lackey", args);
+    QVERIFY(writer.waitForStarted());
+
+    QVERIFY(server.waitForNewConnection(5000));
+    QLocalSocket *oopSocket = server.nextPendingConnection();
+
+    QVERIFY(waitForLine(oopSocket, Lackey::BeforeLockForWrite,print));
+    QVERIFY(!waitForLine(oopSocket, Lackey::AfterUnlockForWrite,false, 1000));
+
+    testLock.unlock();
+    if (print)
+        qDebug() << "Main process: After unlock(write)";
+
+    QVERIFY(waitForLine(oopSocket, Lackey::AfterLockForWrite, print));
+    QVERIFY(waitForLine(oopSocket, Lackey::AfterUnlockForWrite, print));
+    QVERIFY(writer.waitForFinished());
+}
+
+/*
+    Two readers aquire a read-lock, one writer attempts a write lock and blocks,
+    the readers release their locks, the writer gets the lock.
+*/
+void tst_QSystemReadWriteLock_oop::multipleReadersBlockRelease()
+{
+    bool print = false;
+    //Use the Create enum to ensure synchronization primitives are reset
+    //to a "clean" state
+    QSystemReadWriteLock rwlock("Viper", QSystemReadWriteLock::Create);
+
+    const int numReaders = 2;
+    QProcess readers[numReaders];
+    QLocalServer readerServers[numReaders];
+    QLocalSocket *readerSockets[numReaders];
+
+    QString connectionName = "multipleReadersBlockRelease";
+
+    QStringList args;
+    args << "ReadLockReleaseable";
+
+    for( int i=0; i < numReaders; ++i) {
+        QString readerConnectionName = connectionName.append("_reader_").append(QString::number(i));
+        QVERIFY(QLocalServer::removeServer(readerConnectionName));
+        QVERIFY(readerServers[i].listen(readerConnectionName));
+        args.push_front(readerConnectionName);
+
+        readers[i].setReadChannel(QProcess::StandardError);
+        readers[i].start("./lackey", args);
+        QVERIFY(readers[i].waitForStarted());
+
+        QVERIFY(readerServers[i].waitForNewConnection(5000));
+        readerSockets[i] = readerServers[i].nextPendingConnection();
+
+        QVERIFY(waitForLine(readerSockets[i], Lackey::BeforeLockForRead, print));
+        QVERIFY(waitForLine(readerSockets[i], Lackey::AfterLockForRead, print));
+
+        args.pop_front();
+    }
+
+    QLocalServer server;
+    QString writerConnectionName = connectionName.append("_writer");
+    QVERIFY(QLocalServer::removeServer(writerConnectionName));
+    QVERIFY(server.listen(writerConnectionName));
+
+    QProcess writer;
+    writer.setReadChannel(QProcess::StandardError);
+    args.clear();
+    args << writerConnectionName;
+    args << "WriteLock";
+    writer.start("./lackey", args);
+    QVERIFY(writer.waitForStarted());
+
+    QVERIFY(server.waitForNewConnection(5000));
+    QLocalSocket *writerSocket = server.nextPendingConnection();
+
+    QVERIFY(waitForLine(writerSocket, Lackey::BeforeLockForWrite, print));
+    QVERIFY(!waitForLine(writerSocket, Lackey::AfterLockForWrite, false, 1000));
+
+    for (int i = 0; i < numReaders; ++i) {
+        readerSockets[i]->write("release\n");
+        QVERIFY(waitForLine(readerSockets[i], Lackey::AfterUnlockForRead, print));
+        if ( i < (numReaders - 1))
+            QVERIFY(!waitForLine(writerSocket, Lackey::AfterLockForWrite, false, 1000));
+    }
+    QVERIFY(waitForLine(writerSocket, Lackey::AfterLockForWrite, print));
+    QVERIFY(waitForLine(writerSocket, Lackey::AfterUnlockForWrite, print));
+
+    for(int i = 0; i < numReaders; ++i) {
+        QVERIFY(readers[i].waitForFinished());
+    }
+    QVERIFY(writer.waitForFinished());
+}
+
+/*
+    Multiple readers locks and unlocks a lock.
+*/
+void tst_QSystemReadWriteLock_oop::multipleReadersLoop()
+{
+    //ensure synchronization primitives are reset to a "clean" state
+    QSystemReadWriteLock testLock("Viper", QSystemReadWriteLock::Create);
+
+    int runTime = 10;
+    int holdTime = 10;
+    int waitTime = 0;
+
+    const int numReaders = 10;
+    QProcess readers[numReaders];
+    QStringList args;
+    args << "NoComms" // no communications between test and lackey
+        << "ReadLockLoop"
+        << QString::number(runTime)
+        << QString::number(holdTime)
+        << QString::number(waitTime);
+
+    for (int i = 0; i < numReaders; ++i) {
+        readers[i].setProcessChannelMode(QProcess::ForwardedChannels);
+        readers[i].start("./lackey", args);
+    }
+
+    for (int i=0; i < numReaders; ++i) {
+        QVERIFY(readers[i].waitForFinished(5000));
+        QCOMPARE(readers[i].exitCode(), 0);
+        QCOMPARE(readers[i].exitStatus(), QProcess::NormalExit);
+    }
+}
+
+/*
+    Multiple writers lock and unlock a lock
+*/
+void tst_QSystemReadWriteLock_oop::multipleWritersLoop()
+{
+    //ensure synchronization primitives are reset to a "clean" state
+    QSystemReadWriteLock testLock("Viper", QSystemReadWriteLock::Create);
+
+    int runTime = 10;
+    int holdTime = 10;
+    int waitTime = 0;
+
+    const int numWriters = 10;
+    QProcess writers[numWriters];
+    QStringList args;
+    args << "NoComms" // no communications between test and lackey
+        << "WriteLockLoop"
+        << QString::number(runTime)
+        << QString::number(holdTime)
+        << QString::number(waitTime);
+
+    for (int i = 0; i < numWriters; ++i) {
+        writers[i].setProcessChannelMode(QProcess::ForwardedChannels);
+        writers[i].start("./lackey", args);
+    }
+
+    for (int i = 0; i < numWriters; ++i) {
+        QVERIFY(writers[i].waitForFinished(5000));
+        QCOMPARE(writers[i].exitCode(), 0);
+        QCOMPARE(writers[i].exitStatus(), QProcess::NormalExit);
+    }
+}
+
+/*
+    Writers create an exclusive file while they have a write lock
+    and remove it on unlock.  Readers verify that that the file
+    does not exist and writers verify that the file does not
+    already exist when they obtain a lock.
+*/
+void tst_QSystemReadWriteLock_oop::exclusiveWriteTest()
+{
+    //ensure synchronization primitives are reset to a "clean" state
+    QSystemReadWriteLock testLock("Viper", QSystemReadWriteLock::Create);
+
+    int runTime=10000;
+#if !defined(Q_OS_WINCE)
+    const int numReaders = 20;
+#else
+    const int numReaders = 10;
+#endif
+
+    int readerHoldTime = 0;
+    int readerWaitTime = 1;
+
+    const int numWriters = 3;
+    int writerHoldTime = 20;
+    int writerWaitTime = 150;
+
+    QProcess readers[numReaders];
+    QProcess writers[numWriters];
+    QStringList args;
+    args << "NoComms" // no communications between test and lackey
+        << "ReadLockExcl"
+        << QString::number(runTime)
+        << QString::number(readerHoldTime)
+        << QString::number(readerWaitTime);
+
+    for (int i = 0; i < numReaders; ++i) {
+        readers[i].setProcessChannelMode(QProcess::ForwardedChannels);
+        readers[i].start("./lackey", args);
+    }
+
+    args.clear();
+    args << "NoComms" // no communications between test and lackey
+        << "WriteLockExcl"
+        << QString::number(runTime)
+        << QString::number(writerHoldTime)
+        << QString::number(writerWaitTime);
+    for (int i = 0; i < numWriters; ++i) {
+        writers[i].setProcessChannelMode(QProcess::ForwardedChannels);
+        writers[i].start("./lackey", args);
+    }
+
+    // Finding the existence of the exclusive file will
+    // result in a qFatal() in the lackey process
+    // The exit status checks below will fail if this
+    // is the case
+    for (int i = 0; i < numReaders; ++i) {
+        QVERIFY(readers[i].waitForFinished(2 * runTime));
+        QCOMPARE(readers[i].exitCode(), 0);
+        QCOMPARE(readers[i].exitStatus(), QProcess::NormalExit);
+     }
+
+    for (int i = 0; i < numWriters; ++i) {
+        QVERIFY(writers[i].waitForFinished(2 * runTime));
+        QCOMPARE(writers[i].exitCode(), 0);
+        QCOMPARE(writers[i].exitStatus(), QProcess::NormalExit);
+     }
+}
+
+/*
+    Writer1(main process) acquires a write-lock
+    Readers try to acquire read-locks but are blocked
+    Writer2(lackey) tries to acquire a write-lock, but is blocked
+    Writer1 releases the lock, writer2 gets the lock, readers still blocked
+    Writer2 releases the lock, readers are unblocked and get their read-locks
+*/
+void tst_QSystemReadWriteLock_oop::writerPrecedence()
+{
+    bool print = false;
+    QSystemReadWriteLock testRwLock("Viper", QSystemReadWriteLock::Create);
+    if (print)
+        qDebug() << "Main process: About to lock for writing";
+    testRwLock.lockForWrite();
+     if (print)
+        qDebug() << "Main process: After lock for writing";
+
+    const int numReaders = 5;
+    QProcess readers[numReaders];
+    QLocalServer readerServers[numReaders];
+    QLocalSocket *readerSockets[numReaders];
+
+    QString connectionName = "writePrecedence";
+
+    QStringList args;
+    args << "ReadLock";
+    for (int i = 0; i < numReaders; ++i) {
+        QString readerConnectionName = connectionName.append("_reader_").append(QString::number(i));
+        QVERIFY(QLocalServer::removeServer(readerConnectionName));
+        QVERIFY(readerServers[i].listen(readerConnectionName));
+        args.push_front(readerConnectionName);
+
+        readers[i].setReadChannel(QProcess::StandardError);
+        readers[i].start("./lackey", args);
+        QVERIFY(readers[i].waitForStarted());
+
+        QVERIFY(readerServers[i].waitForNewConnection(5000));
+        readerSockets[i] = readerServers[i].nextPendingConnection();
+
+        QVERIFY(waitForLine(readerSockets[i], Lackey::BeforeLockForRead, print));
+        QVERIFY(!waitForLine(readerSockets[i], Lackey::AfterLockForRead, false, 1000));
+
+        args.pop_front();
+    }
+
+    QLocalServer server;
+    QString writerConnectionName = connectionName.append("_writer");
+    QVERIFY(QLocalServer::removeServer(writerConnectionName));
+    QVERIFY(server.listen(writerConnectionName));
+
+    QProcess writer;
+    writer.setReadChannel(QProcess::StandardError);
+    args.clear();
+    args << writerConnectionName;
+    args << "WriteLockReleaseable";
+    writer.start("./lackey", args);
+
+    QVERIFY(server.waitForNewConnection(5000));
+    QLocalSocket *writerSocket = server.nextPendingConnection();
+
+    QVERIFY(waitForLine(writerSocket, Lackey::BeforeLockForWrite, print));
+    QVERIFY(!waitForLine(writerSocket, Lackey::AfterLockForWrite, false, 1000));
+
+    testRwLock.unlock();
+    QVERIFY(waitForLine(writerSocket, Lackey::AfterLockForWrite, print));
+     if (print)
+        qDebug() << "Main process: After unlock(write)";
+
+   //verify the reader processes are still blocked
+    QTest::qSleep(1000);
+    for (int i = 0; i < numReaders; ++i) {
+        QCOMPARE(readers[i].state(), QProcess::Running);
+    }
+
+    writerSocket->write("release\n");
+    QVERIFY(waitForLine(writerSocket, Lackey::AfterUnlockForWrite, print));
+
+    for (int i = 0; i < numReaders; ++i) {
+        QVERIFY(waitForLine(readerSockets[i], Lackey::AfterLockForRead, print));
+    }//Note: 2 loops (ie above and below) are used due to timing issues
+    for (int i = 0; i < numReaders; ++i) {
+        QVERIFY(waitForLine(readerSockets[i], Lackey::AfterUnlockForRead, print));
+    }
+
+    for (int i = 0; i < numReaders; ++i) {
+        QVERIFY(readers[i].waitForFinished(5000));
+    }
+    QVERIFY(writer.waitForFinished(5000));
+}
+
+
+/*
+   Waits for a given \a expectedLine to be written by \a process
+   for a given \a timeout(msecs).  The line written by the process
+   is printed to console if \a print is true.
+*/
+bool tst_QSystemReadWriteLock_oop::waitForLine(QIODevice * device,const QString &expectedLine, bool print, int timeout)
+{
+    if (!device->waitForReadyRead(timeout))
+    {
+        if (print)
+            qWarning() << "Wait for ready read returned false";
+        return false;
+    }
+
+    QString line = device->readLine();
+    line = line.trimmed();
+    if (print)
+        qDebug() << qPrintable(line);
+    if (line.compare(expectedLine) !=0) {
+        qWarning() << "Expected Line: \t" << qPrintable(expectedLine);
+        qWarning() << "Actual Line: \t" << qPrintable(line);
+        return false;
+    }
+
+    return true;
+}
+
+QTEST_MAIN(tst_QSystemReadWriteLock_oop)
+#include "tst_qsystemreadwritelock_oop.moc"