--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qvaluespacepublisher/tst_qvaluespacepublisher.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,723 @@
+/****************************************************************************
+**
+** 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 <qvaluespacesubscriber.h>
+#include <qvaluespacemanager_p.h>
+#include <qvaluespacepublisher.h>
+
+#include <QThread>
+#include <QVector>
+
+#include <QTest>
+#include <QDebug>
+#include <QSignalSpy>
+#include <QFile>
+
+#ifdef Q_OS_WIN
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#endif
+
+#include <QProcess>
+
+#define QTRY_COMPARE(a,e) \
+ for (int _i = 0; _i < 5000; _i += 100) { \
+ if ((a) == (e)) break; \
+ QTest::qWait(100); \
+ } \
+ QCOMPARE(a, e)
+
+#define QTRY_VERIFY(a) \
+ for (int _i = 0; _i < 5000; _i += 100) { \
+ if (a) break; \
+ QTest::qWait(100); \
+ } \
+ QVERIFY(a)
+
+QTM_USE_NAMESPACE
+class ChangeListener : public QObject
+{
+ Q_OBJECT
+Q_SIGNALS:
+ void interestChanged(const QString&, bool);
+};
+
+class tst_QValueSpacePublisher: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testConstructor_data();
+ void testConstructor();
+ void testFilterConstructor_data();
+ void testFilterConstructor();
+ void testBaseConstructor();
+
+ void testSetValue_data();
+ void testSetValue();
+
+ void testSignals_data();
+ void testSignals();
+
+ void valuePermanence_data();
+ void valuePermanence();
+
+ void threads_data();
+ void threads();
+
+private:
+ int variantMetaTypeId;
+};
+
+Q_DECLARE_METATYPE(QAbstractValueSpaceLayer *)
+Q_DECLARE_METATYPE(QUuid)
+Q_DECLARE_METATYPE(QVariant)
+Q_DECLARE_METATYPE(QValueSpace::LayerOptions)
+
+void tst_QValueSpacePublisher::initTestCase()
+{
+ variantMetaTypeId = qRegisterMetaType<QVariant>("QVariant");
+
+#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
+
+#ifdef Q_OS_UNIX
+ QFile::remove("/tmp/qt-0/valuespace_shmlayer");
+#endif
+
+ QValueSpace::initValueSpaceServer();
+
+ if (QValueSpace::availableLayers().contains(QVALUESPACE_GCONF_LAYER)) {
+ QCOMPARE(QProcess::execute("gconftool-2 -u /value"), 0);
+ QCOMPARE(QProcess::execute("gconftool-2 -u /testConstructor/value"), 0);
+ QCOMPARE(QProcess::execute("gconftool-2 -u /testConstructor/subpath/value"), 0);
+ }
+
+}
+
+void tst_QValueSpacePublisher::cleanupTestCase()
+{
+}
+
+#define ADD(layer, id, path, canonical, valid) do {\
+ const QString layerName(layer ? layer->name() : 0); \
+ QTest::newRow((layerName + ' ' + path + " const QString &").toLocal8Bit().constData()) \
+ << layer << id << path << canonical << valid; \
+} while (false)
+
+void tst_QValueSpacePublisher::testConstructor_data()
+{
+ QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+ QTest::addColumn<QUuid>("uuid");
+
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QString>("canonical");
+ QTest::addColumn<bool>("connected");
+
+ QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+ // add all known layers
+ for (int i = 0; i < layers.count(); ++i) {
+ QAbstractValueSpaceLayer *layer = layers.at(i);
+
+ ADD(layer, layer->id(), QString(""), QString("/"), true);
+ ADD(layer, layer->id(), QString("/"), QString("/"), true);
+ ADD(layer, layer->id(), QString("//"), QString("/"), true);
+ ADD(layer, layer->id(), QString("/testConstructor"), QString("/testConstructor"), true);
+ ADD(layer, layer->id(), QString("/testConstructor/"), QString("/testConstructor"), true);
+ ADD(layer, layer->id(), QString("testConstructor"), QString("/testConstructor"), true);
+ ADD(layer, layer->id(), QString("/testConstructor/subpath"),\
+ QString("/testConstructor/subpath"), true);
+ }
+
+ // unknown uuid
+ ADD(reinterpret_cast<QAbstractValueSpaceLayer *>(0),
+ QUuid("{9fa51477-7730-48e0-aee1-3eeb5f0c0c5b}"), QString(), QString("/"), false);
+}
+
+#undef ADD
+
+void tst_QValueSpacePublisher::testConstructor()
+{
+ QFETCH(QAbstractValueSpaceLayer *, layer);
+ QFETCH(QUuid, uuid);
+
+ QFETCH(QString, path);
+ QFETCH(QString, canonical);
+ QFETCH(bool, connected);
+
+ QValueSpacePublisher *publisher = new QValueSpacePublisher(uuid, path);
+
+ QCOMPARE(publisher->path(), canonical);
+ QCOMPARE(publisher->isConnected(), connected);
+
+ if (layer) {
+ QAbstractValueSpaceLayer::Handle handle =
+ layer->item(QAbstractValueSpaceLayer::InvalidHandle, canonical.toUtf8());
+
+ QVariant data;
+ QVERIFY(!layer->value(handle, "/value", &data));
+
+ layer->removeHandle(handle);
+ }
+
+ publisher->setValue(QString("value"), 100);
+ publisher->sync();
+
+ if (layer) {
+ QAbstractValueSpaceLayer::Handle handle =
+ layer->item(QAbstractValueSpaceLayer::InvalidHandle, canonical.toUtf8());
+
+ QVariant data;
+ QVERIFY(layer->value(handle, "/value", &data));
+ QCOMPARE(data.toInt(), 100);
+
+ layer->removeHandle(handle);
+ }
+
+ publisher->resetValue(QString("value"));
+ publisher->sync();
+
+ if (layer) {
+ QAbstractValueSpaceLayer::Handle handle =
+ layer->item(QAbstractValueSpaceLayer::InvalidHandle, canonical.toUtf8());
+
+ QVariant data;
+ QVERIFY(!layer->value(handle, "/value", &data));
+
+ layer->removeHandle(handle);
+ }
+
+ delete publisher;
+
+ if (layer && layer->layerOptions() & QValueSpace::PermanentLayer) {
+ QValueSpacePublisher root(uuid, "/");
+ while (!canonical.isEmpty()) {
+ root.resetValue(canonical.mid(1));
+ canonical.truncate(canonical.lastIndexOf('/'));
+ }
+ root.sync();
+ }
+}
+
+#define ADD(opt, valid) do {\
+ QTest::newRow(QString::number(opt).append(" const QString &").toLocal8Bit().constData()) \
+ << (QValueSpace::UnspecifiedLayer | opt) << valid; \
+} while (false)
+
+void tst_QValueSpacePublisher::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::PermanentLayer | QValueSpace::TransientLayer,
+ false);
+ ADD(QValueSpace::WritableLayer | QValueSpace::ReadOnlyLayer,
+ false);
+}
+
+void tst_QValueSpacePublisher::testFilterConstructor()
+{
+ QFETCH(QValueSpace::LayerOptions, options);
+ QFETCH(bool, connected);
+
+ QValueSpacePublisher *publisher = new QValueSpacePublisher(options, QString("/"));
+
+ QCOMPARE(publisher->isConnected(), connected);
+}
+
+void tst_QValueSpacePublisher::testBaseConstructor()
+{
+ {
+ QValueSpacePublisher publisher("/");
+ QVERIFY(publisher.isConnected());
+ }
+
+ {
+ QValueSpacePublisher publisher(QString("/"));
+ QVERIFY(publisher.isConnected());
+ }
+}
+
+void tst_QValueSpacePublisher::testSetValue_data()
+{
+ QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+ QTest::addColumn<QString>("value");
+
+ QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+ for (int i = 0; i < layers.count(); ++i) {
+ QAbstractValueSpaceLayer *layer = layers.at(i);
+
+ QTest::newRow("empty") << layer << QString::fromLatin1("/");
+ }
+}
+
+void tst_QValueSpacePublisher::testSetValue()
+{
+ QFETCH(QAbstractValueSpaceLayer *, layer);
+ QFETCH(QString, value);
+
+ QValueSpaceSubscriber subscriber(layer->id(), QLatin1String("/testSetValue"));
+ QVERIFY(subscriber.subPaths().isEmpty());
+
+ QValueSpacePublisher publisher(layer->id(), QLatin1String("/testSetValue"));
+
+ publisher.setValue(QLatin1String(""), QLatin1String("default data"));
+ publisher.sync();
+ QVERIFY(subscriber.subPaths().isEmpty());
+ QCOMPARE(subscriber.value(QLatin1String("")).toString(), QLatin1String("default data"));
+
+ publisher.setValue(QLatin1String("key"), QLatin1String("key data"));
+ publisher.sync();
+ QCOMPARE(subscriber.subPaths().count(), 1);
+ QCOMPARE(subscriber.subPaths().first(), QLatin1String("key"));
+ QCOMPARE(subscriber.value(QLatin1String("key")).toString(), QLatin1String("key data"));
+
+ publisher.resetValue(QLatin1String("key"));
+ publisher.resetValue(QLatin1String(""));
+ publisher.sync();
+
+ QVERIFY(!subscriber.value(QLatin1String("")).isValid());
+ QVERIFY(!subscriber.value(QLatin1String("key")).isValid());
+}
+
+
+void tst_QValueSpacePublisher::testSignals_data()
+{
+ QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+ QTest::addColumn<QString>("publisherPath");
+ QTest::addColumn<QString>("subscriberPath");
+ QTest::addColumn<QString>("attribute");
+
+ QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+ bool foundSupported = false;
+
+ for (int i = 0; i < layers.count(); ++i) {
+ QAbstractValueSpaceLayer *layer = layers.at(i);
+
+ if (!layer->supportsInterestNotification())
+ continue;
+
+ foundSupported = true;
+
+ QTest::newRow("/ /")
+ << layer
+ << QString("/")
+ << QString("/")
+ << QString();
+
+ QTest::newRow("/ /testSignals")
+ << layer
+ << QString("/")
+ << QString("/testSignals")
+ << QString("testSignals");
+
+ QTest::newRow("/testSignals /testSignals")
+ << layer
+ << QString("/testSignals")
+ << QString("/testSignals")
+ << QString();
+ }
+
+ if (!foundSupported)
+ QSKIP("No layer supporting interest notifications found.", SkipAll);
+}
+
+void tst_QValueSpacePublisher::testSignals()
+{
+ QFETCH(QAbstractValueSpaceLayer *, layer);
+
+ QFETCH(QString, publisherPath);
+ QFETCH(QString, subscriberPath);
+ QFETCH(QString, attribute);
+
+ QValueSpacePublisher *publisher = new QValueSpacePublisher(layer->id(), publisherPath);
+
+ ChangeListener listener;
+ connect(publisher, SIGNAL(interestChanged(QString,bool)),
+ &listener, SIGNAL(interestChanged(QString,bool)));
+
+ QSignalSpy interestChangedSpy(&listener, SIGNAL(interestChanged(QString,bool)));
+
+ QValueSpaceSubscriber *subscriber = new QValueSpaceSubscriber(layer->id(), subscriberPath);
+
+ QTRY_COMPARE(interestChangedSpy.count(), 1);
+
+ QList<QVariant> arguments = interestChangedSpy.takeFirst();
+ QCOMPARE(arguments.count(), 2);
+ QCOMPARE(arguments.at(0).type(), QVariant::String);
+ QCOMPARE(arguments.at(0).toString(), attribute);
+ QCOMPARE(arguments.at(1).type(), QVariant::Bool);
+ QVERIFY(arguments.at(1).toBool());
+
+ delete subscriber;
+
+ QTRY_COMPARE(interestChangedSpy.count(), 1);
+
+ arguments = interestChangedSpy.takeFirst();
+ QCOMPARE(arguments.count(), 2);
+ QCOMPARE(arguments.at(0).type(), QVariant::String);
+ QCOMPARE(arguments.at(0).toString(), attribute);
+ QCOMPARE(arguments.at(1).type(), QVariant::Bool);
+ QVERIFY(!arguments.at(1).toBool());
+
+ delete publisher;
+}
+
+void tst_QValueSpacePublisher::valuePermanence_data()
+{
+ QTest::addColumn<QAbstractValueSpaceLayer *>("layer");
+
+ QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+ // add all known layers
+ for (int i = 0; i < layers.count(); ++i) {
+ QAbstractValueSpaceLayer *layer = layers.at(i);
+
+ QTest::newRow(layer->name().toLocal8Bit().constData()) << layer;
+ }
+}
+
+void tst_QValueSpacePublisher::valuePermanence()
+{
+ QFETCH(QAbstractValueSpaceLayer *, layer);
+
+ QValueSpacePublisher *publisher = new QValueSpacePublisher(layer->id(), "/valuePermanence");
+
+ publisher->setValue("value", 10);
+
+ QValueSpaceSubscriber subscriber("/valuePermanence");
+ QCOMPARE(subscriber.value("value", 0).toInt(), 10);
+
+ delete publisher;
+
+ if (layer->layerOptions() & QValueSpace::PermanentLayer) {
+ // Permanent layer, check that value is still available after publisher is deleted.
+ QCOMPARE(subscriber.value("value", 0).toInt(), 10);
+
+ publisher = new QValueSpacePublisher(layer->id(), "/valuePermanence");
+
+ publisher->resetValue("value");
+
+ QCOMPARE(subscriber.value("value", 0).toInt(), 0);
+
+ publisher->resetValue(QString());
+
+ delete publisher;
+ } else {
+ // Non-permanent layer, check that value is not available after publisher is deleted.
+ QCOMPARE(subscriber.value("value", 0).toInt(), 0);
+ }
+}
+
+class WriteThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ WriteThread(const QString &path, const QUuid &uuid, unsigned int count);
+ ~WriteThread();
+
+ void runSequential() { run(); }
+
+protected:
+ void run();
+
+private:
+ QString path;
+ unsigned int count;
+ QValueSpacePublisher *publisher;
+};
+
+WriteThread::WriteThread(const QString &path, const QUuid &uuid, unsigned int count)
+: path(path), count(count)
+{
+ publisher = new QValueSpacePublisher(uuid, path, this);
+}
+
+WriteThread::~WriteThread()
+{
+#ifdef Q_OS_SYMBIAN
+ //Cleanup published values since the SymbianSettingLayer is permanent
+ const QString key("key%1");
+ for (unsigned int i = 0; i < count; ++i)
+ publisher->resetValue(key.arg(i));
+
+ publisher->sync();
+#endif
+}
+
+void WriteThread::run()
+{
+ const QString key("key%1");
+ const QString value("value%1");
+
+ for (unsigned int i = 0; i < count; ++i)
+ publisher->setValue(key.arg(i), value.arg(i));
+
+ publisher->sync();
+}
+
+void tst_QValueSpacePublisher::threads_data()
+{
+ QTest::addColumn<QUuid>("uuid");
+
+ QTest::addColumn<unsigned int>("threads");
+ QTest::addColumn<unsigned int>("count");
+ QTest::addColumn<bool>("sequential");
+
+ QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers();
+
+ int foundLayers = 0;
+ for (int i = 0; i < layers.count(); ++i) {
+ QAbstractValueSpaceLayer *layer = layers.at(i);
+
+ if (layer->id() == QVALUESPACE_NONVOLATILEREGISTRY_LAYER)
+ continue;
+
+ //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;
+
+#ifdef Q_OS_WINCE
+ // Limit number of items on Windows CE to prevent out of disk space errors.
+ if (layer->id() == QVALUESPACE_VOLATILEREGISTRY_LAYER) {
+ QTest::newRow("1 thread, 10 items, sequential")
+ << layer->id() << uint(1) << uint(10) << true;
+ QTest::newRow("1 thread, 3000 items, sequential")
+ << layer->id() << uint(1) << uint(3000) << true;
+ QTest::newRow("2 threads, 10 items, sequential")
+ << layer->id() << uint(2) << uint(10) << true;
+ QTest::newRow("2 threads, 1500 items, sequential")
+ << layer->id() << uint(2) << uint(1500) << true;
+ QTest::newRow("4 threads, 750 items, sequential")
+ << layer->id() << uint(4) << uint(750) << true;
+ QTest::newRow("10 threads, 300 items, sequential")
+ << layer->id() << uint(10) << uint(300) << true;
+
+ QTest::newRow("1 thread, 10 items")
+ << layer->id() << uint(1) << uint(10) << false;
+ QTest::newRow("1 thread, 3000 items")
+ << layer->id() << uint(1) << uint(3000) << false;
+ QTest::newRow("2 threads, 10 items")
+ << layer->id() << uint(2) << uint(10) << false;
+ QTest::newRow("2 threads, 1500 items")
+ << layer->id() << uint(2) << uint(1500) << false;
+ QTest::newRow("4 threads, 750 items")
+ << layer->id() << uint(4) << uint(750) << false;
+ QTest::newRow("10 threads, 300 items")
+ << layer->id() << uint(10) << uint(300) << false;
+ } else
+#endif
+
+ // The Shared Memory layer can hold a maximum of 8191 nodes.
+ if (layer->id() == QVALUESPACE_SHAREDMEMORY_LAYER) {
+ QTest::newRow("1 thread, 10 items, sequential")
+ << layer->id() << uint(1) << uint(10) << true;
+ QTest::newRow("1 thread, 8000 items, sequential")
+ << layer->id() << uint(1) << uint(8000) << true;
+ QTest::newRow("2 threads, 10 items, sequential")
+ << layer->id() << uint(2) << uint(10) << true;
+ QTest::newRow("2 threads, 4000 items, sequential")
+ << layer->id() << uint(2) << uint(4000) << true;
+ QTest::newRow("4 threads, 2000 items, sequential")
+ << layer->id() << uint(4) << uint(2000) << true;
+ QTest::newRow("10 threads, 800 items, sequential")
+ << layer->id() << uint(10) << uint(800) << true;
+ QTest::newRow("100 threads, 80 items, sequential")
+ << layer->id() << uint(100) << uint(80) << true;
+
+ QTest::newRow("1 thread, 10 items")
+ << layer->id() << uint(1) << uint(10) << false;
+ QTest::newRow("1 thread, 8000 items")
+ << layer->id() << uint(1) << uint(8000) << false;
+ QTest::newRow("2 threads, 10 items")
+ << layer->id() << uint(2) << uint(10) << false;
+ QTest::newRow("2 threads, 4000 items")
+ << layer->id() << uint(2) << uint(4000) << false;
+ QTest::newRow("4 threads, 2000 items")
+ << layer->id() << uint(4) << uint(2000) << false;
+ QTest::newRow("10 threads, 800 items")
+ << layer->id() << uint(10) << uint(800) << false;
+ QTest::newRow("100 threads, 80 items")
+ << layer->id() << uint(100) << uint(80) << false;
+ } else if (layer->id() == QVALUESPACE_SYMBIAN_SETTINGS_LAYER) {
+ QTest::newRow("1 thread, 10 items, sequential")
+ << layer->id() << uint(1) << uint(10) << true;
+ QTest::newRow("2 threads, 10 items, sequential")
+ << layer->id() << uint(2) << uint(10) << true;
+ } else {
+ // Assume no limits on all other layers.
+ QTest::newRow("1 thread, 10 items, sequential")
+ << layer->id() << uint(1) << uint(10) << true;
+ QTest::newRow("1 thread, 10000 items, sequential")
+ << layer->id() << uint(1) << uint(10000) << true;
+ QTest::newRow("2 threads, 10 items, sequential")
+ << layer->id() << uint(2) << uint(10) << true;
+ QTest::newRow("2 threads, 5000 items, sequential")
+ << layer->id() << uint(2) << uint(5000) << true;
+ QTest::newRow("100 threads, 100 items, sequential")
+ << layer->id() << uint(100) << uint(100) << true;
+
+ QTest::newRow("1 thread, 10 items")
+ << layer->id() << uint(1) << uint(10) << false;
+ QTest::newRow("1 thread, 10000 items")
+ << layer->id() << uint(1) << uint(10000) << false;
+ QTest::newRow("2 threads, 10 items")
+ << layer->id() << uint(2) << uint(10) << false;
+ QTest::newRow("2 threads, 5000 items")
+ << layer->id() << uint(2) << uint(5000) << false;
+ QTest::newRow("100 threads, 100 items")
+ << layer->id() << uint(100) << uint(100) << false;
+ }
+ foundLayers++;
+ }
+
+ if (foundLayers == 0)
+ QSKIP("No layers providing thread-safety found", SkipAll);
+}
+
+void tst_QValueSpacePublisher::threads()
+{
+ QFETCH(QUuid, uuid);
+ QFETCH(unsigned int, threads);
+ QFETCH(unsigned int, count);
+ QFETCH(bool, sequential);
+
+ if (QValueSpace::availableLayers().contains(QVALUESPACE_GCONF_LAYER)) {
+ QCOMPARE(QProcess::execute("gconftool-2 --recursive-unset /threads"), 0);
+ }
+ QStringList expectedPaths;
+ for (unsigned int i = 0; i < threads; ++i)
+ expectedPaths.append(QString("thread%1").arg(i));
+
+ QHash<QString, QString> expectedValues;
+ for (unsigned int i = 0; i < count; ++i)
+ expectedValues.insert(QString("key%1").arg(i), QString("value%1").arg(i));
+
+ QValueSpaceSubscriber *subscriber = new QValueSpaceSubscriber(uuid, "/threads");
+
+ QVERIFY(subscriber->subPaths().isEmpty());
+
+ QVector<WriteThread *> writeThreads(threads);
+
+ // Create and start writer threads.
+ for (unsigned int i = 0; i < threads; ++i) {
+ writeThreads[i] =
+ new WriteThread(QString("/threads/%1").arg(expectedPaths.at(i)), uuid, count);
+
+ if (sequential)
+ writeThreads[i]->runSequential();
+ else
+ writeThreads[i]->start();
+ }
+
+ if (!sequential) {
+ // Wait for writer threads to finish.
+ for (unsigned int i = 0; i < threads; ++i)
+ writeThreads[i]->wait();
+ }
+
+ qDebug() << "Published" << count << "items in" << threads
+ << "theads, totaling" << (threads * count);
+
+ // Verify Value Space
+ QStringList subPaths = subscriber->subPaths();
+ if (subPaths.toSet() != expectedPaths.toSet()) {
+ qDebug() << "Expected Paths:" << expectedPaths;
+ qDebug() << "Actual Paths:" << subPaths;
+ }
+ QVERIFY(subPaths.toSet() == expectedPaths.toSet());
+
+ while (!subPaths.isEmpty()) {
+ QValueSpaceSubscriber threadItem;
+ threadItem.setPath(subscriber);
+ threadItem.cd(subPaths.takeFirst());
+
+ QStringList keys = threadItem.subPaths();
+
+ if (keys.toSet() != expectedValues.keys().toSet()) {
+ qDebug() << "Expected value keys:" << expectedValues.keys();
+ qDebug() << "Actual value keys:" << keys;
+ }
+ QVERIFY(keys.toSet() == expectedValues.keys().toSet());
+
+ while (!keys.isEmpty()) {
+ const QString key = keys.takeFirst();
+ QCOMPARE(threadItem.value(key).toString(), expectedValues.value(key));
+ }
+ }
+
+ delete subscriber;
+
+ // Delete writer threads.
+ for (unsigned int i = 0; i < threads; ++i)
+ delete writeThreads[i];
+}
+
+QTEST_MAIN(tst_QValueSpacePublisher)
+#include "tst_qvaluespacepublisher.moc"