tests/auto/qvaluespacesubscriber/tst_qvaluespacesubscribershared.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qvaluespacesubscriber/tst_qvaluespacesubscribershared.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,1244 @@
+/****************************************************************************
+**
+** 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 "tst_qvaluespacesubscribershared.h"
+
+#include <qvaluespacesubscriber.h>
+#include <qvaluespacepublisher.h>
+#include <qvaluespacemanager_p.h>
+
+#include <QTest>
+#include <QSet>
+#include <QSignalSpy>
+#include <QProcess>
+#include <QFile>
+#include <QThread>
+#include <QTimer>
+#include <math.h>
+
+#include <QDebug>
+
+#ifdef Q_OS_WIN
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#endif
+
+#define ERROR_SETVALUE_NOT_SUPPORTED 1
+
+#if defined(Q_OS_SYMBIAN)// || defined (Q_OS_LINUX)
+    #define QTRY_COMPARE(a,e)                       \
+        for (int _i = 0; _i < 100; _i++) {          \
+            if ((a) == (e)) break;                  \
+            QTest::qWait(1);                        \
+        }                                           \
+        QCOMPARE(a, e)
+
+    #define QTRY_VERIFY(a)                          \
+        for (int _i = 0; _i < 100; _i ++) {         \
+            if (a) break;                           \
+            QTest::qWait(1);                        \
+        }                                           \
+        QVERIFY(a)
+#else
+    #define QTRY_COMPARE(a,e)                       \
+        for (int _i = 0; _i < 10000; _i += 100) {    \
+            if ((a) == (e)) break;                  \
+            QTest::qWait(100);                      \
+        }                                           \
+        QCOMPARE(a, e)
+
+    #define QTRY_VERIFY(a)                       \
+        for (int _i = 0; _i < 10000; _i += 100) {    \
+            if (a) break;                  \
+            QTest::qWait(100);                      \
+        }                                           \
+        QVERIFY(a)
+#endif
+QTM_USE_NAMESPACE
+class ChangeListener : public QObject
+{
+    Q_OBJECT
+
+Q_SIGNALS:
+    void baseChanged();
+    void interestChanged(const QString&, bool);
+};
+
+Q_DECLARE_METATYPE(QValueSpaceSubscriber*)
+Q_DECLARE_METATYPE(QAbstractValueSpaceLayer*)
+Q_DECLARE_METATYPE(QVariant)
+Q_DECLARE_METATYPE(QValueSpace::LayerOptions)
+Q_DECLARE_METATYPE(QUuid)
+
+void tst_QValueSpaceSubscriber::initTestCase()
+{
+    qRegisterMetaType<QVariant>("QVariant");
+    qRegisterMetaType<QValueSpace::LayerOptions>("QValueSpace::LayerOptions");
+
+#ifdef Q_OS_WIN
+    HKEY key;
+    long result = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Nokia",
+                               0, KEY_ALL_ACCESS, &key);
+    if (result == ERROR_SUCCESS) {
+        result = RegDeleteKey(key, L"QtMobility\\volatileContext");
+        result = RegDeleteKey(key, L"QtMobility\\nonVolatileContext");
+        result = RegDeleteKey(key, L"QtMobility");
+
+        RegCloseKey(key);
+    }
+#endif
+
+#if defined(Q_OS_UNIX) && defined(QT_START_VALUESPACE)
+    QFile::remove("/tmp/qt-0/valuespace_shmlayer");
+#endif
+
+#if defined(QT_START_VALUESPACE)
+    QValueSpace::initValueSpaceServer();
+#endif
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i) {
+        QValueSpacePublisher *root = new QValueSpacePublisher(layers.at(i)->id(), "/");
+        root->setValue("/home/user/bool", true);
+        root->setValue("/home/user/int", 3);
+        root->setValue("/home/user/QString", QString("testString"));
+        QStringList stringList;
+        stringList << QString("String 1") << QString("String 2");
+        root->setValue("/home/user/QStringList", stringList);
+        root->setValue("/home/user/qint64", qint64(64));
+        root->setValue("/home/user/QByteArray", QByteArray("testByteArray"));
+        root->setValue("/home/user/double", 4.56);
+        root->setValue("/home/user/float", (float)4.56f);
+        root->setValue("/home/user/QChar", QChar('c'));
+        //so far not a lot of data types are supported
+        //root->setValue("/home/user/QRect", QRect(0,0,5,6));
+
+        root->setValue("/home/usercount", 1);
+
+        root->setValue("/layer/name", layers.at(i)->name());
+        root->setValue("/layer/id", layers.at(i)->id().toString());
+        root->setValue("/layer/options", uint(layers.at(i)->layerOptions()));
+
+        root->sync();
+
+        roots.insert(layers.at(i), root);
+
+        QValueSpacePublisher *busy = new QValueSpacePublisher(layers.at(i)->id(), "/usr");
+        busy->setValue("alex/busy", true);
+        busy->setValue("lorn/busy", false);
+        busy->sync();
+
+        busys.insert(layers.at(i), busy);
+    }
+}
+
+void tst_QValueSpaceSubscriber::cleanupTestCase()
+{
+    foreach (QAbstractValueSpaceLayer *layer, roots.keys()) {
+        QValueSpacePublisher *root = roots.take(layer);
+
+        if (layer->layerOptions() & QValueSpace::PermanentLayer) {
+            root->resetValue("/home/user/bool");
+            root->resetValue("/home/user/int");
+            root->resetValue("/home/user/QString");
+            root->resetValue("/home/user/QStringList");
+            root->resetValue("/home/user/qint64");
+            root->resetValue("/home/user/QByteArray");
+            root->resetValue("/home/user/double");
+            root->resetValue("/home/user/float");
+            root->resetValue("/home/user/QChar");
+            root->resetValue("/home/user");
+            root->resetValue("/home/usercount");
+            root->resetValue("/home");
+            root->resetValue("/layer/name");
+            root->resetValue("/layer/id");
+            root->resetValue("/layer/options");
+            root->resetValue("/layer");
+        }
+
+        delete root;
+    }
+
+    foreach (QAbstractValueSpaceLayer *layer, busys.keys()) {
+        QValueSpacePublisher *busy = busys.take(layer);
+
+        if (layer->layerOptions() & QValueSpace::PermanentLayer) {
+            busy->resetValue("alex/busy");
+            busy->resetValue("alex");
+            busy->resetValue("lorn/busy");
+            busy->resetValue("lorn");
+            busy->resetValue(QString());
+        }
+
+        delete busy;
+    }
+}
+
+void tst_QValueSpaceSubscriber::dataVersatility_data()
+{
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    QTest::addColumn< QVariant >("data");
+    QTest::addColumn< QString >("typeString");
+    QTest::addColumn< int >("typeIdent");
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        //these types have custom loading/saving operator
+        QTest::newRow("Int") << layer
+            << QVariant((int)567) << "Int" << (int)QVariant::Int;
+        QTest::newRow("Bool") << layer
+            << QVariant((bool)true) << "Bool" << (int)QVariant::Bool;
+        QTest::newRow("UInt") << layer
+            << QVariant((unsigned int)4) << "UInt" << (int)QVariant::UInt;
+        QTest::newRow("LongLong") << layer
+            << QVariant((long long)5) << "LongLong" << (int)QVariant::LongLong;
+        QTest::newRow("ULongLong") << layer
+            << QVariant((unsigned long long)6) << "ULongLong" << (int)QVariant::ULongLong;
+        QTest::newRow("Double") << layer
+            << QVariant((double)4.5) << "Double" << (int)QVariant::Double;
+        QTest::newRow("QChar") << layer
+            << QVariant(QChar('@')) << "Char" << (int)QVariant::Char;
+        QTest::newRow("QString") << layer
+            << QVariant(QString("asd")) << "QString" << (int)QVariant::String;
+        QTest::newRow("QByteArray") << layer
+            << QVariant(QByteArray("bytearray")) << "QByteArray" << (int)QVariant::ByteArray;
+        QTest::newRow("QStringList") << layer
+            << QVariant(QStringList() << QString("String 1") << QString("String 2"))
+            << "QStringList" << (int)QVariant::StringList;
+
+        //other types not specifically covered by valuespace -> uses QVariant based serialization
+        QTest::newRow("QRect") << layer
+            << QVariant(QRect(4,5,6,7)) << "QRect" << (int)QVariant::Rect;
+        QTest::newRow("QDateTime") << layer
+            << QVariant(QDateTime::currentDateTime()) << "QDateTime" << (int)QVariant::DateTime;
+    }
+}
+
+void tst_QValueSpaceSubscriber::dataVersatility()
+{
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    QFETCH(QVariant, data);
+    QFETCH(QString, typeString);
+    QFETCH(int, typeIdent);
+
+    QCOMPARE(data.type(), (QVariant::Type)typeIdent);
+
+    QValueSpacePublisher publisher(layer->id(), "/usr/data");
+    publisher.setValue(typeString, data);
+    publisher.sync();
+    QValueSpaceSubscriber subscriber(layer->id(), "/usr/data");
+    QVariant v = subscriber.value(typeString);
+
+    QCOMPARE(v.type(), (QVariant::Type)typeIdent);
+    QCOMPARE(v, data);
+
+    if (layer->layerOptions() & QValueSpace::PermanentLayer)
+        publisher.resetValue(typeString);
+}
+
+void tst_QValueSpaceSubscriber::testConstructor_data()
+{
+    QTest::addColumn< QVariant >("testItem");
+    QTest::addColumn< QVariant >("value");
+    QTest::addColumn< QStringList >("subPaths");
+    QTest::addColumn< QString >("path");
+    QTest::addColumn< QString >("relItemPath");
+    QTest::addColumn< int >("expectedValue");
+
+    QStringList allPaths;
+    allPaths << "bool" << "int" << "QString" << "QStringList" << "qint64" << "QByteArray"
+             << "double" << "float" << "QChar";
+
+    QStringList rootPaths;
+    rootPaths << "home" << "usr" << "layer";
+
+    QStringList homePaths;
+    homePaths << "user" << "usercount";
+
+    QVariant data;
+    QValueSpaceSubscriber *subscriber;
+
+    // Subscribers with / path.
+    subscriber = new QValueSpaceSubscriber(QString(), this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(QString(), this)")
+        << data
+        << QVariant()
+        << rootPaths
+        << QString("/")
+        << QString("home/user/int")
+        << 3;
+
+    subscriber = new QValueSpaceSubscriber(QString("/"), this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(QString(\"/\"), this)")
+        << data
+        << QVariant()
+        << rootPaths
+        << QString("/")
+        << QString("home/user/int")
+        << 3;
+
+    subscriber = new QValueSpaceSubscriber("", this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(\"\", this)")
+        << data
+        << QVariant()
+        << rootPaths
+        << QString("/")
+        << QString("home/user/int")
+        << 3;
+
+    subscriber = new QValueSpaceSubscriber("/", this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(\"/\", this)")
+        << data
+        << QVariant()
+        << rootPaths
+        << QString("/")
+        << QString("home/user/int")
+        << 3;
+
+    subscriber = new QValueSpaceSubscriber(this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(this)")
+        << data
+        << QVariant()
+        << rootPaths
+        << QString("/")
+        << QString("home/user/int")
+        << 3;
+
+    // Subscribers with /home path.
+    subscriber = new QValueSpaceSubscriber(QString("/home"), this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(QString(\"/home\"), this)")
+        << data
+        << QVariant()
+        << homePaths
+        << QString("/home")
+        << QString("user/int")
+        << 3;
+
+    // Subscribers with /home/user path.
+    subscriber = new QValueSpaceSubscriber(QString("/home/user"), this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(QString(\"/home/user\"), this)")
+        << data
+        << QVariant()
+        << allPaths
+        << QString("/home/user")
+        << QString("int")
+        << 3;
+
+    // Direct value subscriber with /home/user/int path.
+    subscriber = new QValueSpaceSubscriber("/home/user/int", this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(\"/home/user/int\", this)")
+        << data
+        << QVariant(3)
+        << QStringList()
+        << QString("/home/user/int")
+        << QString()
+        << 3;
+
+    // Subscriber with invalid path.
+    subscriber = new QValueSpaceSubscriber(QString("/home/invalidPath"), this);
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber(QString(\"/home/invalidPath\"), this)")
+        << data
+        << QVariant()
+        << QStringList()
+        << QString("/home/invalidPath")
+        << QString("user/int")
+        << 100; //should fails -> returns default
+
+    // setPath to /
+    subscriber = new QValueSpaceSubscriber(this);
+    subscriber->setPath("/");
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber::setPath(\"/\"")
+        << data
+        << QVariant()
+        << rootPaths
+        << QString("/")
+        << QString("home/user/int")
+        << 3;
+
+    // setPath to /home
+    subscriber = new QValueSpaceSubscriber(this);
+    subscriber->setPath("/home");
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber::setPath(\"/home\")")
+        << data
+        << QVariant()
+        << homePaths
+        << QString("/home")
+        << QString("user/int")
+        << 3;
+
+    // setPath to /home/user
+    subscriber = new QValueSpaceSubscriber(this);
+    subscriber->setPath("/home/user");
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber::setPath(\"/home/user\")")
+        << data
+        << QVariant()
+        << allPaths
+        << QString("/home/user")
+        << QString("int")
+        << 3;
+
+    // setPath to &QValueSpaceSubscriber
+    subscriber = new QValueSpaceSubscriber(this);
+    {
+        QValueSpaceSubscriber user("/home/user");
+        subscriber->setPath(&user);
+    }
+    qVariantSetValue(data, subscriber);
+    QTest::newRow("QValueSpaceSubscriber::setPath(&QValueSpaceSubscriber(\"/home/user\")")
+        << data
+        << QVariant()
+        << allPaths
+        << QString("/home/user")
+        << QString("int")
+        << 3;
+}
+
+void tst_QValueSpaceSubscriber::testConstructor()
+{
+    QFETCH(QVariant, testItem);
+    QFETCH(QVariant, value);
+    QFETCH(QStringList, subPaths);
+    QFETCH(QString, path);
+    QFETCH(QString, relItemPath);
+    QFETCH(int, expectedValue);
+
+    QValueSpaceSubscriber *subscriber = qvariant_cast<QValueSpaceSubscriber*>(testItem);
+    QCOMPARE(subscriber->parent(), (QObject*)this);
+    QCOMPARE(subscriber->value(), value);
+    QVERIFY(subscriber->subPaths().toSet().contains(subPaths.toSet()));
+    QCOMPARE(subscriber->path(), path);
+    QCOMPARE(subscriber->value(relItemPath, 100).toInt(), expectedValue);
+}
+
+#define ADD(opt, invalid) do {\
+    QTest::newRow(QString::number(opt).append(" const QString &").toLocal8Bit().constData()) \
+        << (QValueSpace::UnspecifiedLayer | opt) << invalid; \
+} while (false)
+
+void tst_QValueSpaceSubscriber::testFilterConstructor_data()
+{
+    QTest::addColumn<QValueSpace::LayerOptions>("options");
+    QTest::addColumn<bool>("connected");
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        ADD(layer->layerOptions(), true);
+    }
+
+    ADD(QValueSpace::UnspecifiedLayer, true);
+    ADD(QValueSpace::PermanentLayer, true);
+    ADD(QValueSpace::TransientLayer, true);
+    ADD(QValueSpace::PermanentLayer | QValueSpace::TransientLayer, false);
+    ADD(QValueSpace::WritableLayer, true);
+    ADD(QValueSpace::ReadOnlyLayer, true);
+    ADD(QValueSpace::WritableLayer | QValueSpace::ReadOnlyLayer, false);
+}
+
+void tst_QValueSpaceSubscriber::testFilterConstructor()
+{
+    QFETCH(QValueSpace::LayerOptions, options);
+    QFETCH(bool, connected);
+
+    QValueSpaceSubscriber *subscriber;
+
+    subscriber = new QValueSpaceSubscriber(options, QString("/layer"));
+
+    if (!connected)
+        QVERIFY(!subscriber->isConnected());
+
+    if (subscriber->isConnected()) {
+        QValueSpace::LayerOptions actualOptions =
+            QValueSpace::LayerOptions(subscriber->value("options", 0).toUInt());
+
+        QVERIFY(options == QValueSpace::UnspecifiedLayer || actualOptions & options);
+    }
+}
+
+void tst_QValueSpaceSubscriber::testPathChanges()
+{
+    QValueSpaceSubscriber subscriber;
+
+    QStringList rootPaths;
+    rootPaths << "layer" << "usr" << "home";
+
+    QStringList homePaths;
+    homePaths << "user" << "usercount";
+
+    QStringList homeUserPaths;
+    homeUserPaths << "bool" << "int" << "QString" << "QStringList" << "qint64" << "QByteArray"
+                  << "double" << "float" << "QChar";
+
+    QCOMPARE(subscriber.path(), QLatin1String("/"));
+    QVERIFY(subscriber.subPaths().toSet().contains(rootPaths.toSet()));
+
+    subscriber.cd("home");
+    QCOMPARE(subscriber.path(), QLatin1String("/home"));
+    QVERIFY(subscriber.subPaths().toSet().contains(homePaths.toSet()));
+
+    subscriber.cd("user");
+    QCOMPARE(subscriber.path(), QLatin1String("/home/user"));
+    QVERIFY(subscriber.subPaths().toSet().contains(homeUserPaths.toSet()));
+
+    subscriber.cdUp();
+    QCOMPARE(subscriber.path(), QLatin1String("/home"));
+    QVERIFY(subscriber.subPaths().toSet().contains(homePaths.toSet()));
+
+    subscriber.cd("/home/user");
+    QCOMPARE(subscriber.path(), QLatin1String("/home/user"));
+    QVERIFY(subscriber.subPaths().toSet().contains(homeUserPaths.toSet()));
+}
+
+void tst_QValueSpaceSubscriber::contentsChanged_data()
+{
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    QTest::addColumn<bool>("implicit");
+    QTest::addColumn< QString >("subscriber_path");
+    QTest::addColumn< QString >("value_path");
+    QTest::addColumn< int >("should_emit_signal");
+    QTest::addColumn< bool >("old_value");
+    QTest::addColumn< bool >("new_value");
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        QTest::newRow(layer->name().append(": implicit (empty)").toLocal8Bit().constData())
+            << layer
+            << true
+            << ""
+            << "usr/alex/busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": explicit (empty)").toLocal8Bit().constData())
+            << layer
+            << false
+            << ""
+            << "usr/alex/busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": implicit /").toLocal8Bit().constData())
+            << layer
+            << true
+            << "/"
+            << "usr/alex/busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": explicit /").toLocal8Bit().constData())
+            << layer
+            << false
+            << "/"
+            << "usr/alex/busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": implicit /usr").toLocal8Bit().constData())
+            << layer
+            << true
+            << "/usr"
+            << "alex/busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": explicit /usr").toLocal8Bit().constData())
+            << layer
+            << false
+            << "/usr"
+            << "alex/busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": implicit /usr/alex").toLocal8Bit().constData())
+            << layer
+            << true
+            << "/usr/alex"
+            << "busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": explicit /usr/alex").toLocal8Bit().constData())
+            << layer
+            << false
+            << "/usr/alex"
+            << "busy"
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": implicit /usr/alex/busy").toLocal8Bit().constData())
+            << layer
+            << true
+            << "/usr/alex/busy"
+            << ""
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": explicit /usr/alex/busy").toLocal8Bit().constData())
+            << layer
+            << false
+            << "/usr/alex/busy"
+            << ""
+            << 1
+            << false
+            << true;
+
+        QTest::newRow(layer->name().append(": implicit /usr/lorn").toLocal8Bit().constData())
+            << layer
+            << true
+            << "/usr/lorn"
+            << "busy"
+            << 0
+            << false
+            << false;
+
+        QTest::newRow(layer->name().append(": explicit /usr/lorn").toLocal8Bit().constData())
+            << layer
+            << false
+            << "/usr/lorn"
+            << "busy"
+            << 0
+            << false
+            << false;
+    }
+}
+
+void tst_QValueSpaceSubscriber::contentsChanged()
+{
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    QFETCH(bool, implicit);
+    QFETCH(QString, subscriber_path);
+    QFETCH(QString, value_path);
+    QFETCH(int, should_emit_signal);
+    QFETCH(bool, old_value);
+    QFETCH(bool, new_value);
+
+    QValueSpacePublisher *busy = busys.value(layer);
+
+    busy->setValue("alex/busy", old_value);
+    busy->sync();
+
+    QValueSpaceSubscriber subscriber(layer->id(), subscriber_path);
+    QCOMPARE(subscriber.value(value_path,!old_value).toBool(), old_value);
+
+    ChangeListener* listener = 0;
+    QSignalSpy *spy;
+    if (implicit) {
+        listener = new ChangeListener;
+        spy = new QSignalSpy(listener, SIGNAL(baseChanged()));
+        connect(&subscriber, SIGNAL(contentsChanged()), listener, SIGNAL(baseChanged()));
+    } else {
+        spy = new QSignalSpy(&subscriber, SIGNAL(contentsChanged()));
+        subscriber.property("value");
+    }
+
+    #ifdef Q_OS_LINUX
+        //Wait for possible asynchronously emitted signals
+        QEventLoop loop;
+        QTimer::singleShot(100, &loop, SLOT(quit()));
+        loop.exec();
+        spy->clear();
+    #endif
+
+    QCOMPARE(spy->count(), 0);
+
+    busy->setValue("alex/busy", new_value);
+    busy->sync();
+
+    QTRY_COMPARE(spy->count(), should_emit_signal);
+    QCOMPARE(subscriber.value(value_path,!old_value).toBool(), new_value);
+
+    spy->clear();
+
+    //removing the value triggers signal
+    busy->resetValue("alex/busy");
+    busy->sync();
+    QTRY_COMPARE(spy->count(), should_emit_signal);
+    QCOMPARE(subscriber.value(value_path,!old_value).toBool(), new_value);
+
+    spy->clear();
+    busy->setValue("alex/busy", new_value);
+    busy->sync();
+
+    QTRY_COMPARE(spy->count(), should_emit_signal);
+    QCOMPARE(subscriber.value(value_path,!old_value).toBool(), new_value);
+
+    if (listener)
+        disconnect(&subscriber, SIGNAL(contentsChanged()), listener, SIGNAL(baseChanged()));
+
+    delete spy;
+    delete listener;
+}
+
+void tst_QValueSpaceSubscriber::value_data()
+{
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i)
+        QTest::newRow(layers.at(i)->name().toLocal8Bit().constData()) << layers.at(i);
+}
+
+void tst_QValueSpaceSubscriber::value()
+{
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    QValueSpaceSubscriber base(layer->id(), QString("/"));
+    QCOMPARE(base.value("home/usercount", 5).toInt(), 1);
+    QCOMPARE(base.value("home/user/QString", "default").toString(), QString("testString"));
+    QCOMPARE(base.value("home/user/bool", false).toBool(), true);
+    QCOMPARE(base.value("home/user/int", 5).toInt(), 3);
+    QCOMPARE(base.value("home/user/QByteArray", QByteArray("invalid")).toByteArray(),
+             QByteArray("testByteArray"));
+    QVERIFY(fabs(base.value("home/user/double", 4.0).toDouble() - double(4.56)) < 0.01);
+    //QCOMPARE(base.value("home/user/float", 4.0).toDouble(), (double)4.56);
+
+    QValueSpaceSubscriber base1(layer->id(), QString("/home"));
+    QCOMPARE(base1.value(QString("usercount"), 5).toInt(), 1);
+    QCOMPARE(base1.value(QString("user/QString"), "default").toString(), QString("testString"));
+    QCOMPARE(base1.value("user/bool", false).toBool(), true);
+    QCOMPARE(base1.value("user/int", 5).toInt(), 3);
+    QCOMPARE(base1.value("user/QByteArray", QByteArray("invalid")).toByteArray(),
+             QByteArray("testByteArray"));
+    QVERIFY(fabs(base.value("home/user/double", 4.0).toDouble() - double(4.56)) < 0.01);
+    //QCOMPARE(base1.value("user/float", 4.0).toDouble(), double(4.56));
+
+    QValueSpaceSubscriber base2(layer->id(), QString("/home/user"));
+    QCOMPARE(base2.value(QString("usercount"), 5).toInt(), 5);
+    QCOMPARE(base2.value(QString("QString"), "default").toString(), QString("testString"));
+    QCOMPARE(base2.value("bool", false).toBool(), true);
+    QCOMPARE(base2.value("int", 5).toInt(), 3);
+    QCOMPARE(base2.value("QByteArray", QByteArray("invalid")).toByteArray(),
+             QByteArray("testByteArray"));
+    QVERIFY(fabs(base.value("home/user/double", 4.0).toDouble() - double(4.56)) < 0.01);
+    //QCOMPARE(base2.value("float", 4.0).toDouble(), 4.56);
+}
+
+void tst_QValueSpaceSubscriber::ipcTests_data()
+{
+#ifdef QT_NO_PROCESS
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i)
+        QTest::newRow(layers.at(i)->name().toLocal8Bit().constData()) << layers.at(i);
+#endif
+}
+
+void tst_QValueSpaceSubscriber::ipcTests()
+{
+#ifdef Q_OS_SYMBIAN
+    QSKIP("No multiple processes in Symbian emulator", SkipAll);
+#endif
+
+#if defined(QT_NO_PROCESS)
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    QValueSpaceSubscriber subscriber(layer->id(), "/usr/lackey/subdir/value");
+    ChangeListener listener;
+    QSignalSpy spy(&listener, SIGNAL(baseChanged()));
+    connect(&subscriber, SIGNAL(contentsChanged()), &listener, SIGNAL(baseChanged()));
+
+    QProcess process;
+    process.setProcessChannelMode(QProcess::ForwardedChannels);
+#ifdef Q_OS_UNIX
+    process.start("./vsiTestLackey", QStringList() << "-ipcTests" << layer->id().toString());
+#else
+    process.start("vsiTestLackey", QStringList() << "-ipcTests" << layer->id().toString());
+#endif
+    QVERIFY(process.waitForStarted());
+
+    //lackey sets value to 100 as part of its startup
+    QTRY_COMPARE(spy.count(), 1);
+    QCOMPARE(subscriber.value("", 99).toInt(), 100);
+    spy.clear();
+
+    //lackey sets value to 101
+    QTRY_COMPARE(spy.count(), 1);
+    QCOMPARE(subscriber.value("", 99).toInt(), 101);
+    spy.clear();
+
+    //value was removed -> returns default
+    QTRY_COMPARE(spy.count(), 1);
+    QCOMPARE(subscriber.value("", 99).toInt(), 99);
+    spy.clear();
+
+    //lackey sets value to 102
+    QTRY_COMPARE(spy.count(), 1);
+    QCOMPARE(subscriber.value("", 99).toInt(), 102);
+    spy.clear();
+
+    //value was removed -> returns default
+    QTRY_COMPARE(spy.count(), 1);
+    QCOMPARE(subscriber.value("", 99).toInt(), 99);
+
+    QVERIFY(process.waitForFinished(10000));
+#endif
+}
+
+void tst_QValueSpaceSubscriber::ipcRemoveKey_data()
+{
+#ifdef QT_NO_PROCESS
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    bool skip = true;
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        if (layer->layerOptions() & QValueSpace::PermanentLayer)
+            continue;
+
+        skip = false;
+
+        QTest::newRow(layer->name().toLocal8Bit().constData()) << layer;
+    }
+
+    if (skip)
+        QSKIP("No applicable layers found.", SkipAll);
+#endif
+}
+
+void tst_QValueSpaceSubscriber::ipcRemoveKey()
+{
+#if defined(QT_NO_PROCESS)
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    QValueSpaceSubscriber subscriber(layer->id(), "/ipcRemoveKey");
+
+    ChangeListener listener;
+    QSignalSpy changeSpy(&listener, SIGNAL(baseChanged()));
+    QObject::connect(&subscriber, SIGNAL(contentsChanged()), &listener, SIGNAL(baseChanged()));
+
+    QProcess process;
+    process.setProcessChannelMode(QProcess::ForwardedChannels);
+#ifdef Q_OS_UNIX
+    process.start("./vsiTestLackey", QStringList() << "-ipcRemoveKey" << layer->id().toString());
+#else
+    process.start("vsiTestLackey", QStringList() << "-ipcRemoveKey" << layer->id().toString());
+#endif
+    QVERIFY(process.waitForStarted());
+
+    // Wait for lackey to create "value".
+    QTRY_COMPARE(changeSpy.count(), 1);
+    QCOMPARE(subscriber.value("value", 5).toInt(), 100);
+
+    // Wait for lackey to delete key "/ipcRemoveKey".
+    changeSpy.clear();
+    QTRY_COMPARE(changeSpy.count(), 1);
+
+    QVERIFY(subscriber.subPaths().isEmpty());
+    QCOMPARE(subscriber.value("value", 6).toInt(), 6);
+    QVERIFY(process.waitForFinished(10000));
+#endif
+}
+
+void tst_QValueSpaceSubscriber::interestNotification_data()
+{
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    QTest::addColumn<QString>("publisherPath");
+    QTest::addColumn<QString>("attribute");
+
+    bool foundSupported = false;
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        if (!layer->supportsInterestNotification())
+            continue;
+
+        foundSupported = true;
+
+        QTest::newRow("QValueSpaceSubscriber(QString)")
+            << layer << "/interestNotification" << "/value";
+    }
+
+    if (!foundSupported)
+        QSKIP("No layer supporting interest notifications found.", SkipAll);
+}
+
+void tst_QValueSpaceSubscriber::interestNotification()
+{
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    QFETCH(QString, publisherPath);
+    QFETCH(QString, attribute);
+
+    QValueSpacePublisher *publisher;
+    publisher = new QValueSpacePublisher(layer->id(), publisherPath);
+
+    ChangeListener notificationListener;
+    connect(publisher, SIGNAL(interestChanged(QString,bool)),
+            &notificationListener, SIGNAL(interestChanged(QString,bool)));
+
+    QSignalSpy notificationSpy(&notificationListener,
+                               SIGNAL(interestChanged(QString,bool)));
+
+    const QString subscriberPath = publisherPath + attribute;
+
+    QValueSpaceSubscriber *subscriber = new QValueSpaceSubscriber(layer->id(), subscriberPath);
+
+    QTRY_COMPARE(notificationSpy.count(), 1);
+
+    QList<QVariant> arguments = notificationSpy.takeFirst();
+    QCOMPARE(arguments.at(0).type(), QVariant::String);
+    QCOMPARE(arguments.at(0).toString(), attribute);
+    QCOMPARE(arguments.at(1).type(), QVariant::Bool);
+    QCOMPARE(arguments.at(1).toBool(), true);
+
+    QCOMPARE(subscriber->value(QString(), 10).toInt(), 10);
+
+    publisher->setValue(attribute, 5);
+    publisher->sync();
+
+    QCOMPARE(subscriber->value(QString(), 10).toInt(), 5);
+
+    notificationSpy.clear();
+
+    delete subscriber;
+
+    QTRY_COMPARE(notificationSpy.count(), 1);
+
+    arguments = notificationSpy.takeFirst();
+    QCOMPARE(arguments.at(0).type(), QVariant::String);
+    QCOMPARE(arguments.at(0).toString(), attribute);
+    QCOMPARE(arguments.at(1).type(), QVariant::Bool);
+    QCOMPARE(arguments.at(1).toBool(), false);
+
+    delete publisher;
+}
+
+void tst_QValueSpaceSubscriber::ipcInterestNotification_data()
+{
+#ifdef QT_NO_PROCESS
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+    QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+    bool foundSupported = false;
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        if (!layer->supportsInterestNotification())
+            continue;
+
+        foundSupported = true;
+
+        QTest::newRow(layer->name().toLocal8Bit().constData()) << layer;
+    }
+
+    if (!foundSupported)
+        QSKIP("No layer supporting interest notifications found.", SkipAll);
+#endif
+}
+
+void tst_QValueSpaceSubscriber::ipcInterestNotification()
+{
+#if defined(QT_NO_PROCESS)
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+    QFETCH(QAbstractValueSpaceLayer *, layer);
+
+    // Test QValueSpaceSubscriber construction before QValueSpacePublisher.
+
+    QValueSpaceSubscriber *subscriber =
+        new QValueSpaceSubscriber(layer->id(), "/ipcInterestNotification/value");
+
+    ChangeListener listener;
+    QObject::connect(subscriber, SIGNAL(contentsChanged()), &listener, SIGNAL(baseChanged()));
+    QSignalSpy changeSpy(&listener, SIGNAL(baseChanged()));
+
+    // Lackey is not running, so value will not exist.
+    QCOMPARE(subscriber->value(QString(), 10).toInt(), 10);
+
+    QProcess process;
+    process.setProcessChannelMode(QProcess::ForwardedChannels);
+#ifdef Q_OS_UNIX
+    process.start("./vsiTestLackey", QStringList()
+        << "-ipcInterestNotification" << layer->id().toString());
+#else
+    process.start("vsiTestLackey", QStringList()
+        << "-ipcInterestNotification" << layer->id().toString());
+#endif
+    QVERIFY(process.waitForStarted());
+
+    // Lackey will receive interestChanged from server and set the attribute.
+    QTRY_COMPARE(changeSpy.count(), 1);
+    changeSpy.clear();
+
+    QCOMPARE(subscriber->value(QString(), 10).toInt(), 5);
+
+    // Lackey will receive interestChanged and remove attribute.
+    delete subscriber;
+    QTest::qWait(1000);
+
+    // Test QValueSpaceSubscriber construction after QValueSpacePublisher
+
+    subscriber = new QValueSpaceSubscriber(layer->id(), "/ipcInterestNotification/value");
+    QObject::connect(subscriber, SIGNAL(contentsChanged()), &listener, SIGNAL(baseChanged()));
+
+    QTRY_COMPARE(changeSpy.count(), 1);
+
+    QCOMPARE(subscriber->value(QString(), 10).toInt(), 5);
+#endif
+}
+
+void tst_QValueSpaceSubscriber::clientServer()
+{
+#if defined(QT_START_VALUESPACE)
+    QVERIFY(QValueSpaceManager::instance()->isServer());
+#else
+    QVERIFY(!QValueSpaceManager::instance()->isServer());
+#endif
+}
+
+class WriteThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    WriteThread(const QUuid& layerId);
+
+    void setDone();
+
+protected:
+    void run();
+
+private:
+    QValueSpacePublisher *publisher;
+    bool done;
+    const QUuid& layerId;
+};
+
+WriteThread::WriteThread(const QUuid& layerId)
+:   publisher(0), done(false), layerId(layerId)
+{
+}
+
+void WriteThread::setDone()
+{
+    done = true;
+}
+
+void WriteThread::run()
+{
+    QTest::qWait(100);  // give some ReadThreads some time to start.
+
+    QValueSpacePublisher publisher(layerId, "/threads");
+
+    uint value = 0;
+    while (!done) {
+        publisher.setValue("value", value);
+        publisher.sync();
+        QTest::qWait(100);
+        value += 100;
+    }
+}
+
+class ReadThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    ReadThread(QValueSpaceSubscriber *subscriber, bool sync, const QUuid& layerId);
+
+protected:
+    void run();
+
+private:
+    QValueSpaceSubscriber *masterSubscriber;
+    int iterations;
+    bool synchronised;
+    const QUuid& layerId;
+};
+
+ReadThread::ReadThread(QValueSpaceSubscriber *subscriber, bool sync, const QUuid& layerId)
+:   masterSubscriber(subscriber), iterations(0), synchronised(sync), layerId(layerId)
+{
+}
+
+void ReadThread::run()
+{
+    while (true) {
+        QValueSpaceSubscriber subscriber(layerId, masterSubscriber->path());
+
+        if (synchronised) {
+            QEventLoop loop;
+            connect(&subscriber, SIGNAL(contentsChanged()), &loop, SLOT(quit()));
+            QTimer::singleShot(1000, &loop, SLOT(quit()));
+            loop.exec();
+        }
+
+        ++iterations;
+
+        QVariant value = subscriber.value();
+        if (value.isValid() && value.toUInt() > 1000)
+            break;
+    }
+}
+
+void tst_QValueSpaceSubscriber::threads_data()
+{
+    QTest::addColumn<unsigned int>("threads");
+    QTest::addColumn<bool>("synchronised");
+    QTest::addColumn<QUuid>("layerId");
+
+    QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+    int foundLayers = 0;
+    for (int i = 0; i < layers.count(); ++i) {
+        QAbstractValueSpaceLayer *layer = layers.at(i);
+
+        //GConfLayer can't provide thread-safety because it eventually depends on
+        //DBus which isn't fully thread-safe
+        if (layer->id() == QVALUESPACE_GCONF_LAYER) {
+            continue;
+        }
+
+        const QUuid id = layer->id();
+
+        QTest::newRow("1 thread") << uint(1) << true << id;
+        QTest::newRow("2 threads") << uint(2) << true << id;
+#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+        QTest::newRow("10 threads") << uint(10) << true << id;
+#else
+        QTest::newRow("100 threads") << uint(100) << true << id;
+#endif
+        QTest::newRow("1 thread, unsynchronised") << uint(1) << false << id;
+        QTest::newRow("2 threads, unsynchronised") << uint(2) << false << id;
+#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+        QTest::newRow("10 threads") << uint(10) << false  << id;
+#else
+        QTest::newRow("100 threads, unsynchronised") << uint(100) << false << id;
+#endif
+        foundLayers++;
+    }
+
+    if (foundLayers == 0)
+        QSKIP("No layers providing thread-safety found", SkipAll);
+}
+
+void tst_QValueSpaceSubscriber::threads()
+{
+    QFETCH(unsigned int, threads);
+    QFETCH(bool, synchronised);
+    QFETCH(QUuid, layerId);
+
+    QEventLoop writeLoop;
+    WriteThread *writeThread = new WriteThread(layerId);
+    connect(writeThread, SIGNAL(finished()), &writeLoop, SLOT(quit()));
+    writeThread->start();
+
+    QValueSpaceSubscriber masterSubscriber(layerId, "/threads/value");
+
+    QVector<ReadThread *> readThreads(threads);
+
+    for (unsigned int i = 0; i < threads; ++i) {
+        readThreads[i] = new ReadThread(&masterSubscriber, synchronised, layerId);
+        readThreads[i]->start();
+    }
+
+    for (unsigned int i = 0; i < threads; ++i) {
+        QEventLoop loop;
+        connect(readThreads[i], SIGNAL(finished()), &loop, SLOT(quit()));
+        if (!readThreads[i]->isFinished())
+            loop.exec();
+
+        delete readThreads[i];
+    }
+
+    writeThread->setDone();
+
+    if (!writeThread->isFinished())
+        writeLoop.exec();
+
+    delete writeThread;
+#ifdef Q_OS_SYMBIAN
+    QValueSpacePublisher resetPublisher(layerId, "/threads");
+    resetPublisher.resetValue("value");
+#endif
+
+}
+
+#include "tst_qvaluespacesubscribershared.moc"