/****************************************************************************
**
** 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::testInvalidPath_data()
{
QTest::addColumn<QString>("subscriber_path");
QTest::newRow("One char missing") << "/hom";
QTest::newRow("Two chars missing") << "/ho";
QTest::newRow("One char missing in subdir") << "/home/use";
QTest::newRow("Two chars missing in subdir") << "/home/us";
}
void tst_QValueSpaceSubscriber::testInvalidPath()
{
QFETCH(QString, subscriber_path);
QValueSpaceSubscriber subscriber(subscriber_path);
QVERIFY(subscriber.subPaths().isEmpty());
}
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)),
¬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<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"