tests/auto/servicedatabase/tst_servicedatabase.cpp
changeset 0 876b1a06bc25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/servicedatabase/tst_servicedatabase.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,1579 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtCore>
+#define private public
+#include <qserviceinterfacedescriptor.h>
+#include <qserviceinterfacedescriptor_p.h>
+#include <servicedatabase_p.h>
+#include <qservicefilter.h>
+
+#define RESOLVERDATABASE "services.db"
+
+#if defined(Q_OS_SYMBIAN)
+# define TESTDATA_DIR "."
+#endif
+
+QTM_USE_NAMESPACE
+class ServiceDatabaseUnitTest: public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void initTestCase();
+    void testRegistration();
+    void getInterfaces();
+    void searchByInterfaceName();
+    void searchByInterfaceAndService();
+    void searchByCustomAttribute();
+    void searchByCapability();
+    void attributes();
+    void getServiceNames();
+    void defaultExternalIfaceIDs();
+    void interfaceDefault();
+    void setInterfaceDefault();
+    void unregister();
+    void securityTokens();
+    void cleanupTestCase();
+
+private:
+
+     bool compareDescriptor(QServiceInterfaceDescriptor interface,
+                            QString interfaceName,
+                            QString serviceName,
+                            int majorVersion,
+                            int minorVersion);
+
+    bool compareDescriptor(QServiceInterfaceDescriptor interface,
+                            QString interfaceName,
+                            QString serviceName,
+                            int majorVersion,
+                            int minorVersion,
+                            QStringList capabilities,
+                            const QHash<QString,QString> customProps,
+                            QString filePath="",
+                            QString serviceDescription="",
+                            QString interfaceDescription=""); 
+
+    QStringList getInterfaceIDs(const QString &serviceName);
+    QStringList getServiceIDs(const QString &serviceName);
+    bool existsInInterfacePropertyTable(const QString &interfaceID);
+    bool existsInServicePropertyTable(const QString &serviceID);
+    bool existsInDefaultsTable(const QString &interfaceID);
+    bool registerService(const ServiceMetaDataResults &service, const QString &securityToken = QString());
+    bool unregisterService(const QString &serviceName, const QString &securityToken = QString());
+
+    ServiceMetaData* parser;
+    ServiceDatabase database;
+};
+
+void ServiceDatabaseUnitTest::initTestCase()
+{
+#if defined(Q_OS_SYMBIAN)
+    database.m_databasePath = QDir::toNativeSeparators(QDir::currentPath().append("/services.db"));
+#endif
+    database.close();
+    QFile::remove(database.databasePath());
+}
+
+static const QString securityTokenOwner("SecurityTokenOwner");
+static const QString securityTokenStranger("SecurityTokenStranger");
+
+void ServiceDatabaseUnitTest::testRegistration()
+{
+    QDir testdir = QDir(TESTDATA_DIR "/testdata" );
+
+    ServiceMetaData parser(testdir.absoluteFilePath("ServiceAcme.xml"));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(!registerService(parser.parseResults()));
+
+    QCOMPARE(database.lastError().code(), DBError::DatabaseNotOpen);
+#if defined(Q_OS_SYMBIAN)
+    database.m_databasePath = QDir::toNativeSeparators(QDir::currentPath().append("/services.db"));
+#endif
+    QVERIFY(database.open());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceOmni.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceLuthorCorp.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceWayneEnt.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServicePrimatech.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceCyberdyne.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceSkynet.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceAutobot.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    //try to register an already registered service
+    QVERIFY(!registerService(parser.parseResults()));
+    QCOMPARE(database.lastError().code(), DBError::LocationAlreadyRegistered);
+
+    //try to register a service with a dll that provides interface implementations
+    //that are already provided by a currently registered service
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServicePrimatech2error.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(!registerService(parser.parseResults()));
+    QCOMPARE(database.lastError().code(), DBError::IfaceImplAlreadyRegistered);
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceYamagatoError.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(!registerService(parser.parseResults()));
+    QCOMPARE(database.lastError().code(), DBError::LocationAlreadyRegistered);
+
+    //make sure errors above are corectly rolled back by
+    //registering a valid service
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceDecepticon.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+
+    QStringList xmlFiles;
+    xmlFiles << "ServiceDharma_Swan.xml"
+             << "ServiceDharma_Pearl.xml"
+             << "ServiceDharma_Flame.xml";
+
+    foreach(const QString &file, xmlFiles) {
+        //Check that when we register a service we reseed
+        //the random number generator so we don't have
+        //any service id clashes.  To verify this is
+        //happening we set the seed here to 1.
+        qsrand(1);
+        parser.setDevice(new QFile(testdir.absoluteFilePath(file)));
+        QVERIFY(parser.extractMetadata());
+        QVERIFY(registerService(parser.parseResults()));
+    }
+
+    QVERIFY(database.close());
+    QVERIFY(database.close());  //verify we can close multiple times
+                                //without side effects
+}
+
+void ServiceDatabaseUnitTest::getInterfaces()
+{
+    QServiceFilter filter;
+    QList<QServiceInterfaceDescriptor> interfaces;
+
+    filter.setServiceName("acme");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(database.lastError().code(), DBError::DatabaseNotOpen);
+    QVERIFY(database.close());  //check we can close the database even if it
+                                //has not been opened
+
+    QVERIFY(database.open());
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(interfaces.count(), 5);
+
+    QHash<QString,QString> customs;
+    QStringList capabilities;
+    compareDescriptor(interfaces[0], "com.acme.service.downloader", "acme", 1, 0, capabilities, customs, "C:/TestData/testservice.dll",
+            "Acme services", "Interface that provides download support");
+
+    QServiceInterfaceDescriptor interface;
+    customs["coordinate"] = QString("global");
+    QVERIFY(compareDescriptor(interfaces[1], "com.acme.service.location", "acme", 1,0, capabilities,customs,  "C:/TestData/testservice.dll"));
+
+    customs.clear();
+    customs["cpu"]=QString("");
+    capabilities.append(QString("ReadUserData"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.acme.device.sysinfo", "acme", 2, 3, capabilities, customs, "C:/TestData/testservice.dll"));
+
+    customs.clear();
+    customs["smtp"]=QString("smtp.mail.com");
+    capabilities.clear();
+    capabilities.append("ReadUserData");
+    capabilities.append("WriteUserData");
+    QVERIFY(compareDescriptor(interfaces[3], "com.acme.device.sendMessage", "acme", 3, 0, capabilities,customs,  "C:/TestData/testservice.dll"));
+
+    customs.clear();
+    customs["imap"]=QString("imap.mail.com");
+    customs["pop"]=QString("pop.mail.com");
+    capabilities.clear();
+    capabilities.append("ReadUserData");
+    capabilities.append("WriteUserData");
+    capabilities.append("ExecUserData");
+    QVERIFY(compareDescriptor(interfaces[4], "com.acme.device.receiveMessage", "acme", 1, 1, capabilities, customs, "C:/TestData/testservice.dll"));
+
+    //check that searching is case insensitive
+    filter.setServiceName("OmNi");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(interfaces.count(), 3);
+
+    customs.clear();
+    capabilities.clear();
+    capabilities << "SurroundingsDD";
+    QVERIFY(compareDescriptor(interfaces[0], "com.omni.device.Accelerometer", "OMNI", 1, 1, capabilities, customs, "C:/OmniInc/omniinc.dll",
+                "Omni mobile", "Interface that provides accelerometer readings(omni)"));
+
+    capabilities.clear();
+    QVERIFY(compareDescriptor(interfaces[1], "com.omni.device.Lights","OMNI", 9, 0, capabilities, customs, "C:/OmniInc/omniinc.dll"));
+
+    capabilities.clear();
+    capabilities << "MultimediaDD" << "NetworkServices" << "ReadUserData" << "WriteUserData";
+    QVERIFY(compareDescriptor(interfaces[2], "com.omni.service.Video", "OMNI", 1, 4, capabilities, customs, "C:/OmniInc/omniinc.dll"));
+
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::searchByInterfaceName()
+{
+    QServiceFilter filter;
+    QList<QServiceInterfaceDescriptor> interfaces;
+
+    QVERIFY(database.open());
+
+    QString iface = "com.omni.device.Accelerometer";
+    // == search via interface name only ==
+    filter.setInterface(iface);
+    interfaces = database.getInterfaces(filter);
+
+    QCOMPARE(interfaces.count(), 5);
+    QHash<QString,QString> customs;
+    QStringList capabilities;
+    capabilities << "SurroundingsDD";
+    QVERIFY(compareDescriptor(interfaces[0], iface, "OMNI", 1, 1, capabilities,customs,"C:/OmniInc/omniinc.dll",
+               "Omni mobile", "Interface that provides accelerometer readings(omni)"));
+    QVERIFY(compareDescriptor(interfaces[1], iface, "LuthorCorp", 1, 2, capabilities,customs,"C:/Metropolis/kryptonite.dll",
+           "", "Interface that provides accelerometer readings")); //service description is empty tag in xml
+    QVERIFY(compareDescriptor(interfaces[2], iface, "WayneEnt", 2, 0, capabilities,customs,"C:/Gotham/knight.dll",
+            "", "Interface that provides accelerometer readings"));//no service description tag in xml
+    QVERIFY(compareDescriptor(interfaces[3], iface, "Primatech", 1, 4, capabilities,customs,"C:/NewYork/kensei.dll",
+                "Primatech Cellular Services", "Interface that provides accelerometer readings"));
+    QVERIFY(compareDescriptor(interfaces[4], iface, "Primatech", 1, 2, capabilities,customs,"C:/NewYork/kensei.dll",
+                "Primatech Cellular Services", "Interface that provides accelerometer readings"));
+
+    //search with non-existent interface name
+    filter.setInterface("com.omni.device.FluxCapacitor");
+    interfaces = database.getInterfaces(filter);
+
+    QCOMPARE(interfaces.count(), 0);
+
+    // == search for an exact version match ==
+    filter.setInterface(iface, "1.4", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+
+    QCOMPARE(interfaces.count(), 1);
+    QVERIFY(compareDescriptor(interfaces[0], iface, "Primatech", 1, 4));
+
+    //try exact version match but with multiple expected instances returned.
+    filter.setInterface("com.oMNi.device.accelerometer", "1.2", QServiceFilter::ExactVersionMatch); //also test case insensitivity
+    interfaces = database.getInterfaces(filter);
+
+    QCOMPARE(interfaces.count(), 2);
+    QVERIFY(compareDescriptor(interfaces[0], iface, "LuthorCorp", 1, 2));
+    QVERIFY(compareDescriptor(interfaces[1], iface, "Primatech", 1, 2));
+
+    //try exact match for an interface that exists but a version that doesn't
+    filter.setInterface(iface,"1.3", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+
+    // == search for a minimum version match ==
+    filter.setInterface(iface,"1.2", QServiceFilter::MinimumVersionMatch);//use existent version
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+
+    QVERIFY(compareDescriptor(interfaces[0], iface, "LuthorCorp", 1, 2));
+    QVERIFY(compareDescriptor(interfaces[1], iface, "WayneEnt", 2, 0));
+    QVERIFY(compareDescriptor(interfaces[2], iface, "Primatech", 1, 4));
+    QVERIFY(compareDescriptor(interfaces[3], iface, "Primatech", 1, 2));
+
+    filter.setInterface(iface, "1.3", QServiceFilter::MinimumVersionMatch);//use non-existent version
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 2);
+    QVERIFY(compareDescriptor(interfaces[0], iface, "WayneEnt", 2, 0));
+    QVERIFY(compareDescriptor(interfaces[1], iface, "Primatech", 1, 4));
+
+    //try minimum version match that will find all available versions
+    filter.setInterface(iface,"1.0", QServiceFilter::MinimumVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 5);
+
+    //try minimum version match but no implementations are available
+    filter.setInterface(iface,"9.0", QServiceFilter::MinimumVersionMatch);//use non-existent version
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+
+    //try setting an invalid version (should default to interface name search)
+    filter.setInterface(iface,"-1.0", QServiceFilter::MinimumVersionMatch);//use non-existent version
+    interfaces =database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+
+    // == search for an interface spread over multiple plugins by a single service==
+    iface = "com.dharma.electro.discharge";
+    filter.setInterface(iface);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+    capabilities.clear();
+    QVERIFY(compareDescriptor(interfaces[0], iface, "DharmaInitiative", 4, 0, capabilities,customs, "C:/island/swan.dll"));
+    QVERIFY(compareDescriptor(interfaces[1], iface, "DharmaInitiative", 8, 0, capabilities, customs,"C:/island/pearl.dll"));
+    QVERIFY(compareDescriptor(interfaces[2], iface, "DharmaInitiative", 15, 0, capabilities, customs,"C:/island/flame.dll"));
+    QVERIFY(compareDescriptor(interfaces[3], iface, "DharmaInitiative", 16, 0, capabilities, customs,"C:/island/flame.dll"));
+
+    //try searching by minimum version for interface implementation spread over multiple plugins
+    filter.setInterface(iface, "5.0", QServiceFilter::MinimumVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 3);
+    QVERIFY(compareDescriptor(interfaces[0], iface, "DharmaInitiative", 8, 0, capabilities, customs,"C:/island/pearl.dll"));
+    QVERIFY(compareDescriptor(interfaces[1], iface, "DharmaInitiative", 15, 0, capabilities, customs,"C:/island/flame.dll"));
+    QVERIFY(compareDescriptor(interfaces[2], iface, "DharmaInitiative", 16, 0, capabilities, customs,"C:/island/flame.dll"));
+
+    //try searching for a single version of an interface implementation
+    filter.setInterface(iface, "8.0", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    QVERIFY(compareDescriptor(interfaces[0], iface, "DharmaInitiative", 8, 0, capabilities, customs,"C:/island/pearl.dll"));
+
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::searchByInterfaceAndService()
+{
+    QServiceFilter filter;
+    QList<QServiceInterfaceDescriptor> interfaces;
+
+    QVERIFY(database.open());
+
+    // == search using only interface and service name ==
+    filter.setServiceName("Omni");
+    filter.setInterface("com.omni.device.Lights");
+    interfaces = database.getInterfaces(filter);
+    QVERIFY(compareDescriptor(interfaces[0], "com.omni.device.Lights", "OMNI", 9, 0));
+
+    QCOMPARE(interfaces.count(), 1);
+
+    //try searching with service that implements the same interface
+    //more than once
+    filter.setServiceName("Primatech");
+    filter.setInterface("com.omni.device.Accelerometer");
+    interfaces = database.getInterfaces(filter);
+
+    QCOMPARE(interfaces.count(), 2);
+    QVERIFY(compareDescriptor(interfaces[0], "com.omni.device.Accelerometer", "Primatech", 1, 4));
+    QVERIFY(compareDescriptor(interfaces[1], "com.omni.device.Accelerometer", "Primatech", 1, 2));
+
+    //try and existing service but non-existent interface
+    filter.setServiceName("Primatech");
+    filter.setInterface("com.omni.device.FluxCapacitor");
+
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+
+    //try an non-existing service but an existing interface
+    filter.setServiceName("StarkInd");
+    filter.setInterface("com.omni.device.Accelerometer");
+
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+
+    // == search using interface and service name and exact version match ==
+    filter.setServiceName("Primatech");
+    filter.setInterface("com.omni.device.Accelerometer", "1.2", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+
+    QCOMPARE(interfaces.count(), 1);
+    QVERIFY(compareDescriptor(interfaces[0], "com.omni.device.Accelerometer", "Primatech", 1, 2));
+
+    //try an exact match for and non-existent interface version
+    filter.setInterface("com.omni.device.Accelerometer","1.3", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+
+    // == search using interface and service name and minimum version match ==
+    filter.setServiceName("Cyberdyne");
+    filter.setInterface("com.cyberdyne.terminator","1.6", QServiceFilter::MinimumVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+
+    QVERIFY(compareDescriptor(interfaces[0], "com.cyberdyne.terminator", "Cyberdyne", 2, 1));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cyberdyne.terminator", "Cyberdyne", 2, 0));
+    QVERIFY(compareDescriptor(interfaces[2], "com.cyberdyne.terminator", "Cyberdyne", 1, 7));
+    QVERIFY(compareDescriptor(interfaces[3], "com.cyberdyne.terminator", "Cyberdyne", 1, 6));
+
+    //try again with the same interface but a different service
+    filter.setServiceName("Skynet");
+    filter.setInterface("com.cyberdyne.terminator", "1.6", QServiceFilter::MinimumVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+
+    QVERIFY(compareDescriptor(interfaces[0], "com.cyberdyne.terminator", "skynet", 3, 6));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cyberdyne.terminator", "skynet", 2, 0));
+    QVERIFY(compareDescriptor(interfaces[2], "com.cyberdyne.terminator", "skynet", 1, 8));
+    QVERIFY(compareDescriptor(interfaces[3], "com.cyberdyne.terminator", "skynet", 1, 6));
+
+    //try with a non-existent interface version (but later versions exist)
+    filter.setServiceName("Skynet");
+    filter.setInterface("com.cyberdyne.terminator","1.9", QServiceFilter::MinimumVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cyberdyne.terminator", "skynet", 3, 6));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cyberdyne.terminator", "skynet", 2, 0));
+    QCOMPARE(interfaces.count(), 2);
+
+    // == using wildcard matching when searching, ie service and/or interface field is empty
+    filter.setServiceName("");
+    filter.setInterface("");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 36);
+
+    // == searches on a service which is made up of multiple plugins
+    // try searching for all interfaces offered by the service
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 5);
+
+    QHash<QString,QString> customs;
+    QStringList capabilities;
+    QVERIFY(compareDescriptor(interfaces[0], "com.dharma.electro.discharge", "DharmaInitiative", 4, 0, capabilities, customs, "C:/island/swan.dll",
+                "Department of Heuristics And Research on Material Applications(S)",
+                "Releases electromagnetic energy buildup every 108 minutes"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.dharma.electro.discharge", "DharmaInitiative", 8, 0, capabilities, customs, "C:/island/pearl.dll",
+                "Department of Heuristics And Research on Material Applications(P)",
+                "Releases electromagnetic energy buildup every 108 minutes"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.dharma.electro.discharge", "DharmaInitiative", 15, 0, capabilities, customs, "C:/island/flame.dll",
+                "Department of Heuristics And Research on Material Applications(F)",
+                "Releases electromagnetic energy buildup every 108 minutes"));
+    QVERIFY(compareDescriptor(interfaces[3], "com.dharma.radio", "DharmaInitiative", 8, 15, capabilities, customs, "C:/island/flame.dll",
+                "Department of Heuristics And Research on Material Applications(F)",
+                "Enables communication off island"));
+    QVERIFY(compareDescriptor(interfaces[4], "com.dharma.electro.discharge", "DharmaInitiative", 16, 0, capabilities, customs, "C:/island/flame.dll",
+                "Department of Heuristics And Research on Material Applications(F)",
+                "Releases electromagnetic energy buildup every 108 minutes"));
+
+    // try searching for all implementations of a specific interface offered by the service
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("com.dharma.electro.discharge");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+    QVERIFY(compareDescriptor(interfaces[0], "com.dharma.electro.discharge", "DharmaInitiative", 4, 0, capabilities, customs, "C:/island/swan.dll"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.dharma.electro.discharge", "DharmaInitiative", 8, 0, capabilities, customs, "C:/island/pearl.dll"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.dharma.electro.discharge", "DharmaInitiative", 15, 0, capabilities, customs, "C:/island/flame.dll"));
+    QVERIFY(compareDescriptor(interfaces[3], "com.dharma.electro.discharge", "DharmaInitiative", 16, 0, capabilities, customs, "C:/island/flame.dll"));
+
+    //try doing a minimum version search
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("com.dharma.electro.discharge", "7.9", QServiceFilter::MinimumVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 3);
+    QVERIFY(compareDescriptor(interfaces[0], "com.dharma.electro.discharge", "DharmaInitiative", 8, 0, capabilities, customs, "C:/island/pearl.dll"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.dharma.electro.discharge", "DharmaInitiative", 15, 0, capabilities, customs, "C:/island/flame.dll"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.dharma.electro.discharge", "DharmaInitiative", 16, 0, capabilities, customs, "C:/island/flame.dll"));
+
+    //try doing a exact version search
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("com.dharma.electro.discharge", "15.0", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    QVERIFY(compareDescriptor(interfaces[0], "com.dharma.electro.discharge", "DharmaInitiative", 15, 0, capabilities, customs, "C:/island/flame.dll"));
+
+    //trying setting invalid interface parameters, supply a version without an interface
+    filter.setInterface("", "3.0", QServiceFilter::MinimumVersionMatch); //this call should be ignored
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    QVERIFY(compareDescriptor(interfaces[0], "com.dharma.electro.discharge", "DharmaInitiative", 15, 0, capabilities, customs, "C:/island/flame.dll"));
+
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::searchByCapability()
+{
+    QServiceFilter filter;
+    QList<QServiceInterfaceDescriptor> interfaces;
+    QHash<QString,QString> customs;
+    QHash<QString,QString> customsW = customs;
+    customsW["weapon"] = "";
+
+    QVERIFY(database.open());
+
+    filter.setServiceName("Decepticon");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),4);
+
+    filter.setCapabilities(QServiceFilter::MatchLoadable, QStringList());
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),1);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 5, 3,
+                QStringList(), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+
+    QStringList caps;
+    caps << "hunt" << "spy";
+    filter.setCapabilities(QServiceFilter::MatchMinimum, caps);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),2);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 2, 0,
+                (QStringList()<<"hunt" << "spy" << "kill"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Decepticon", 2, 5,
+                (QStringList()<<"hunt" << "spy"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+
+    filter.setCapabilities(QServiceFilter::MatchLoadable, caps);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),3);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 1, 1,
+                QStringList()<<"hunt", customsW, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Decepticon", 5, 3,
+                QStringList() , customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.cybertron.transform", "Decepticon", 2, 5,
+                (QStringList()<<"hunt" << "spy"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    
+    caps.clear();
+    caps << "hunt";
+    filter.setCapabilities(QServiceFilter::MatchMinimum, caps);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),3);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 2, 0,
+                (QStringList()<<"hunt" << "spy" << "kill"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Decepticon", 1, 1,
+                QStringList()<<"hunt" , customsW, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.cybertron.transform", "Decepticon", 2, 5,
+                (QStringList()<<"hunt" << "spy"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+
+
+    filter.setCapabilities(QServiceFilter::MatchLoadable, caps);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),2);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 1, 1,
+                QStringList()<<"hunt", customsW, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Decepticon", 5, 3,
+                QStringList(), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+
+    caps.clear();
+    filter.setCapabilities(QServiceFilter::MatchMinimum, caps);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),4);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 2, 0,
+                (QStringList()<<"hunt" << "spy" << "kill"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Decepticon", 1, 1,
+                QStringList()<<"hunt", customsW, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[2], "com.cybertron.transform", "Decepticon", 5, 3,
+                QStringList(), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    QVERIFY(compareDescriptor(interfaces[3], "com.cybertron.transform", "Decepticon", 2, 5,
+                (QStringList()<<"hunt" << "spy"), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+    
+
+    filter.setCapabilities(QServiceFilter::MatchLoadable, caps);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),1);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Decepticon", 5, 3,
+                QStringList(), customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+
+    QServiceFilter emptyFilter;
+    emptyFilter.setCapabilities(QServiceFilter::MatchLoadable);
+    interfaces = database.getInterfaces(emptyFilter);
+    QCOMPARE(interfaces.count(), 14); //show all services which don't require any caps
+}
+
+void ServiceDatabaseUnitTest::searchByCustomAttribute()
+{
+    QServiceFilter filter;
+    QList<QServiceInterfaceDescriptor> interfaces;
+    QHash<QString,QString> customs;
+    QStringList capabilities;
+
+    QVERIFY(database.open());
+
+    filter.setServiceName("Autobot");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),5);
+
+    filter.setCustomAttribute("bot", "automatic");
+    QCOMPARE(filter.customAttribute("bot"), QString("automatic"));
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),3);
+
+    customs["bot"] = "automatic";
+    customs["extension"] = "multidrive";
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Autobot", 2, 7,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+
+    customs.clear();
+    customs["bot"] = "automatic";
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Autobot", 2, 5,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+
+    customs.clear();
+    customs["bot"] = "automatic";
+    customs["weapon"] = "";
+    QVERIFY(compareDescriptor(interfaces[2], "com.cybertron.transform", "Autobot", 1, 9,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+
+    filter.setCustomAttribute("extension","multidrive");
+    QCOMPARE(filter.customAttribute("extension"), QString("multidrive"));
+    QCOMPARE(filter.customAttribute("bot"), QString("automatic"));
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),1);
+    
+    customs.clear();
+    customs["bot"] = "automatic";
+    customs["extension"] = "multidrive";
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Autobot", 2, 7,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+
+    QServiceFilter manualFilter;
+    manualFilter.setCustomAttribute("bot", "manual");
+    interfaces = database.getInterfaces(manualFilter);
+    QCOMPARE(interfaces.count(),2);
+
+    customs.clear();
+    customs["bot"]="manual";
+    customs["weapon"]="laser";
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Autobot", 1, 0,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+    customs.clear();
+    customs["bot"] = "manual";
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Autobot", 2, 0,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+
+    QServiceFilter multidriveFilter;
+    multidriveFilter.setCustomAttribute("extension", "multidrive");
+    interfaces = database.getInterfaces(multidriveFilter);
+    QCOMPARE(interfaces.count(),1);
+
+    customs.clear();
+    customs["bot"] = "automatic";
+    customs["extension"] = "multidrive";
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Autobot", 2, 7,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+
+    //test whether querying a custom property will affect the filter
+    filter.setServiceName("");
+    filter.setInterface("");
+    filter.clearCustomAttribute();
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 36);
+    QString customProperty = filter.customAttribute("spark");
+    QVERIFY(customProperty.isEmpty());
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 36);
+
+    //test the removal of a custom property from the filter
+    filter.setCustomAttribute("bot", "automatic");
+    filter.setCustomAttribute("extension", "multidrive");
+    QCOMPARE(filter.customAttributes().length(), 2);
+    filter.clearCustomAttribute("bot");
+    QCOMPARE(filter.customAttributes().length(), 1);
+    filter.clearCustomAttribute("extension");
+    QCOMPARE(filter.customAttributes().length(), 0);
+
+    //test clearing of custom attributes
+    filter.setCustomAttribute("bot", "automatic");
+    filter.setCustomAttribute("extension", "multidrive");
+    QCOMPARE(filter.customAttributes().length(),2);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    filter.clearCustomAttribute();
+    QCOMPARE(filter.customAttributes().length(), 0);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 36);
+
+    //test searching for an empty custom property
+    filter.setCustomAttribute("weapon", "");
+    interfaces = database.getInterfaces(filter);
+    customs.clear();
+    customs["bot"] = "automatic";
+    customs["weapon"] = "";
+    QCOMPARE(interfaces.length(), 2);
+    QVERIFY(compareDescriptor(interfaces[0], "com.cybertron.transform", "Autobot", 1, 9,
+                capabilities, customs, "C:/Ark/matrix.dll", "Autobot Protection Services", "Transformation interface"));
+    customs.clear();
+    capabilities.clear();
+    capabilities << "hunt";
+    customs["weapon"]= "";
+    QVERIFY(compareDescriptor(interfaces[1], "com.cybertron.transform", "Decepticon", 1, 1,
+                capabilities, customs, "C:/Cybertron/unicron.dll", "Decepticon Elimination Services", "Transformation interface"));
+
+
+    filter.clearCustomAttribute();
+
+    //test searching against a non-existent custom property
+    filter.setCustomAttribute("fluxcapacitor", "fluxing");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+
+    //try searching for custom property with service name and interface constraints
+    filter.clearCustomAttribute();
+    filter.setServiceName("autobot");
+    filter.setInterface("com.cybertron.transform", "2.0");
+    filter.setCustomAttribute("bot", "automatic");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(),2);
+    QVERIFY(interfaces[0].majorVersion() == 2 && interfaces[0].minorVersion() == 7);
+    QVERIFY(interfaces[1].majorVersion() == 2 && interfaces[1].minorVersion() == 5);
+
+    //test that there is a difference between querying a custom
+    //property with an empty value and a custom property that has not been set
+    filter.clearCustomAttribute();
+    filter.setCustomAttribute("AllSpark", "");
+    QVERIFY(!filter.customAttribute("AllSpark").isNull());
+    QVERIFY(filter.customAttribute("AllSpark").isEmpty());
+    QVERIFY(filter.customAttribute("Non-existentProperty").isNull());
+
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::attributes()
+{
+    QVERIFY(database.open());
+
+    // == Capability property ==
+    //get empty list of capabilities from an interface registered with capabilities=""
+    QServiceFilter filter;
+    filter.setServiceName("acme");
+    filter.setInterface("com.acme.service.location", "1.0", QServiceFilter::ExactVersionMatch);
+    QList<QServiceInterfaceDescriptor> interfaces = database.getInterfaces(filter);
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(interfaces.count(), 1);
+    QServiceInterfaceDescriptor interface = interfaces[0];
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList(), QStringList());
+
+    //get empty list of capabilites from an interface registered without a
+    //capabilities attribute
+    filter.setServiceName("DHarmaInitiative");
+    filter.setInterface("Com.dharma.electro.discharge", "16.0", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interface = interfaces[0];
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList(), QStringList());
+
+    //get a list of capabilites from an interface with only 1 capability
+    filter.setServiceName("Omni");
+    filter.setInterface("com.omni.device.Accelerometer", "1.1", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interface = interfaces[0];
+    QStringList capabilities;
+    capabilities  << "SurroundingsDD";
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList(), capabilities);
+
+    //get a list of capabilites from an interface with multiple capabilities
+    filter.setServiceName("Omni");
+    filter.setInterface("com.omni.service.Video", "1.4", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interface = interfaces[0];
+    capabilities.clear();
+    capabilities  << "MultimediaDD" << "NetworkServices" << "ReadUserData" << "WriteUserData";
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList(), capabilities);
+
+    //get a list of capabilities from a default service
+    interface = database.interfaceDefault("com.cyberdyne.terminator");
+    capabilities.clear();
+    capabilities << "NetworkServices";
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList(), capabilities);
+
+    // == Interface Description Property ==
+    //get the interface description off an interface registered without a description tag
+    filter.setServiceName("Skynet");
+    filter.setInterface("com.cyberdyne.terminator", "1.8", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interface = interfaces[0];
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString(), QString());
+
+    //get the interface description off an interface registered with an
+    //empty description tag
+    filter.setServiceName("Skynet");
+    filter.setInterface("com.cyberdyne.terminator", "1,6", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interface = interfaces[0];
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString(), QString());
+
+    //get the interface description of an interface
+    filter.setServiceName("Skynet");
+    filter.setInterface("com.cyberdyne.terminator", "1.5", QServiceFilter::ExactVersionMatch);
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interface = interfaces[0];
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString(),
+            QString("Remote communications interface for the T-800v1.5"));
+
+    //get a description from a default service
+    interface = database.interfaceDefault("com.omni.device.Accelerometer");
+    QCOMPARE(interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString(),
+            QString("Interface that provides accelerometer readings(omni)"));
+
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::getServiceNames()
+{
+    QStringList services;
+    services = database.getServiceNames("com.acme.device.sysinfo");
+    QCOMPARE(database.lastError().code(), DBError::DatabaseNotOpen);
+    QCOMPARE(services.count(), 0);
+    QVERIFY(database.open());
+
+    //try wildcard match to get all services
+    services = database.getServiceNames("" );
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(services.count(), 10);
+
+    //try an interface that is implemented by only one service
+    services = database.getServiceNames("com.acme.device.sysinfo");
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(services.count(), 1);
+    QCOMPARE(services[0], QString("acme"));
+
+    //try an interface which  is implmented by multiple services
+    services = database.getServiceNames("COM.omni.device.ACCELerometer"); //also test case insensitivity
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(services.count(), 4);
+    QVERIFY(services.contains("LuthorCorp"));
+    QVERIFY(services.contains("OMNI"));
+    QVERIFY(services.contains("Primatech"));
+    QVERIFY(services.contains("WayneEnt"));
+
+    //try again but with services that have multiple implementation versions of a particular implementation
+    services = database.getServiceNames("com.cyberdyne.terminator");
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(services.count(), 2);
+    QVERIFY(services.contains("Cyberdyne"));
+    QVERIFY(services.contains("skynet"));
+
+    //try with an interface implemented in multiple plugins
+    services = database.getServiceNames("com.dharma.electro.discharge");
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(services.count(), 1);
+    QVERIFY(services.contains("DharmaInitiative", Qt::CaseInsensitive));
+
+    //try with a non-existing interface
+    services = database.getServiceNames("com.omni.device.FluxCapacitor");
+    QCOMPARE(database.lastError().code(), DBError::NoError);
+    QCOMPARE(services.count(), 0);
+
+    QVERIFY(database.close());
+}
+
+bool ServiceDatabaseUnitTest::compareDescriptor(QServiceInterfaceDescriptor interface,
+    QString interfaceName, QString serviceName, int majorVersion, int minorVersion)
+{
+    interface.d->attributes[QServiceInterfaceDescriptor::Capabilities] = QStringList();
+
+    QHash<QString,QString> customs;
+    return compareDescriptor(interface, interfaceName, serviceName, majorVersion, minorVersion,
+            QStringList(), customs);
+}
+
+bool ServiceDatabaseUnitTest::compareDescriptor(QServiceInterfaceDescriptor interface,
+    QString interfaceName, QString serviceName, int majorVersion, int minorVersion,
+    QStringList capabilities, const QHash<QString,QString> customProps, QString filePath, QString serviceDescription,
+    QString interfaceDescription)
+{
+
+    if(interface.interfaceName().compare(interfaceName, Qt::CaseInsensitive) !=0) {
+        qWarning() << "Interface name mismatch: expected =" << interfaceName
+                    << " actual =" << interface.interfaceName();
+        return false;
+    }
+
+    if (interface.serviceName().compare(serviceName, Qt::CaseInsensitive) != 0) {
+        qWarning() << "Service name mismatch: expected =" << serviceName
+                    << " actual =" << interface.serviceName();
+        return false;
+    }
+
+    if (interface.majorVersion() != majorVersion) {
+        qWarning() << "Major version mismatch: expected =" << majorVersion
+                        << " actual =" << interface.majorVersion();
+        return false;
+    }
+
+    if (interface.minorVersion() != minorVersion) {
+        qWarning() << "Minor version mismatch: expected =" << minorVersion
+                    << " actual =" << interface.minorVersion();
+        return false;
+    }
+
+    if (capabilities.count() != 0 || interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList().count() != 0 ) {
+        QStringList securityCapabilities;
+        securityCapabilities = interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList();
+
+        if(securityCapabilities.count() != capabilities.count()) {
+            qWarning() << "Capabilities count mismatch: expected =" << capabilities.count()
+                        << " actual="<< securityCapabilities.count()
+                        << "\texpected capabilities =" << capabilities
+                        << "actual capabilities =" << securityCapabilities;
+            return false;
+        }
+
+        for (int i = 0; i < securityCapabilities.count(); ++i) {
+            if (securityCapabilities[i] != capabilities[i]) {
+                qWarning() << "Capability mismatch: expected =" << capabilities[i]
+                            << " actual =" << securityCapabilities[i];
+                return false;
+            }
+        }
+    }
+
+    if (!filePath.isEmpty()) {
+        if (interface.attribute(QServiceInterfaceDescriptor::Location).toString() != filePath) {
+            qWarning() << "File path mismatch: expected =" << filePath
+                << " actual =" << interface.attribute(QServiceInterfaceDescriptor::Location).toString();
+            return false;
+        }
+    }
+    if (!serviceDescription.isEmpty()) {
+        if (interface.attribute(QServiceInterfaceDescriptor::ServiceDescription).toString() != serviceDescription) {
+            qWarning() << "Service Description mismatch: expected =" << serviceDescription
+                        << " actual=" << interface.attribute(QServiceInterfaceDescriptor::ServiceDescription).toString();
+            return false;
+        }
+    }
+    if (!interfaceDescription.isEmpty()) {
+        if (interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString() != interfaceDescription) {
+            qWarning() << "Interface Description mismatch: expected =" << interfaceDescription
+                        << " actual =" << interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString();
+            return false;
+        }
+
+    }
+
+    if (interface.d->customAttributes.size() != customProps.size()) {
+        qWarning() << "Number of Interface custom attributes don't match. expected: " 
+            <<customProps.size() << "actual: " << interface.d->customAttributes.size();
+            ;
+        qWarning() << "expected:" << customProps << "actual:" << interface.d->customAttributes;
+        return false;
+    }
+
+    QHash<QString, QString>::const_iterator i;
+    for (i = customProps.constBegin(); i!=customProps.constEnd(); i++) {
+        if (interface.customAttribute(i.key()) != i.value()) {
+            qWarning() << "Interface custom property mismatch: expected =" << i.key() <<"("<<i.value()<<")" 
+                        << " actual =" << i.key() << "(" << interface.customAttribute(i.key()) << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+void ServiceDatabaseUnitTest::defaultExternalIfaceIDs()
+{
+    database.open();
+    QServiceInterfaceDescriptor interface;
+    interface.d = new QServiceInterfaceDescriptorPrivate;
+    interface.d->serviceName = "StargateCommand";
+    interface.d->interfaceName = "gov.usa.stargate";
+    interface.d->major = 13;
+    interface.d->minor = 37;
+
+    //see if we can set a "cross-reference" default interface
+    //ie user db referencing an interfaceID belonging to the system db
+    QVERIFY(database.setInterfaceDefault(interface, "FAKE-INTERFACE-ID"));
+    QString interfaceID;
+    QServiceInterfaceDescriptor descriptor = database.interfaceDefault("gov.usa.stargate", &interfaceID);
+    QCOMPARE(database.lastError().code(), DBError::ExternalIfaceIDFound);
+    QCOMPARE(interfaceID, QString("FAKE-INTERFACE-ID"));
+
+    interface.d->interfaceName = "gov.ru.stargate";
+    QVERIFY(database.setInterfaceDefault(interface, "FAKE-INTERFACE-ID2"));
+    QList<QPair<QString,QString> > externalDefaultsInfo = database.externalDefaultsInfo();
+    QCOMPARE(externalDefaultsInfo[0].second, QString("FAKE-INTERFACE-ID"));
+    QCOMPARE(externalDefaultsInfo[1].second, QString("FAKE-INTERFACE-ID2"));
+
+    //see if we can remove the "cross-reference" default interface
+    QVERIFY(database.removeExternalDefaultServiceInterface("FAKE-INTERFACE-ID"));
+    interfaceID.clear();
+    descriptor = database.interfaceDefault("gov.usa.stargate", &interfaceID);
+    QVERIFY(database.lastError().code() == DBError::NotFound);
+    QVERIFY(interfaceID.isEmpty());
+
+    //try to delete an interfaceID that's actually a local interfaceID
+    QServiceFilter filter;
+    filter.setServiceName("omnI");
+    filter.setInterface("com.omni.Device.Lights");
+    QList<QServiceInterfaceDescriptor> interfaces;
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 1);
+    interfaceID = database.getInterfaceID(interfaces[0]);
+    QVERIFY(!database.removeExternalDefaultServiceInterface(interfaceID));
+    QVERIFY(database.lastError().code()  ==  DBError::IfaceIDNotExternal);
+
+    database.close();
+}
+
+void ServiceDatabaseUnitTest::interfaceDefault()
+{
+    QServiceInterfaceDescriptor interface;
+    bool ok;
+
+    //try getting the default service interface implementation when database is not open
+    interface = database.interfaceDefault("com.cyberdyne.terminator");
+    QCOMPARE(database.lastError().code(), DBError::DatabaseNotOpen);
+    QVERIFY(!interface.isValid());
+
+    //try getting a valid default, in this case only one implementation exists
+    QVERIFY(database.open());
+    interface = database.interfaceDefault("com.omni.device.Lights");
+    QVERIFY(database.lastError().code() == DBError::NoError);
+    QVERIFY(interface.isValid());
+    QStringList capabilities;
+    QHash<QString,QString> customs;
+    QVERIFY(compareDescriptor(interface, "com.omni.device.Lights",
+                                "OMNI", 9, 0, capabilities, customs,
+                                "C:/OmniInc/omniinc.dll",
+                                "Omni mobile",
+                                "Interface that provides access to device lights"));
+
+    //try getting a valid default, in this case two services implement the interface
+    ok = false;
+    interface = database.interfaceDefault("com.CyBerDynE.Terminator");
+    QVERIFY(database.lastError().code() == DBError::NoError);
+    QVERIFY(interface.isValid());
+
+    capabilities << "NetworkServices";
+    QVERIFY(compareDescriptor(interface, "com.cyberdyne.terminator",
+                                    "Cyberdyne", 2,1, capabilities, customs,
+                                    "C:/California/connor.dll",
+                                    "Cyberdyne Termination Services",
+                                    "Remote communications interface for the T-800"));
+
+    //try getting a valid default, in this case multiple services implement the interface
+    ok = false;
+    interface = database.interfaceDefault("com.omni.device.Accelerometer");
+    QVERIFY(database.lastError().code() == DBError::NoError);
+    QVERIFY(interface.isValid());
+    capabilities.clear();
+    capabilities << "SurroundingsDD";
+    QVERIFY(compareDescriptor(interface, "com.omni.device.Accelerometer",
+                                    "OMNI", 1, 1, capabilities, customs,
+                                    "C:/OmniInc/omniinc.dll",
+                                    "Omni mobile",
+                                    "Interface that provides accelerometer readings(omni)"));
+
+    //try searching for an interface that isn't registered
+    interface = database.interfaceDefault("com.omni.device.FluxCapacitor");
+    QVERIFY(database.lastError().code() == DBError::NotFound);
+    QVERIFY(!interface.isValid());
+
+    //try getting the default interface impl for a service that is made up of multiple
+    //plugins
+    interface = database.interfaceDefault("com.dharma.electro.discharge");
+    QVERIFY(database.lastError().code() == DBError::NoError);
+    capabilities.clear();
+    QVERIFY(compareDescriptor(interface, "com.dharma.electro.discharge",
+                                "DharmaInitiative", 4, 0,
+                                capabilities, customs, "C:/island/swan.dll"));
+
+    //trying getting the default using an empty interface name
+    interface = database.interfaceDefault("");
+    QVERIFY(database.lastError().code() == DBError::NotFound);
+    QVERIFY(!interface.isValid());
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::setInterfaceDefault()
+{
+    QServiceInterfaceDescriptor interface;
+    interface.d = new QServiceInterfaceDescriptorPrivate;
+    interface.d->serviceName = "cyberdyne";
+    interface.d->interfaceName = "com.cyberdyne.terminator";
+    interface.d->major = 1;
+    interface.d->minor = 6;
+
+    QVERIFY(!database.setInterfaceDefault(interface));
+    QCOMPARE(database.lastError().code(), DBError::DatabaseNotOpen);
+
+    QVERIFY(database.open());
+
+    //try setting the default to a older version provided by the same
+    //service
+    QServiceInterfaceDescriptor defaultInterface;
+    defaultInterface = database.interfaceDefault("com.cyberdyne.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "Cyberdyne", 2, 1));
+
+    QVERIFY(database.setInterfaceDefault(interface));
+    defaultInterface = database.interfaceDefault("com.cyberdyne.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "Cyberdyne", 1, 6));
+
+    //try setting the default to another service
+    interface.d->serviceName = "SKYnet"; //check that behaviour is case insensitive
+    interface.d->interfaceName = "COM.cyberdyne.terminaTOR";
+    interface.d->major = 1;
+    interface.d->minor = 5;
+
+    QVERIFY(database.setInterfaceDefault(interface));
+    defaultInterface = database.interfaceDefault("com.CYBERDYNE.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "skynet", 1, 5));
+
+    //try setting the default of a service that is made up of multiple plugins
+    interface.d->serviceName = "DharmaInitiative";
+    interface.d->interfaceName = "com.dharma.electro.discharge";
+    interface.d->major = 8;
+    interface.d->minor = 0;
+    QVERIFY(database.setInterfaceDefault(interface));
+    QStringList capabilities;
+    QHash<QString,QString> customs;
+    defaultInterface = database.interfaceDefault("com.dharma.electro.discharge");
+    QVERIFY(compareDescriptor(defaultInterface, "com.dharma.electro.discharge",
+                                "DharmaInitiative", 8, 0, capabilities, customs,
+                                "C:/island/pearl.dll"));
+
+    //try setting the default to a implementation verison not supplied
+    //by the service
+    interface.d->serviceName = "SKYnet"; //check that behaviour is case insensitive
+    interface.d->interfaceName = "COM.cyberdyne.terminaTOR";
+    interface.d->major = 1;
+    interface.d->minor = 9;
+
+    QVERIFY(!database.setInterfaceDefault(interface));
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    defaultInterface = database.interfaceDefault("com.CYBERDYNE.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "skynet", 1, 5));
+
+    //try setting the default of an interface that doesn't exist
+    interface.d->serviceName = "SKYnet"; //check that behaviour is case insensitive
+    interface.d->interfaceName = "com.omni.device.FluxCapacitor";
+    interface.d->major = 1;
+    interface.d->minor = 5;
+
+    QVERIFY(!database.setInterfaceDefault(interface));
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    defaultInterface = database.interfaceDefault("com.CYBERDYNE.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "skynet", 1, 5));
+
+    //try setting an interface default to a service that does not implement the interface
+    interface.d->serviceName = "Primatech";
+    interface.d->interfaceName = "com.cyberdyne.terminator";
+    interface.d->major = 1;
+    interface.d->minor = 4;
+    QVERIFY(!database.setInterfaceDefault(interface));
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    defaultInterface = database.interfaceDefault("com.CYBERDYNE.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "skynet", 1, 5));
+
+    //try setting an interface default to a non-existent service
+    interface.d->serviceName = "StarkInd"; //check that behaviour is case insensitive
+    interface.d->interfaceName = "COM.cyberdyne.terminaTOR";
+    interface.d->major = 1;
+    interface.d->minor = 5;
+    QVERIFY(!database.setInterfaceDefault(interface));
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    defaultInterface = database.interfaceDefault("com.CYBERDYNE.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "skynet", 1, 5));
+
+    //try setting the default using an invalid interface
+    QServiceInterfaceDescriptor invalidInterface;
+    QVERIFY(!invalidInterface.isValid());
+    QVERIFY(!database.setInterfaceDefault(invalidInterface));
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    defaultInterface = database.interfaceDefault("com.CYBERDYNE.terminator");
+    QVERIFY(compareDescriptor(defaultInterface, "com.cyberdyne.terminator",
+                                        "skynet", 1, 5));
+
+    QVERIFY(database.close());
+}
+
+void ServiceDatabaseUnitTest::unregister()
+{
+    QVERIFY(!unregisterService("acme"));
+    QCOMPARE(database.lastError().code(), DBError::DatabaseNotOpen);
+
+    QVERIFY(database.open());
+
+    //try unregister a non-existing service
+    QVERIFY(!unregisterService("StarkInd"));
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    QServiceFilter filter;
+
+    // == check that the service to delete is already in the database ==
+    //try a search for descriptors by service name 
+    filter.setServiceName("omni");
+    QList<QServiceInterfaceDescriptor> interfaces;
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 3);
+
+    //search for service via interface name
+    QStringList services;
+    services = database.getServiceNames("com.omni.device.Accelerometer");
+    QCOMPARE(services.count(), 4);
+    QVERIFY(services.contains("OMNI"));
+
+    //search for descriptors via interface name
+    filter.setServiceName("");
+    filter.setInterface("com.omni.device.Accelerometer");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 5);
+    bool serviceFound = false;
+    foreach (QServiceInterfaceDescriptor interface, interfaces) {
+        if(interface.serviceName() == "OMNI")
+            serviceFound = true;
+    }
+    QVERIFY(serviceFound);
+
+    //confirm that it is the default service for a couple of interfaces
+    QServiceInterfaceDescriptor interface;
+    interface = database.interfaceDefault("com.omni.device.Accelerometer");
+    QVERIFY(interface.serviceName() == "OMNI");//other services implement this interface
+
+    interface = database.interfaceDefault("com.omni.service.Video");
+    QVERIFY(interface.serviceName() == "OMNI");//no other services implmement this interface
+
+    //confirm that interface and service attributes exist for the service
+    QStringList serviceIDs = getServiceIDs("omni");
+    foreach(const QString &serviceID, serviceIDs)
+        QVERIFY(existsInServicePropertyTable(serviceID));
+
+    QStringList interfaceIDs = getInterfaceIDs("omni");
+    QCOMPARE(interfaceIDs.count(), 3);
+    foreach(const QString &interfaceID, interfaceIDs)
+        QVERIFY(existsInInterfacePropertyTable(interfaceID));
+
+    QVERIFY(unregisterService("oMni")); //ensure case insensitive behaviour
+
+    //  == check that deleted service and associated interfaces cannot be found ==
+    //try a search for descriptors by service name
+    QServiceFilter serviceFilter;
+    serviceFilter.setServiceName("omni");
+    interfaces = database.getInterfaces(serviceFilter);
+    QCOMPARE(interfaces.count(), 0);
+
+    //search for service via interface name
+    services = database.getServiceNames("com.omni.device.Accelerometer");
+    QCOMPARE(services.count(), 3);
+    QVERIFY(!services.contains("OMNI"));
+
+    //search for descriptors via interface name
+    filter.setServiceName("");
+    filter.setInterface("com.omni.device.Accelerometer");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+    serviceFound = false;
+    foreach (QServiceInterfaceDescriptor interface, interfaces) {
+        if(interface.serviceName() == "OMNI")
+            serviceFound = true;
+    }
+    QVERIFY(!serviceFound);
+
+    //ensure a new default interface has been assigned for the following interface
+    interface = database.interfaceDefault("com.omni.device.Accelerometer");
+    QVERIFY(interface.isValid());
+    QCOMPARE(interface.serviceName(), QString("WayneEnt"));
+    QCOMPARE(interface.majorVersion(), 2);
+    QCOMPARE(interface.minorVersion(), 0);
+
+    //ensure there is no longer a default for the following interface
+    interface = database.interfaceDefault("com.omni.service.Video");
+    QVERIFY(!interface.isValid());
+
+    //ensure the associated interfaceIDs no longer exist in the Property
+    //and Defaults tables
+    foreach (const QString &serviceID, serviceIDs)
+        QVERIFY(!existsInServicePropertyTable(serviceID));
+    foreach (const QString &interfaceID, interfaceIDs)
+        QVERIFY(!existsInInterfacePropertyTable(interfaceID));
+    foreach(const QString &interfaceID, interfaceIDs)
+        QVERIFY(!existsInDefaultsTable(interfaceID));
+
+    //  == unregister an service that implements a default interface
+    //     but whose default interface name differs in case
+    //     from the other implementations it provides ==
+    interface.d = new QServiceInterfaceDescriptorPrivate;
+    interface.d->serviceName = "Cyberdyne";
+    interface.d->interfaceName = "com.cyberdyne.terminator";
+    interface.d->major = 2;
+    interface.d->minor = 0;
+    QVERIFY(database.setInterfaceDefault(interface));
+
+    interface = database.interfaceDefault("com.cyberdyne.terminator");
+    QVERIFY(interface.isValid());
+    QVERIFY(unregisterService("cyberDYNE"));
+    interface = database.interfaceDefault("com.cyberdyne.terminatOR");
+    QVERIFY(interface.isValid());
+    QVERIFY(compareDescriptor(interface, "com.cyberdyne.terminator",
+                                "skynet", 3, 6));
+
+    // == unregister a service that is made up of multiple plugins ==
+    // get the serviceIDs and interfaceIDs to make sure it's been deleted later on
+    serviceIDs = getServiceIDs("DharmaInitiative");
+    QVERIFY(serviceIDs.count() > 0);
+    interfaceIDs = getInterfaceIDs("DharmaInitiative");
+    QVERIFY(interfaceIDs.count() > 0);
+
+    foreach(const QString &interfaceID, interfaceIDs)
+        QVERIFY(existsInInterfacePropertyTable(interfaceID));
+    foreach(const QString &serviceID, serviceIDs)
+        QVERIFY(existsInServicePropertyTable(serviceID));
+
+    QVERIFY(unregisterService("DHARMAInitiative"));
+    interface = database.interfaceDefault("com.dharma.electro.discharge");
+    QVERIFY(!interface.isValid());
+    QCOMPARE(database.lastError().code(), DBError::NotFound);
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 0);
+
+    foreach(const QString &serviceID, serviceIDs)
+        QVERIFY(!existsInServicePropertyTable(serviceID));
+    foreach(const QString &interfaceID, interfaceIDs)
+        QVERIFY(!existsInInterfacePropertyTable(interfaceID));
+    foreach(const QString &interfaceID, interfaceIDs)
+        QVERIFY(!existsInDefaultsTable(interfaceID));
+
+    //  == check that the service can be registered again
+    //     after it has been unregistered ==
+    QDir testdir = QDir(TESTDATA_DIR "/testdata" );
+    ServiceMetaData parser(testdir.absoluteFilePath("ServiceDharma_Flame.xml"));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+    interface = database.interfaceDefault("com.dharma.electro.discharge");
+    QVERIFY(interface.isValid());
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 3);
+
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceDharma_Swan.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults()));
+    filter.setServiceName("DharmaInitiative");
+    filter.setInterface("");
+    interfaces = database.getInterfaces(filter);
+    QCOMPARE(interfaces.count(), 4);
+    interface = database.interfaceDefault("com.dharma.electro.discharge");
+    QVERIFY(interface.isValid());
+    QStringList capabilities;
+    QHash<QString,QString> customs;
+    QVERIFY(compareDescriptor(interface, "com.dharma.electro.discharge",
+                    "DharmaInitiative", 16, 0, capabilities, customs, "C:/island/flame.dll"));
+}
+
+QStringList ServiceDatabaseUnitTest::getInterfaceIDs(const QString &serviceName) {
+    QSqlDatabase sqlDatabase  = QSqlDatabase::database(database.m_connectionName);
+    QSqlQuery query(sqlDatabase);
+    QString statement("Select Interface.ID FROM Interface, Service "
+                        "WHERE Service.ID = Interface.ServiceID "
+                        "AND Service.Name = ? COLLATE NOCASE");
+    QList<QVariant> bindValues;
+    bindValues.append(serviceName);
+    database.executeQuery(&query, statement, bindValues);
+
+    QStringList ids;
+    while   (query.next()) {
+        ids << query.value(0).toString();
+    }
+    return ids;
+}
+
+QStringList ServiceDatabaseUnitTest::getServiceIDs(const QString &serviceName) {
+    QSqlDatabase sqlDatabase = QSqlDatabase::database(database.m_connectionName);
+    QSqlQuery query(sqlDatabase);
+    QString statement("SELECT Service.ID from Service "
+                        "WHERE Service.Name = ? COLLATE NOCASE");
+    QList<QVariant> bindValues;
+    bindValues.append(serviceName);
+    database.executeQuery(&query, statement, bindValues);
+
+    QStringList ids;
+    while(query.next()) {
+        ids << query.value(0).toString();
+    }
+    return ids;
+}
+
+bool ServiceDatabaseUnitTest::existsInInterfacePropertyTable(const QString &interfaceID)
+{
+    QSqlDatabase sqlDatabase  = QSqlDatabase::database(database.m_connectionName);
+    QSqlQuery query(sqlDatabase);
+
+    QString statement("SELECT InterfaceID FROM InterfaceProperty "
+                "WHERE InterfaceID = ?");
+    QList<QVariant> bindValues;
+    bindValues.append(interfaceID);
+    database.executeQuery(&query, statement, bindValues);
+    if (query.next())
+        return true;
+    else
+        return false;
+}
+
+bool ServiceDatabaseUnitTest::existsInServicePropertyTable(const QString &serviceID)
+{
+    QSqlDatabase sqlDatabase  = QSqlDatabase::database(database.m_connectionName);
+    QSqlQuery query(sqlDatabase);
+
+    QString statement("SELECT ServiceID from ServiceProperty "
+                        "WHERE ServiceID = ?");
+    QList<QVariant> bindValues;
+    bindValues.append(serviceID);
+    database.executeQuery(&query, statement, bindValues);
+    if (query.next())
+        return true;
+    else
+        return false;
+}
+
+bool ServiceDatabaseUnitTest::existsInDefaultsTable(const QString &interfaceID)
+{
+    QSqlDatabase sqlDatabase = QSqlDatabase::database(database.m_connectionName);
+    QSqlQuery query(sqlDatabase);
+    QString statement("SELECT InterfaceID From Defaults "
+                    "WHERE InterfaceID = ?");
+    QList<QVariant> bindValues;
+    bindValues.append(interfaceID);
+    database.executeQuery(&query, statement, bindValues);
+
+    if (query.next())
+        return true;
+    else
+        return false;
+}
+
+// Two small helper functions to assist with passing the security token to the servicedatabase.
+// Do not use this function if you intentionally want to provide emtpy securityToken,
+// but rather call database directly.
+bool ServiceDatabaseUnitTest::registerService(const ServiceMetaDataResults &service, const QString &securityToken)
+{
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+    if (securityToken.isEmpty()) {
+        return database.registerService(service, securityTokenOwner);
+    } else {
+        return database.registerService(service, securityToken);
+    }
+#else
+    Q_UNUSED(securityToken);
+    return database.registerService(service);
+#endif
+}
+
+bool ServiceDatabaseUnitTest::unregisterService(const QString &serviceName, const QString &securityToken)
+{
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+    if (securityToken.isEmpty()) {
+        return database.unregisterService(serviceName, securityTokenOwner);
+    } else {
+        return database.unregisterService(serviceName, securityToken);
+    }
+#else
+    Q_UNUSED(securityToken);
+    return database.unregisterService(serviceName);
+#endif
+}
+
+void ServiceDatabaseUnitTest::securityTokens() {
+#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+    QSKIP("Security tokens are not enabled (currently only enabled on Symbian).", SkipAll);
+#endif
+    // Clear databases just in case
+    database.close();
+    QFile::remove(database.databasePath());
+    QDir testdir = QDir(TESTDATA_DIR "/testdata" );
+    QVERIFY(database.open());
+    database.m_databasePath = QDir::toNativeSeparators(QDir::currentPath().append("/services.db"));
+
+    // Declare and setup testdata
+    ServiceMetaData parser(testdir.absoluteFilePath("ServiceAcme.xml"));
+    QVERIFY(parser.extractMetadata());
+
+    // Actual teststeps
+    qDebug("---------- 1. Add and remove with same security token. (OK)");
+    QVERIFY(registerService(parser.parseResults(), securityTokenOwner));
+    QVERIFY(unregisterService("acme", securityTokenOwner));
+
+    qDebug("---------- 2. Add and remove with empty security token. (NOK)");
+    QVERIFY(!database.registerService(parser.parseResults()));
+    QCOMPARE(database.lastError().code(), DBError::NoWritePermissions);
+    QVERIFY(!database.unregisterService("acme"));
+    QCOMPARE(database.lastError().code(), DBError::NoWritePermissions);
+
+    qDebug("---------- 3. Add, then remove with different security token. (NOK)");
+    QVERIFY(registerService(parser.parseResults(), securityTokenOwner));
+    QVERIFY(!unregisterService("acme", securityTokenStranger));
+    QCOMPARE(database.lastError().code(), DBError::NoWritePermissions);
+    QVERIFY(unregisterService("acme", securityTokenOwner));
+
+    qDebug("---------- 4. Add namesake but but differently located service with same security token. (OK)");
+    QStringList xmlFilesNamesakeServices;
+    xmlFilesNamesakeServices << "ServiceDharma_Swan.xml" << "ServiceDharma_Pearl.xml";
+    foreach(const QString &file, xmlFilesNamesakeServices) {
+        parser.setDevice(new QFile(testdir.absoluteFilePath(file)));
+        QVERIFY(parser.extractMetadata());
+        QVERIFY(registerService(parser.parseResults(), securityTokenOwner));
+    }
+    QVERIFY(!database.getServiceNames(QString()).isEmpty());
+    QVERIFY(unregisterService("DharmaInitiative", securityTokenOwner));
+    QVERIFY(database.getServiceNames(QString()).isEmpty());
+
+    qDebug("---------- 5. Add namesake but differently located services with different security token. (NOK)");
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceDharma_Swan.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(registerService(parser.parseResults(), securityTokenOwner));
+    parser.setDevice(new QFile(testdir.absoluteFilePath("ServiceDharma_Pearl.xml")));
+    QVERIFY(parser.extractMetadata());
+    QVERIFY(!registerService(parser.parseResults(), securityTokenStranger));
+    QCOMPARE(database.lastError().code(), DBError::NoWritePermissions);
+    QVERIFY(unregisterService("DharmaInitiative", securityTokenOwner));
+}
+
+void ServiceDatabaseUnitTest::cleanupTestCase()
+{
+    database.close();
+    QFile::remove(database.databasePath());
+}
+QTEST_MAIN(ServiceDatabaseUnitTest)
+
+#include "tst_servicedatabase.moc"