diff -r 000000000000 -r 876b1a06bc25 tests/auto/qvaluespacesubscriber/tst_qvaluespacesubscribershared.cpp --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef Q_OS_WIN +#define _WIN32_WINNT 0x0500 +#include +#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"); + qRegisterMetaType("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 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("layer"); + + QTest::addColumn< QVariant >("data"); + QTest::addColumn< QString >("typeString"); + QTest::addColumn< int >("typeIdent"); + + QList 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(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("options"); + QTest::addColumn("connected"); + + QList 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("layer"); + + QTest::addColumn("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 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("layer"); + + QList 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("layer"); + + QList 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("layer"); + + bool skip = true; + + QList 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("layer"); + + QTest::addColumn("publisherPath"); + QTest::addColumn("attribute"); + + bool foundSupported = false; + + QList 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)), + ¬ificationListener, SIGNAL(interestChanged(QString,bool))); + + QSignalSpy notificationSpy(¬ificationListener, + SIGNAL(interestChanged(QString,bool))); + + const QString subscriberPath = publisherPath + attribute; + + QValueSpaceSubscriber *subscriber = new QValueSpaceSubscriber(layer->id(), subscriberPath); + + QTRY_COMPARE(notificationSpy.count(), 1); + + QList 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("layer"); + + bool foundSupported = false; + + QList 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("threads"); + QTest::addColumn("synchronised"); + QTest::addColumn("layerId"); + + QList 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 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"