--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qcontactmanager/tst_qcontactmanager.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,3325 @@
+/****************************************************************************
+**
+** 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 "qtcontacts.h"
+#include "qcontactchangeset.h"
+#include "qcontactmanagerdataholder.h"
+
+QTM_USE_NAMESPACE
+// Eventually these will make it into qtestcase.h
+// but we might need to tweak the timeout values here.
+#ifndef QTRY_COMPARE
+#define QTRY_COMPARE(__expr, __expected) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 5000; \
+ if ((__expr) != (__expected)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
+ QTest::qWait(__step); \
+ } \
+ QCOMPARE(__expr, __expected); \
+ } while(0)
+#endif
+
+#ifndef QTRY_VERIFY
+#define QTRY_VERIFY(__expr) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 5000; \
+ if (!(__expr)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
+ QTest::qWait(__step); \
+ } \
+ QVERIFY(__expr); \
+ } while(0)
+#endif
+
+
+#define QTRY_WAIT(code, __expr) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 5000; \
+ if (!(__expr)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
+ do { code } while(0); \
+ QTest::qWait(__step); \
+ } \
+ } while(0)
+
+#define QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(params) params.remove(QString::fromAscii(QTCONTACTS_VERSION_NAME)); \
+ params.remove(QString::fromAscii(QTCONTACTS_IMPLEMENTATION_VERSION_NAME))
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+// to get QFETCH to work with the template expression...
+typedef QMap<QString,QString> tst_QContactManager_QStringMap;
+Q_DECLARE_METATYPE(tst_QContactManager_QStringMap)
+Q_DECLARE_METATYPE(QList<QContactLocalId>)
+
+/* A class that no backend can support */
+class UnsupportedMetatype {
+ int foo;
+};
+Q_DECLARE_METATYPE(UnsupportedMetatype)
+Q_DECLARE_METATYPE(QContact)
+Q_DECLARE_METATYPE(QContactManager::Error)
+
+class tst_QContactManager : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QContactManager();
+ virtual ~tst_QContactManager();
+
+private:
+ void dumpContactDifferences(const QContact& a, const QContact& b);
+ void dumpContact(const QContact &c);
+ void dumpContacts(QContactManager *cm);
+ bool isSuperset(const QContact& ca, const QContact& cb);
+ QList<QContactDetail> removeAllDefaultDetails(const QList<QContactDetail>& details);
+ void addManagers(); // add standard managers to the data
+ QContact createContact(QContactDetailDefinition nameDef, QString firstName, QString lastName, QString phoneNumber);
+ void saveContactName(QContact *contact, QContactDetailDefinition nameDef, QContactName *contactName, const QString &name) const;
+
+ QScopedPointer<QContactManagerDataHolder> managerDataHolder;
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+private slots:
+
+ void doDump();
+ void doDump_data() {addManagers();}
+
+ void doDumpSchema();
+ void doDumpSchema_data() {addManagers();}
+
+ /* Special test with special data */
+ void uriParsing();
+ void nameSynthesis();
+ void compatibleContact();
+
+ /* Tests that are run on all managers */
+ void metadata();
+ void nullIdOperations();
+ void add();
+ void update();
+ void remove();
+ void batch();
+ void signalEmission();
+ void detailDefinitions();
+ void displayName();
+ void actionPreferences();
+ void selfContactId();
+ void detailOrders();
+ void relationships();
+ void contactType();
+
+ /* Tests that take no data */
+ void contactValidation();
+ void errorStayingPut();
+ void ctors();
+ void invalidManager();
+ void memoryManager();
+ void changeSet();
+ void fetchHint();
+
+ /* Special test with special data */
+ void uriParsing_data();
+ void nameSynthesis_data();
+ void compatibleContact_data();
+ /* Tests that are run on all managers */
+ void metadata_data() {addManagers();}
+ void nullIdOperations_data() {addManagers();}
+ void add_data() {addManagers();}
+ void update_data() {addManagers();}
+ void remove_data() {addManagers();}
+ void batch_data() {addManagers();}
+ void signalEmission_data() {addManagers();}
+ void detailDefinitions_data() {addManagers();}
+ void displayName_data() {addManagers();}
+ void actionPreferences_data() {addManagers();}
+ void selfContactId_data() {addManagers();}
+ void detailOrders_data() {addManagers();}
+ void relationships_data() {addManagers();}
+ void contactType_data() {addManagers();}
+};
+
+tst_QContactManager::tst_QContactManager()
+{
+}
+
+tst_QContactManager::~tst_QContactManager()
+{
+}
+
+void tst_QContactManager::initTestCase()
+{
+ managerDataHolder.reset(new QContactManagerDataHolder());
+
+ /* Make sure these other test plugins are NOT loaded by default */
+ // These are now removed from the list of managers in addManagers()
+ //QVERIFY(!QContactManager::availableManagers().contains("testdummy"));
+ //QVERIFY(!QContactManager::availableManagers().contains("teststaticdummy"));
+ //QVERIFY(!QContactManager::availableManagers().contains("maliciousplugin"));
+}
+
+void tst_QContactManager::cleanupTestCase()
+{
+ managerDataHolder.reset(0);
+}
+
+void tst_QContactManager::dumpContactDifferences(const QContact& ca, const QContact& cb)
+{
+ // Try to narrow down the differences
+ QContact a(ca);
+ QContact b(cb);
+
+ QContactName n1 = a.detail(QContactName::DefinitionName);
+ QContactName n2 = b.detail(QContactName::DefinitionName);
+
+ // Check the name components in more detail
+ QCOMPARE(n1.firstName(), n2.firstName());
+ QCOMPARE(n1.middleName(), n2.middleName());
+ QCOMPARE(n1.lastName(), n2.lastName());
+ QCOMPARE(n1.prefix(), n2.prefix());
+ QCOMPARE(n1.suffix(), n2.suffix());
+ QCOMPARE(n1.customLabel(), n2.customLabel());
+
+ // Check the display label
+ QCOMPARE(a.displayLabel(), b.displayLabel());
+
+ // Now look at the rest
+ QList<QContactDetail> aDetails = a.details();
+ QList<QContactDetail> bDetails = b.details();
+
+ // They can be in any order, so loop
+ // First remove any matches
+ foreach(QContactDetail d, aDetails) {
+ foreach(QContactDetail d2, bDetails) {
+ if(d == d2) {
+ a.removeDetail(&d);
+ b.removeDetail(&d2);
+ break;
+ }
+ }
+ }
+
+ // Now dump the extra details that were unmatched in A (note that DisplayLabel and Type are always present).
+ aDetails = a.details();
+ bDetails = b.details();
+ foreach(QContactDetail d, aDetails) {
+ if (d.definitionName() != QContactDisplayLabel::DefinitionName && d.definitionName() != QContactType::DefinitionName)
+ qDebug() << "A contact had extra detail:" << d.definitionName() << d.variantValues();
+ }
+ // and same for B
+ foreach(QContactDetail d, bDetails) {
+ if (d.definitionName() != QContactDisplayLabel::DefinitionName && d.definitionName() != QContactType::DefinitionName)
+ qDebug() << "B contact had extra detail:" << d.definitionName() << d.variantValues();
+ }
+
+ // now test specifically the display label and the type
+ if (a.displayLabel() != b.displayLabel()) {
+ qDebug() << "A contact display label =" << a.displayLabel();
+ qDebug() << "B contact display label =" << b.displayLabel();
+ }
+ if (a.type() != b.type()) {
+ qDebug() << "A contact type =" << a.type();
+ qDebug() << "B contact type =" << b.type();
+ }
+}
+
+bool tst_QContactManager::isSuperset(const QContact& ca, const QContact& cb)
+{
+ // returns true if contact ca is a superset of contact cb
+ // we use this test instead of equality because dynamic information
+ // such as presence/location, and synthesised information such as
+ // display label and (possibly) type, may differ between a contact
+ // in memory and the contact in the managed store.
+
+ QContact a(ca);
+ QContact b(cb);
+ QList<QContactDetail> aDetails = a.details();
+ QList<QContactDetail> bDetails = b.details();
+
+ // They can be in any order, so loop
+ // First remove any matches
+ foreach(QContactDetail d, aDetails) {
+ foreach(QContactDetail d2, bDetails) {
+ if(d == d2) {
+ a.removeDetail(&d);
+ b.removeDetail(&d2);
+ break;
+ }
+ }
+ }
+
+ // Second remove any superset matches (eg, backend adds a field)
+ aDetails = a.details();
+ bDetails = b.details();
+ foreach (QContactDetail d, aDetails) {
+ foreach (QContactDetail d2, bDetails) {
+ if (d.definitionName() == d2.definitionName()) {
+ bool canRemove = true;
+ QMap<QString, QVariant> d2map = d2.variantValues();
+ foreach (QString key, d2map.keys()) {
+ if (d.value(key) != d2.value(key)) {
+ // d can have _more_ keys than d2,
+ // but not _less_; and it cannot
+ // change the value.
+ canRemove = false;
+ }
+ }
+
+ if (canRemove) {
+ // if we get to here, we can remove the details.
+ a.removeDetail(&d);
+ b.removeDetail(&d2);
+ break;
+ }
+ }
+ }
+ }
+
+ // check for contact type updates
+ if (!a.type().isEmpty())
+ if (!b.type().isEmpty())
+ if (a.type() != b.type())
+ return false; // nonempty type is different.
+
+ // Now check to see if b has any details remaining; if so, a is not a superset.
+ // Note that the DisplayLabel and Type can never be removed.
+ if (b.details().size() > 2
+ || (b.details().size() == 2 && (b.details().value(0).definitionName() != QContactDisplayLabel::DefinitionName
+ || b.details().value(1).definitionName() != QContactType::DefinitionName)))
+ return false;
+ return true;
+}
+
+void tst_QContactManager::dumpContact(const QContact& contact)
+{
+ QContactManager m;
+ qDebug() << "Contact: " << contact.id().localId() << "(" << m.synthesizedContactDisplayLabel(contact) << ")";
+ QList<QContactDetail> details = contact.details();
+ foreach(QContactDetail d, details) {
+ qDebug() << " " << d.definitionName() << ":";
+ qDebug() << " Vals:" << d.variantValues();
+ }
+}
+
+void tst_QContactManager::dumpContacts(QContactManager *cm)
+{
+ QList<QContactLocalId> ids = cm->contactIds();
+
+ qDebug() << "There are" << ids.count() << "contacts in" << cm->managerUri();
+
+ foreach(QContactLocalId id, ids) {
+ QContact c = cm->contact(id);
+ dumpContact(c);
+ }
+}
+
+void tst_QContactManager::uriParsing_data()
+{
+ QTest::addColumn<QString>("uri");
+ QTest::addColumn<bool>("good"); // is this a good uri or not
+ QTest::addColumn<QString>("manager");
+ QTest::addColumn<QMap<QString, QString> >("parameters");
+
+ QMap<QString, QString> inparameters;
+ inparameters.insert("foo", "bar");
+ inparameters.insert("bazflag", QString());
+ inparameters.insert("bar", "glob");
+
+ QMap<QString, QString> inparameters2;
+ inparameters2.insert("this has spaces", QString());
+ inparameters2.insert("and& an", " &");
+ inparameters2.insert("and an ", "=quals");
+
+ QTest::newRow("built") << QContactManager::buildUri("manager", inparameters) << true << "manager" << inparameters;
+ QTest::newRow("built with escaped parameters") << QContactManager::buildUri("manager", inparameters2) << true << "manager" << inparameters2;
+ QTest::newRow("no scheme") << "this should not split" << false << QString() << tst_QContactManager_QStringMap();
+ QTest::newRow("wrong scheme") << "invalidscheme:foo bar" << false << QString() << tst_QContactManager_QStringMap();
+ QTest::newRow("right scheme, no colon") << "qtcontacts" << false << QString() << tst_QContactManager_QStringMap();
+ QTest::newRow("no manager, colon, no params") << "qtcontacts::" << false << "manager" << tst_QContactManager_QStringMap();
+ QTest::newRow("yes manager, no colon, no params") << "qtcontacts:manager" << true << "manager" << tst_QContactManager_QStringMap();
+ QTest::newRow("yes manager, yes colon, no params") << "qtcontacts:manager:" << true << "manager"<< tst_QContactManager_QStringMap();
+ QTest::newRow("yes params") << "qtcontacts:manager:foo=bar&bazflag=&bar=glob" << true << "manager" << inparameters;
+ QTest::newRow("yes params but misformed") << "qtcontacts:manager:foo=bar&=gloo&bar=glob" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 2") << "qtcontacts:manager:=&=gloo&bar=glob" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 3") << "qtcontacts:manager:==" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 4") << "qtcontacts:manager:&&" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 5") << "qtcontacts:manager:&goo=bar" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 6") << "qtcontacts:manager:goo&bar" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 7") << "qtcontacts:manager:goo&bar&gob" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 8") << "qtcontacts:manager:==&&==&goo=bar" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 9") << "qtcontacts:manager:foo=bar=baz" << false << "manager" << inparameters;
+ QTest::newRow("yes params but misformed 10") << "qtcontacts:manager:foo=bar=baz=glob" << false << "manager" << inparameters;
+ QTest::newRow("no manager but yes params") << "qtcontacts::foo=bar&bazflag=&bar=glob" << false << QString() << inparameters;
+ QTest::newRow("no manager or params") << "qtcontacts::" << false << QString() << inparameters;
+ QTest::newRow("no manager or params or colon") << "qtcontacts:" << false << QString() << inparameters;
+}
+
+void tst_QContactManager::addManagers()
+{
+ QTest::addColumn<QString>("uri");
+
+ QStringList managers = QContactManager::availableManagers();
+
+ /* Known one that will not pass */
+ managers.removeAll("invalid");
+ managers.removeAll("testdummy");
+ managers.removeAll("teststaticdummy");
+ managers.removeAll("maliciousplugin");
+
+ foreach(QString mgr, managers) {
+ QMap<QString, QString> params;
+ QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params);
+ if (mgr == "memory") {
+ params.insert("id", "tst_QContactManager");
+ QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params);
+ }
+ }
+}
+
+/*
+ * Helper method for creating a QContact instance with name and phone number
+ * details. Name is generated according to the detail definition assuming that
+ * either first and last name or custom label is supported.
+ */
+QContact tst_QContactManager::createContact(
+ QContactDetailDefinition nameDef,
+ QString firstName,
+ QString lastName,
+ QString phoneNumber)
+{
+ QContact contact;
+
+ if(!firstName.isEmpty() || !lastName.isEmpty()) {
+ QContactName n;
+
+ if(nameDef.fields().contains(QContactName::FieldFirstName)
+ && nameDef.fields().contains(QContactName::FieldFirstName)) {
+ n.setFirstName(firstName);
+ n.setLastName(lastName);
+ } else if(nameDef.fields().contains(QContactName::FieldCustomLabel)) {
+ n.setCustomLabel(firstName + " " + lastName);
+ } else {
+ // assume that either first and last name or custom label is supported
+ QTest::qWarn("Neither custom label nor first name/last name supported!");
+ return QContact();
+ }
+ contact.saveDetail(&n);
+ }
+
+ if (!phoneNumber.isEmpty()) {
+ QContactPhoneNumber ph;
+ ph.setNumber(phoneNumber);
+ contact.saveDetail(&ph);
+ }
+
+ return contact;
+}
+
+void tst_QContactManager::saveContactName(QContact *contact, QContactDetailDefinition nameDef, QContactName *contactName, const QString &name) const
+{
+ // check which name fields are supported in the following order:
+ // 1. custom label, 2. first name, 3. last name
+ if(nameDef.fields().contains(QContactName::FieldCustomLabel)) {
+ contactName->setCustomLabel(name);
+ } else if(nameDef.fields().contains(QContactName::FieldFirstName)) {
+ contactName->setFirstName(name);
+ } else if(nameDef.fields().contains(QContactName::FieldLastName)) {
+ contactName->setLastName(name);
+ } else {
+ // Assume that at least one of the above name fields is supported by the backend
+ QVERIFY(false);
+ }
+ contact->saveDetail(contactName);
+}
+
+void tst_QContactManager::metadata()
+{
+ // ensure that the backend is publishing its metadata (name / parameters / uri) correctly
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(new QContactManager("memory"));
+ QVERIFY(QContactManager::buildUri(cm->managerName(), cm->managerParameters()) == cm->managerUri());
+}
+
+
+void tst_QContactManager::nullIdOperations()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(new QContactManager("memory"));
+ QVERIFY(!cm->removeContact(QContactLocalId()));
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+
+ QContact c = cm->contact(QContactLocalId());
+ QVERIFY(c.id() == QContactId());
+ QVERIFY(c.isEmpty());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+}
+
+void tst_QContactManager::uriParsing()
+{
+ QFETCH(QString, uri);
+ QFETCH(bool, good);
+ QFETCH(QString, manager);
+ QFETCH(tst_QContactManager_QStringMap, parameters);
+
+ QString outmanager;
+ QMap<QString, QString> outparameters;
+
+ if (good) {
+ /* Good split */
+ /* Test splitting */
+ QVERIFY(QContactManager::parseUri(uri, 0, 0)); // no out parms
+
+ // 1 out param
+ QVERIFY(QContactManager::parseUri(uri, &outmanager, 0));
+ QCOMPARE(manager, outmanager);
+ QVERIFY(QContactManager::parseUri(uri, 0, &outparameters));
+
+ QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters);
+
+ QCOMPARE(parameters, outparameters);
+
+ outmanager.clear();
+ outparameters.clear();
+ QVERIFY(QContactManager::parseUri(uri, &outmanager, &outparameters));
+
+ QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters);
+
+ QCOMPARE(manager, outmanager);
+ QCOMPARE(parameters, outparameters);
+ } else {
+ /* bad splitting */
+ outmanager.clear();
+ outparameters.clear();
+ QVERIFY(QContactManager::parseUri(uri, 0, 0) == false);
+ QVERIFY(QContactManager::parseUri(uri, &outmanager, 0) == false);
+ QVERIFY(outmanager.isEmpty());
+ QVERIFY(QContactManager::parseUri(uri, 0, &outparameters) == false);
+ QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters);
+ QVERIFY(outparameters.isEmpty());
+
+ /* make sure the in parameters don't change with a bad split */
+ outmanager = manager;
+ outparameters = parameters;
+ QVERIFY(QContactManager::parseUri(uri, &outmanager, 0) == false);
+ QCOMPARE(manager, outmanager);
+ QVERIFY(QContactManager::parseUri(uri, 0, &outparameters) == false);
+ QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters);
+ QCOMPARE(parameters, outparameters);
+ }
+}
+
+void tst_QContactManager::ctors()
+{
+ /* test the different ctors to make sure we end up with the same uri */
+ QVERIFY(QContactManager::availableManagers().count() > 1); // invalid + something else
+ QVERIFY(QContactManager::availableManagers().contains("invalid"));
+ QString defaultStore = QContactManager::availableManagers().value(0);
+ QVERIFY(defaultStore != "invalid");
+
+ qDebug() << "Available managers:" << QContactManager::availableManagers();
+
+ QMap<QString, QString> randomParameters;
+ randomParameters.insert("something", "old");
+ randomParameters.insert("something...", "new");
+ randomParameters.insert("something ", "borrowed");
+ randomParameters.insert(" something", "blue");
+
+ QObject parent;
+
+ QContactManager cm; // default
+ QContactManager cm2(defaultStore);
+ QContactManager cm3(defaultStore, QMap<QString, QString>());
+ QContactManager cm4(cm.managerUri()); // should fail
+
+ QScopedPointer<QContactManager> cm5(QContactManager::fromUri(QContactManager::buildUri(defaultStore, QMap<QString, QString>())));
+ QScopedPointer<QContactManager> cm6(QContactManager::fromUri(cm.managerUri())); // uri is not a name; should fail.
+ QScopedPointer<QContactManager> cm9(QContactManager::fromUri(QString(), &parent));
+
+ QVERIFY(cm9->parent() == &parent);
+
+ /* OLD TEST WAS THIS: */
+ //QCOMPARE(cm.managerUri(), cm2.managerUri());
+ //QCOMPARE(cm.managerUri(), cm3.managerUri());
+ //QCOMPARE(cm.managerUri(), cm5->managerUri());
+ //QCOMPARE(cm.managerUri(), cm6->managerUri());
+ //QCOMPARE(cm.managerUri(), cm9->managerUri());
+ /* NEW TEST IS THIS: Test that the names of the managers are the same */
+ QCOMPARE(cm.managerName(), cm2.managerName());
+ QCOMPARE(cm.managerName(), cm3.managerName());
+ QCOMPARE(cm.managerName(), cm5->managerName());
+ QCOMPARE(cm.managerName(), cm6->managerName());
+ QCOMPARE(cm.managerName(), cm9->managerName());
+
+ QVERIFY(cm.managerUri() != cm4.managerUri()); // don't pass a uri to the ctor
+
+ /* Test that we get invalid stores when we do silly things */
+ QContactManager em("non existent");
+ QContactManager em2("non existent", QMap<QString, QString>());
+ QContactManager em3("memory", randomParameters);
+
+ /* Also invalid, since we don't have one of these anyway */
+ QScopedPointer<QContactManager> em4(QContactManager::fromUri("invalid uri"));
+ QScopedPointer<QContactManager> em5(QContactManager::fromUri(QContactManager::buildUri("nonexistent", QMap<QString, QString>())));
+ QScopedPointer<QContactManager> em6(QContactManager::fromUri(em3.managerUri()));
+
+
+ /*
+ * Sets of stores that should be equivalent:
+ * - 1, 2, 4, 5
+ * - 3, 6
+ */
+
+ /* First some URI testing for equivalent stores */
+ QVERIFY(em.managerUri() == em2.managerUri());
+ QVERIFY(em.managerUri() == em5->managerUri());
+ QVERIFY(em.managerUri() == em4->managerUri());
+ QVERIFY(em2.managerUri() == em4->managerUri());
+ QVERIFY(em2.managerUri() == em5->managerUri());
+ QVERIFY(em4->managerUri() == em5->managerUri());
+
+ QVERIFY(em3.managerUri() == em6->managerUri());
+
+ /* Test the stores that should not be the same */
+ QVERIFY(em.managerUri() != em3.managerUri());
+ QVERIFY(em.managerUri() != em6->managerUri());
+
+ /* now the components */
+ QCOMPARE(em.managerName(), QString("invalid"));
+ QCOMPARE(em2.managerName(), QString("invalid"));
+ QCOMPARE(em3.managerName(), QString("memory"));
+ QCOMPARE(em4->managerName(), QString("invalid"));
+ QCOMPARE(em5->managerName(), QString("invalid"));
+ QCOMPARE(em6->managerName(), QString("memory"));
+ QCOMPARE(em.managerParameters(), tst_QContactManager_QStringMap());
+ QCOMPARE(em2.managerParameters(), tst_QContactManager_QStringMap());
+ QCOMPARE(em4->managerParameters(), tst_QContactManager_QStringMap());
+ QCOMPARE(em5->managerParameters(), tst_QContactManager_QStringMap());
+ QCOMPARE(em3.managerParameters(), em6->managerParameters()); // memory engine discards the given params, replaces with id.
+
+
+ // Finally test the platform specific engines are actually the defaults
+#if defined(Q_OS_SYMBIAN)
+ QCOMPARE(defaultStore, QString("symbian"));
+#elif defined(Q_WS_MAEMO_6)
+ QCOMPARE(defaultStore, QString("tracker"));
+#elif defined(Q_WS_MAEMO_5)
+ QCOMPARE(defaultStore, QString("maemo5"));
+#elif defined(Q_OS_WINCE)
+ QCOMPARE(defaultStore, QString("wince"));
+#else
+ QCOMPARE(defaultStore, QString("memory"));
+#endif
+}
+
+void tst_QContactManager::doDump()
+{
+ // Only do this if it has been explicitly selected
+ if (QCoreApplication::arguments().contains("doDump")) {
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ dumpContacts(cm.data());
+ }
+}
+
+Q_DECLARE_METATYPE(QVariant)
+
+void tst_QContactManager::doDumpSchema()
+{
+ // Only do this if it has been explicitly selected
+ if (QCoreApplication::arguments().contains("doDumpSchema")) {
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ // Get the schema for each supported type
+ foreach(QString type, cm->supportedContactTypes()) {
+ QMap<QString, QContactDetailDefinition> defs = cm->detailDefinitions(type);
+
+ foreach(QContactDetailDefinition def, defs.values()) {
+ if (def.isUnique())
+ qDebug() << QString("%2::%1 (Unique) {").arg(def.name()).arg(type).toAscii().constData();
+ else
+ qDebug() << QString("%2::%1 {").arg(def.name()).arg(type).toAscii().constData();
+ QMap<QString, QContactDetailFieldDefinition> fields = def.fields();
+
+ foreach(QString fname, fields.keys()) {
+ QContactDetailFieldDefinition field = fields.value(fname);
+
+ if (field.allowableValues().count() > 0) {
+ // Make some pretty output
+ QStringList allowedList;
+ foreach(QVariant var, field.allowableValues()) {
+ QString allowed;
+ if (var.type() == QVariant::String)
+ allowed = QString("'%1'").arg(var.toString());
+ else if (var.type() == QVariant::StringList)
+ allowed = QString("'%1'").arg(var.toStringList().join(","));
+ else {
+ // use the textstream <<
+ QDebug dbg(&allowed);
+ dbg << var;
+ }
+ allowedList.append(allowed);
+ }
+
+ qDebug() << QString(" %2 %1 {%3}").arg(fname).arg(QMetaType::typeName(field.dataType())).arg(allowedList.join(",")).toAscii().constData();
+ } else
+ qDebug() << QString(" %2 %1").arg(fname).arg(QMetaType::typeName(field.dataType())).toAscii().constData();
+ }
+
+ qDebug() << "}";
+ }
+ }
+ }
+}
+
+void tst_QContactManager::add()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ QContactDetailDefinition nameDef = cm->detailDefinition(QContactName::DefinitionName, QContactType::TypeContact);
+ QContact alice = createContact(nameDef, "Alice", "inWonderland", "1234567");
+ int currCount = cm->contactIds().count();
+ QVERIFY(cm->saveContact(&alice));
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ QVERIFY(!alice.id().managerUri().isEmpty());
+ QVERIFY(alice.id().localId() != 0);
+ QCOMPARE(cm->contactIds().count(), currCount+1);
+
+ QContact added = cm->contact(alice.id().localId());
+ QVERIFY(added.id() == alice.id());
+
+ if (!isSuperset(added, alice)) {
+ dumpContacts(cm.data());
+ dumpContactDifferences(added, alice);
+ QCOMPARE(added, alice);
+ }
+
+ // now try adding a contact that does not exist in the database with non-zero id
+ if (cm->managerName() == "symbiansim") {
+ // TODO: symbiansim backend fails this test currently. Will be fixed later.
+ QWARN("This manager has a known issue with saving a non-zero id contact. Skipping this test step.");
+ } else {
+ QContact nonexistent = createContact(nameDef, "nonexistent", "contact", "");
+ QVERIFY(cm->saveContact(&nonexistent)); // should work
+ QVERIFY(cm->removeContact(nonexistent.id().localId())); // now nonexistent has an id which does not exist
+ QVERIFY(!cm->saveContact(&nonexistent)); // hence, should fail
+ QCOMPARE(cm->error(), QContactManager::DoesNotExistError);
+ nonexistent.setId(QContactId());
+ QVERIFY(cm->saveContact(&nonexistent)); // after setting id to zero, should save
+ QVERIFY(cm->removeContact(nonexistent.id().localId()));
+ }
+
+ // now try adding a "megacontact"
+ // - get list of all definitions supported by the manager
+ // - add one detail of each definition to a contact
+ // - save the contact
+ // - read it back
+ // - ensure that it's the same.
+ QContact megacontact;
+ QMap<QString, QContactDetailDefinition> defmap = cm->detailDefinitions();
+ QList<QContactDetailDefinition> defs = defmap.values();
+ foreach (const QContactDetailDefinition def, defs) {
+
+ // Leave these warnings here - might need an API for this
+ // XXX FIXME: access constraint reporting as moved to the detail itself
+ //if (def.accessConstraint() == QContactDetailDefinition::ReadOnly) {
+ // continue;
+ //}
+
+ if (cm->managerName() == "maemo5") {
+ // The maemo5 backend only supports reading of Guid and QCOA
+ if (def.name() == QContactGuid::DefinitionName)
+ continue;
+ if (def.name() == QContactOnlineAccount::DefinitionName)
+ continue;
+ if (def.name() == QContactPresence::DefinitionName)
+ continue;
+ }
+
+ // This is probably read-only
+ if (def.name() == QContactTimestamp::DefinitionName)
+ continue;
+
+ // otherwise, create a new detail of the given type and save it to the contact
+ QContactDetail det(def.name());
+ QMap<QString, QContactDetailFieldDefinition> fieldmap = def.fields();
+ QStringList fieldKeys = fieldmap.keys();
+ foreach (const QString& fieldKey, fieldKeys) {
+ // get the field, and check to see that it's not constrained.
+ QContactDetailFieldDefinition currentField = fieldmap.value(fieldKey);
+
+ // Don't test detail uris as these are manager specific
+ if (fieldKey == QContactDetail::FieldDetailUri)
+ continue;
+
+ // Special case: phone number.
+ if (def.name() == QContactPhoneNumber::DefinitionName &&
+ fieldKey == QContactPhoneNumber::FieldNumber) {
+ det.setValue(fieldKey, "+3581234567890");
+ continue;
+ }
+
+ // Attempt to create a worthy value
+ if (!currentField.allowableValues().isEmpty()) {
+ // we want to save a value that will be accepted.
+ if (currentField.dataType() == QVariant::StringList)
+ det.setValue(fieldKey, QStringList() << currentField.allowableValues().first().toString());
+ else if (currentField.dataType() == QVariant::List)
+ det.setValue(fieldKey, QVariantList() << currentField.allowableValues().first());
+ else
+ det.setValue(fieldKey, currentField.allowableValues().first());
+ } else {
+ // any value of the correct type will be accepted
+ bool savedSuccessfully = false;
+ QVariant dummyValue = QVariant(fieldKey); // try to get some unique string data
+ if (dummyValue.canConvert(currentField.dataType())) {
+ savedSuccessfully = dummyValue.convert(currentField.dataType());
+ if (savedSuccessfully) {
+ // we have successfully created a (supposedly) valid field for this detail.
+ det.setValue(fieldKey, dummyValue);
+ continue;
+ }
+ }
+
+ // nope, couldn't save the string value (test); try a date.
+ dummyValue = QVariant(QDate::currentDate());
+ if (dummyValue.canConvert(currentField.dataType())) {
+ savedSuccessfully = dummyValue.convert(currentField.dataType());
+ if (savedSuccessfully) {
+ // we have successfully created a (supposedly) valid field for this detail.
+ det.setValue(fieldKey, dummyValue);
+ continue;
+ }
+ }
+
+ // nope, couldn't convert a string or a date - try the integer value (42)
+ dummyValue = QVariant(42);
+ if (dummyValue.canConvert(currentField.dataType())) {
+ savedSuccessfully = dummyValue.convert(currentField.dataType());
+ if (savedSuccessfully) {
+ // we have successfully created a (supposedly) valid field for this detail.
+ det.setValue(fieldKey, dummyValue);
+ continue;
+ }
+ }
+
+ // if we get here, we don't know what sort of value can be saved...
+ }
+ }
+ if (!det.isEmpty())
+ megacontact.saveDetail(&det);
+ }
+
+ QVERIFY(cm->saveContact(&megacontact)); // must be able to save since built from definitions.
+ QContact retrievedMegacontact = cm->contact(megacontact.id().localId());
+ if (!isSuperset(retrievedMegacontact, megacontact)) {
+ dumpContactDifferences(megacontact, retrievedMegacontact);
+ QEXPECT_FAIL("mgr='wince'", "Address Display Label mismatch", Continue);
+ QCOMPARE(megacontact, retrievedMegacontact);
+ }
+
+ // now a contact with many details of a particular definition
+ // if the detail is not unique it should then support minumum of two of the same kind
+ const int nrOfdetails = 2;
+ QContact veryContactable = createContact(nameDef, "Very", "Contactable", "");
+ for (int i = 0; i < nrOfdetails; i++) {
+ QString phnStr = QString::number(i);
+ QContactPhoneNumber vcphn;
+ vcphn.setNumber(phnStr);
+ QVERIFY(veryContactable.saveDetail(&vcphn));
+ }
+
+ // check that all the numbers were added successfully
+ QVERIFY(veryContactable.details(QContactPhoneNumber::DefinitionName).size() == nrOfdetails);
+
+ // check if it can be saved
+ QContactDetailDefinition def = cm->detailDefinition(QContactPhoneNumber::DefinitionName);
+ if (def.isUnique()) {
+ QVERIFY(!cm->saveContact(&veryContactable));
+ }
+ else {
+ QVERIFY(cm->saveContact(&veryContactable));
+
+ // verify save
+ QContact retrievedContactable = cm->contact(veryContactable.id().localId());
+ if (!isSuperset(retrievedContactable, veryContactable)) {
+ dumpContactDifferences(veryContactable, retrievedContactable);
+ QEXPECT_FAIL("mgr='wince'", "Number of phones supported mismatch", Continue);
+ QCOMPARE(veryContactable, retrievedContactable);
+ }
+ }
+}
+
+void tst_QContactManager::update()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ if (cm->managerName() == QString(QLatin1String("maemo5"))) {
+ // we specifically want to test the update semantics of the maemo5 backend
+ // since there are various complexities relating to roster contacts.
+ QContact mt;
+ QContactName mtn;
+ mtn.setFirstName("test");
+ mtn.setLastName("maemo");
+ QContactPhoneNumber pn;
+ pn.setNumber("12345");
+
+ mt.saveDetail(&mtn);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId()); // force reload of (persisted) contact
+ QVERIFY(mt.details<QContactPhoneNumber>().count() == 0);
+
+ // now save a single phonenumber
+ mt.saveDetail(&pn);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId()); // force reload of (persisted) contact
+ QVERIFY(mt.details<QContactPhoneNumber>().count() == 1);
+
+ // edit some other existing detail and save (shouldn't duplicate the phone number)
+ mtn.setMiddleName("middle");
+ mt.saveDetail(&mtn);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId()); // force reload of (persisted) contact
+ QCOMPARE(mt.details<QContactPhoneNumber>().count(), 1);
+
+ // add some other detail and save (shouldn't duplicate the phone number)
+ QContactEmailAddress mte;
+ mte.setEmailAddress("test@test.com");
+ mt.saveDetail(&mte);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId()); // force reload of (persisted) contact
+ QCOMPARE(mt.details<QContactPhoneNumber>().count(), 1);
+
+ // add another phone number detail and save (should create a single other phone number)
+ QContactPhoneNumber pn2;
+ pn2.setNumber("98765");
+ mt.saveDetail(&pn2);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId()); // force reload of (persisted) contact
+ QCOMPARE(mt.details<QContactPhoneNumber>().count(), 2);
+
+ // here we do something tricky: we save one of the previously saved phone numbers
+ // in a _different_ contact, and see if that causes problems with the overwrite vs new detail code.
+ QContactPhoneNumber pn2Copy = pn2;
+ QContact mt2;
+ QContactName mt2n;
+ mt2n.setFirstName("test2");
+ mt2.saveDetail(&mt2n);
+ QContactPhoneNumber shouldBeNew = pn;
+ mt2.saveDetail(&shouldBeNew);
+ QVERIFY(cm->saveContact(&mt2));
+ mt2 = cm->contact(mt2.localId());
+ QCOMPARE(mt2.details<QContactPhoneNumber>().count(), 1);
+ mt2.saveDetail(&pn2);
+ QVERIFY(cm->saveContact(&mt2));
+ mt2 = cm->contact(mt2.localId());
+ QCOMPARE(mt2.details<QContactPhoneNumber>().count(), 2);
+ pn2 = pn2Copy; // reset just in case backend added some fields.
+
+ // remove the other phone number detail, shouldn't cause side effects to the first...
+ // NOTE: we need to reload the details before attempting to remove/edit them
+ // because the backend can change the ids.
+ QList<QContactPhoneNumber> pnums = mt.details<QContactPhoneNumber>();
+ foreach (const QContactPhoneNumber& pd, pnums) {
+ if (pd.number() == pn2.number())
+ pn2 = pd;
+ else if (pd.number() == pn.number())
+ pn = pd;
+ }
+ mt.removeDetail(&pn2);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId()); // force reload of (persisted) contact
+ QCOMPARE(mt.details<QContactPhoneNumber>().count(), 1);
+
+ // edit the original phone number detail, shouldn't duplicate the phone number
+ // NOTE: we need to reload the details before attempting to remove/edit them
+ // because the backend can change the ids.
+ pnums = mt.details<QContactPhoneNumber>();
+ foreach (const QContactPhoneNumber& pd, pnums) {
+ if (pd.number() == pn2.number())
+ pn2 = pd;
+ else if (pd.number() == pn.number())
+ pn = pd;
+ }
+ pn.setNumber("54321");
+ mt.saveDetail(&pn);
+ cm->saveContact(&mt);
+ mt = cm->contact(mt.localId());
+ QCOMPARE(mt.details<QContactPhoneNumber>().count(), 1);
+ QVERIFY(mt.detail<QContactPhoneNumber>() == pn);
+
+ // we also should do the same test for other details (for example, gender).
+ // if the backend cannot save multiple copies of a detail (eg, gender always overwrites)
+ // it should FAIL the save operation if the contact has multiple of that detail type,
+ // and set error to QContactManager::LimitReachedError.
+ QContactGender mtg, mtg2;
+ mtg.setGender(QContactGender::GenderFemale);
+ mtg2.setGender(QContactGender::GenderMale);
+ mt.saveDetail(&mtg);
+ QVERIFY(cm->saveContact(&mt)); // one gender is fine
+ mt.saveDetail(&mtg2);
+ QVERIFY(!cm->saveContact(&mt)); // two is not
+ //QCOMPARE(cm->error(), QContactManager::LimitReachedError); // should be LimitReachedError.
+ mt = cm->contact(mt.localId());
+ QVERIFY(mt.details<QContactGender>().count() == 1);
+ }
+
+ /* Save a new contact first */
+ int contactCount = cm->contacts().size();
+ QContactDetailDefinition nameDef = cm->detailDefinition(QContactName::DefinitionName, QContactType::TypeContact);
+ QContact alice = createContact(nameDef, "Alice", "inWonderland", "1234567");
+ QVERIFY(cm->saveContact(&alice));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ contactCount += 1; // added a new contact.
+ QCOMPARE(cm->contacts().size(), contactCount);
+
+ /* Update name */
+ QContactName name = alice.detail(QContactName::DefinitionName);
+ saveContactName(&alice, nameDef, &name, "updated");
+ QVERIFY(cm->saveContact(&alice));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ saveContactName(&alice, nameDef, &name, "updated2");
+ QVERIFY(cm->saveContact(&alice));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ alice = cm->contact(alice.localId()); // force reload of (persisted) alice
+ QContact updated = cm->contact(alice.localId());
+ QContactName updatedName = updated.detail(QContactName::DefinitionName);
+ QCOMPARE(updatedName, name);
+ QCOMPARE(cm->contacts().size(), contactCount); // contact count should be the same, no new contacts
+
+ /* Test that adding a new detail doesn't cause unwanted side effects */
+ int detailCount = alice.details().size();
+ QContactEmailAddress email;
+ email.setEmailAddress("test@example.com");
+ alice.saveDetail(&email);
+ QVERIFY(cm->saveContact(&alice));
+ QCOMPARE(cm->contacts().size(), contactCount); // contact count shoudl be the same, no new contacts
+
+ // This test is dangerous, since backends can add timestamps etc...
+ detailCount += 1;
+ QCOMPARE(detailCount, alice.details().size()); // adding a detail should cause the detail count to increase by one.
+
+ /* Test that removal of fields in a detail works */
+ QContactPhoneNumber phn = alice.detail<QContactPhoneNumber>();
+ phn.setNumber("1234567");
+ phn.setContexts(QContactDetail::ContextHome);
+ alice.saveDetail(&phn);
+ QVERIFY(cm->saveContact(&alice));
+ alice = cm->contact(alice.localId()); // force reload of (persisted) alice
+ QVERIFY(alice.detail<QContactPhoneNumber>().contexts().contains(QContactDetail::ContextHome)); // check context saved.
+ phn = alice.detail<QContactPhoneNumber>(); // reload the detail, since it's key could have changed
+ phn.setContexts(QStringList()); // remove context field.
+ alice.saveDetail(&phn);
+ QVERIFY(cm->saveContact(&alice));
+ alice = cm->contact(alice.localId()); // force reload of (persisted) alice
+ QVERIFY(alice.detail<QContactPhoneNumber>().contexts().isEmpty()); // check context removed.
+ QCOMPARE(cm->contacts().size(), contactCount); // removal of a field of a detail shouldn't affect the contact count
+
+ // This test is dangerous, since backends can add timestamps etc...
+ QCOMPARE(detailCount, alice.details().size()); // removing a field from a detail should affect the detail count
+
+ /* Test that removal of details works */
+ phn = alice.detail<QContactPhoneNumber>(); // reload the detail, since it's key could have changed
+ alice.removeDetail(&phn);
+ QVERIFY(cm->saveContact(&alice));
+ alice = cm->contact(alice.localId()); // force reload of (persisted) alice
+ QVERIFY(alice.details<QContactPhoneNumber>().isEmpty()); // no such detail.
+ QCOMPARE(cm->contacts().size(), contactCount); // removal of a detail shouldn't affect the contact count
+
+ // This test is dangerous, since backends can add timestamps etc...
+ //detailCount -= 1;
+ //QCOMPARE(detailCount, alice.details().size()); // removing a detail should cause the detail count to decrease by one.
+
+ if (cm->hasFeature(QContactManager::Groups)) {
+ // Try changing types - not allowed
+ // from contact -> group
+ alice.setType(QContactType::TypeGroup);
+ QContactName na = alice.detail(QContactName::DefinitionName);
+ alice.removeDetail(&na);
+ QVERIFY(!cm->saveContact(&alice));
+ QVERIFY(cm->error() == QContactManager::AlreadyExistsError);
+
+ // from group -> contact
+ QContact jabberwock = createContact(nameDef, "", "", "1234567890");
+ jabberwock.setType(QContactType::TypeGroup);
+ QVERIFY(cm->saveContact(&jabberwock));
+ jabberwock.setType(QContactType::TypeContact);
+ QVERIFY(!cm->saveContact(&jabberwock));
+ QVERIFY(cm->error() == QContactManager::AlreadyExistsError);
+ }
+}
+
+void tst_QContactManager::remove()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ /* Save a new contact first */
+ QContactDetailDefinition nameDef = cm->detailDefinition(QContactName::DefinitionName, QContactType::TypeContact);
+ QContact alice = createContact(nameDef, "Alice", "inWonderland", "1234567");
+ QVERIFY(cm->saveContact(&alice));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ QVERIFY(alice.id() != QContactId());
+
+ /* Remove the created contact */
+ const int contactCount = cm->contactIds().count();
+ QVERIFY(cm->removeContact(alice.localId()));
+ QCOMPARE(cm->contactIds().count(), contactCount - 1);
+ QVERIFY(cm->contact(alice.localId()).isEmpty());
+ QCOMPARE(cm->error(), QContactManager::DoesNotExistError);
+}
+
+void tst_QContactManager::batch()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ /* First test null pointer operations */
+ QVERIFY(!cm->saveContacts(NULL, NULL));
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ QVERIFY(!cm->removeContacts(QList<QContactLocalId>(), NULL));
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ // Get supported name field
+ QString nameField = QContactName::FieldFirstName;
+ QContactDetailDefinition def = cm->detailDefinition(QContactName::DefinitionName);
+ if (!def.fields().contains(QContactName::FieldFirstName)) {
+ if(def.fields().contains(QContactName::FieldCustomLabel))
+ nameField = QLatin1String(QContactName::FieldCustomLabel);
+ else
+ QSKIP("This backend does not support the required name field!", SkipSingle);
+ }
+
+ /* Now add 3 contacts, all valid */
+ QContact a;
+ QContactName na;
+ na.setValue(nameField, "XXXXXX Albert");
+ a.saveDetail(&na);
+
+ QContact b;
+ QContactName nb;
+ nb.setValue(nameField, "XXXXXX Bob");
+ b.saveDetail(&nb);
+
+ QContact c;
+ QContactName nc;
+ nc.setValue(nameField, "XXXXXX Carol");
+ c.saveDetail(&nc);
+
+ QList<QContact> contacts;
+ contacts << a << b << c;
+
+ QMap<int, QContactManager::Error> errorMap;
+ // Add one dummy error to test if the errors are reset
+ errorMap.insert(0, QContactManager::NoError);
+ QVERIFY(cm->saveContacts(&contacts, &errorMap));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ QVERIFY(errorMap.count() == 0);
+
+ /* Make sure our contacts got updated too */
+ QVERIFY(contacts.count() == 3);
+ QVERIFY(contacts.at(0).id() != QContactId());
+ QVERIFY(contacts.at(1).id() != QContactId());
+ QVERIFY(contacts.at(2).id() != QContactId());
+
+ QVERIFY(contacts.at(0).detail(QContactName::DefinitionName) == na);
+ QVERIFY(contacts.at(1).detail(QContactName::DefinitionName) == nb);
+ QVERIFY(contacts.at(2).detail(QContactName::DefinitionName) == nc);
+
+ /* Retrieve again */
+ a = cm->contact(contacts.at(0).id().localId());
+ b = cm->contact(contacts.at(1).id().localId());
+ c = cm->contact(contacts.at(2).id().localId());
+ QVERIFY(contacts.at(0).detail(QContactName::DefinitionName) == na);
+ QVERIFY(contacts.at(1).detail(QContactName::DefinitionName) == nb);
+ QVERIFY(contacts.at(2).detail(QContactName::DefinitionName) == nc);
+
+ /* Save again, with a null error map */
+ QVERIFY(cm->saveContacts(&contacts, NULL));
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ /* Now make an update to them all */
+ QContactPhoneNumber number;
+ number.setNumber("1234567");
+
+ QVERIFY(contacts[0].saveDetail(&number));
+ number.setNumber("234567");
+ QVERIFY(contacts[1].saveDetail(&number));
+ number.setNumber("34567");
+ QVERIFY(contacts[2].saveDetail(&number));
+
+ QVERIFY(cm->saveContacts(&contacts, &errorMap));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ QVERIFY(errorMap.count() == 0);
+
+ /* Retrieve them and check them again */
+ a = cm->contact(contacts.at(0).id().localId());
+ b = cm->contact(contacts.at(1).id().localId());
+ c = cm->contact(contacts.at(2).id().localId());
+ QVERIFY(contacts.at(0).detail(QContactName::DefinitionName) == na);
+ QVERIFY(contacts.at(1).detail(QContactName::DefinitionName) == nb);
+ QVERIFY(contacts.at(2).detail(QContactName::DefinitionName) == nc);
+
+ QVERIFY(a.details<QContactPhoneNumber>().count() == 1);
+ QVERIFY(b.details<QContactPhoneNumber>().count() == 1);
+ QVERIFY(c.details<QContactPhoneNumber>().count() == 1);
+
+ QVERIFY(a.details<QContactPhoneNumber>().at(0).number() == "1234567");
+ QVERIFY(b.details<QContactPhoneNumber>().at(0).number() == "234567");
+ QVERIFY(c.details<QContactPhoneNumber>().at(0).number() == "34567");
+
+ /* Now delete them all */
+ QList<QContactLocalId> ids;
+ ids << a.id().localId() << b.id().localId() << c.id().localId();
+ QVERIFY(cm->removeContacts(ids, &errorMap));
+ QVERIFY(errorMap.count() == 0);
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ /* Make sure the contacts really don't exist any more */
+ QVERIFY(cm->contact(a.id().localId()).id() == QContactId());
+ QVERIFY(cm->contact(a.id().localId()).isEmpty());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+ QVERIFY(cm->contact(b.id().localId()).id() == QContactId());
+ QVERIFY(cm->contact(b.id().localId()).isEmpty());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+ QVERIFY(cm->contact(c.id().localId()).id() == QContactId());
+ QVERIFY(cm->contact(c.id().localId()).isEmpty());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+ /* Now try removing with all invalid ids (e.g. the ones we just removed) */
+ ids.clear();
+ ids << a.id().localId() << b.id().localId() << c.id().localId();
+ QVERIFY(!cm->removeContacts(ids, &errorMap));
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+ QVERIFY(errorMap.count() == 3);
+ QVERIFY(errorMap.values().at(0) == QContactManager::DoesNotExistError);
+ QVERIFY(errorMap.values().at(1) == QContactManager::DoesNotExistError);
+ QVERIFY(errorMap.values().at(2) == QContactManager::DoesNotExistError);
+
+ /* And again with a null error map */
+ QVERIFY(!cm->removeContacts(ids, NULL));
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+ /* Try adding some new ones again, this time one with an error */
+ contacts.clear();
+ a.setId(QContactId());
+ b.setId(QContactId());
+ c.setId(QContactId());
+
+ /* Make B the bad guy */
+ QContactDetail bad("does not exist and will break if you add it");
+ bad.setValue("This is also bad", "Very bad");
+ b.saveDetail(&bad);
+
+ contacts << a << b << c;
+ QVERIFY(!cm->saveContacts(&contacts, &errorMap));
+ /* We can't really say what the error will be.. maybe bad argument, maybe invalid detail */
+ QVERIFY(cm->error() != QContactManager::NoError);
+
+ /* It's permissible to fail all the adds, or to add the successful ones */
+ QVERIFY(errorMap.count() > 0);
+ QVERIFY(errorMap.count() <= 3);
+
+ // A might have gone through
+ if (errorMap.keys().contains(0)) {
+ QVERIFY(errorMap.value(0) != QContactManager::NoError);
+ QVERIFY(contacts.at(0).id() == QContactId());
+ } else {
+ QVERIFY(contacts.at(0).id() != QContactId());
+ }
+
+ // B should have failed
+ QVERIFY(errorMap.value(1) == QContactManager::InvalidDetailError);
+ QVERIFY(contacts.at(1).id() == QContactId());
+
+ // C might have gone through
+ if (errorMap.keys().contains(2)) {
+ QVERIFY(errorMap.value(2) != QContactManager::NoError);
+ QVERIFY(contacts.at(2).id() == QContactId());
+ } else {
+ QVERIFY(contacts.at(2).id() != QContactId());
+ }
+
+ /* Fix up B and re save it */
+ QVERIFY(contacts[1].removeDetail(&bad));
+ QVERIFY(cm->saveContacts(&contacts, &errorMap));
+ QVERIFY(errorMap.count() == 0);
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ // Save and remove a fourth contact. Store the id.
+ a.setId(QContactId());
+ QVERIFY(cm->saveContact(&a));
+ QContactLocalId removedId = a.localId();
+ QVERIFY(cm->removeContact(removedId));
+
+ /* Now delete 3 items, but with one bad argument */
+ ids.clear();
+ ids << contacts.at(0).id().localId();
+ ids << removedId;
+ ids << contacts.at(2).id().localId();
+
+ QVERIFY(!cm->removeContacts(ids, &errorMap));
+ QVERIFY(cm->error() != QContactManager::NoError);
+
+ /* Again, the backend has the choice of either removing the successful ones, or not */
+ QVERIFY(errorMap.count() > 0);
+ QVERIFY(errorMap.count() <= 3);
+
+ // A might have gone through
+ if (errorMap.keys().contains(0)) {
+ QVERIFY(errorMap.value(0) != QContactManager::NoError);
+ QVERIFY(contacts.at(0).id() == QContactId());
+ } else {
+ QVERIFY(contacts.at(0).id() != QContactId());
+ }
+
+ /* B should definitely have failed */
+ QVERIFY(errorMap.value(1) == QContactManager::DoesNotExistError);
+ QVERIFY(ids.at(1) == removedId);
+
+ // A might have gone through
+ if (errorMap.keys().contains(2)) {
+ QVERIFY(errorMap.value(2) != QContactManager::NoError);
+ QVERIFY(contacts.at(2).id() == QContactId());
+ } else {
+ QVERIFY(contacts.at(2).id() != QContactId());
+ }
+}
+
+void tst_QContactManager::invalidManager()
+{
+ /* Create an invalid manager */
+ QContactManager manager("this should never work");
+ QVERIFY(manager.managerName() == "invalid");
+ QVERIFY(manager.managerVersion() == 0);
+
+ /* also, test the other ctor behaviour is sane also */
+ QContactManager anotherManager("this should never work", 15);
+ QVERIFY(anotherManager.managerName() == "invalid");
+ QVERIFY(anotherManager.managerVersion() == 0);
+
+ /* Now test that all the operations fail */
+ QVERIFY(manager.contactIds().count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ QContact foo;
+ QContactName nf;
+ nf.setLastName("Lastname");
+ foo.saveDetail(&nf);
+
+ QVERIFY(manager.synthesizedContactDisplayLabel(foo).isEmpty());
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ QVERIFY(manager.saveContact(&foo) == false);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ QVERIFY(foo.id() == QContactId());
+ QVERIFY(manager.contactIds().count() == 0);
+
+ QVERIFY(manager.contact(foo.id().localId()).id() == QContactId());
+ QVERIFY(manager.contact(foo.id().localId()).isEmpty());
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ QVERIFY(manager.removeContact(foo.id().localId()) == false);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ QMap<int, QContactManager::Error> errorMap;
+ errorMap.insert(0, QContactManager::NoError);
+ QVERIFY(!manager.saveContacts(0, &errorMap));
+ QVERIFY(errorMap.count() == 0);
+ QVERIFY(manager.error() == QContactManager::BadArgumentError);
+
+ /* filters */
+ QContactFilter f; // matches everything
+ QContactDetailFilter df;
+ df.setDetailDefinitionName(QContactDisplayLabel::DefinitionName, QContactDisplayLabel::FieldLabel);
+ QVERIFY(manager.contactIds(QContactFilter()).count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ QVERIFY(manager.contactIds(df).count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ QVERIFY(manager.contactIds(f | f).count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ QVERIFY(manager.contactIds(df | df).count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ QVERIFY(manager.isFilterSupported(f) == false);
+ QVERIFY(manager.isFilterSupported(df) == false);
+
+ QList<QContact> list;
+ list << foo;
+
+ QVERIFY(!manager.saveContacts(&list, &errorMap));
+ QVERIFY(errorMap.count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ QVERIFY(!manager.removeContacts(QList<QContactLocalId>(), &errorMap));
+ QVERIFY(errorMap.count() == 0);
+ QVERIFY(manager.error() == QContactManager::BadArgumentError);
+
+ QList<QContactLocalId> idlist;
+ idlist << foo.id().localId();
+ QVERIFY(!manager.removeContacts(idlist, &errorMap));
+ QVERIFY(errorMap.count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+
+ /* Detail definitions */
+ QVERIFY(manager.detailDefinitions().count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+
+ QContactDetailDefinition def;
+ def.setUnique(true);
+ def.setName("new field");
+ QMap<QString, QContactDetailFieldDefinition> fields;
+ QContactDetailFieldDefinition currField;
+ currField.setDataType(QVariant::String);
+ fields.insert("value", currField);
+ def.setFields(fields);
+
+ QVERIFY(manager.saveDetailDefinition(def, QContactType::TypeContact) == false);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+ QVERIFY(manager.saveDetailDefinition(def) == false);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+ QVERIFY(manager.detailDefinitions().count(QContactType::TypeContact) == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+ QVERIFY(manager.detailDefinitions().count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+ QVERIFY(manager.detailDefinition("new field").name() == QString());
+ QVERIFY(manager.removeDetailDefinition(def.name(), QContactType::TypeContact) == false);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+ QVERIFY(manager.removeDetailDefinition(def.name()) == false);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+ QVERIFY(manager.detailDefinitions().count() == 0);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::InvalidContactTypeError);
+
+ /* Self contact id */
+ QVERIFY(!manager.setSelfContactId(QContactLocalId(12)));
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ QVERIFY(manager.selfContactId() == QContactLocalId());
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::DoesNotExistError);
+
+ /* Relationships */
+ QContactId idOne, idTwo;
+ idOne.setLocalId(QContactLocalId(15));
+ idOne.setManagerUri(QString());
+ idTwo.setLocalId(QContactLocalId(22));
+ idTwo.setManagerUri(QString());
+ QContactRelationship invalidRel;
+ invalidRel.setFirst(idOne);
+ invalidRel.setSecond(idTwo);
+ QList<QContactRelationship> invalidRelList;
+ invalidRelList << invalidRel;
+ QVERIFY(!manager.saveRelationship(&invalidRel));
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ QVERIFY(manager.relationships().isEmpty());
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ manager.saveRelationships(&invalidRelList, NULL);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError);
+ manager.removeRelationships(invalidRelList, NULL);
+ QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::DoesNotExistError);
+
+ /* Capabilities */
+ QVERIFY(manager.supportedDataTypes().count() == 0);
+ QVERIFY(!manager.hasFeature(QContactManager::ActionPreferences));
+ QVERIFY(!manager.hasFeature(QContactManager::MutableDefinitions));
+}
+
+void tst_QContactManager::memoryManager()
+{
+ QMap<QString, QString> params;
+ QContactManager m1("memory");
+ params.insert("random", "shouldNotBeUsed");
+ QContactManager m2("memory", params);
+ params.insert("id", "shouldBeUsed");
+ QContactManager m3("memory", params);
+ QContactManager m4("memory", params);
+ params.insert("id", QString(""));
+ QContactManager m5("memory", params); // should be another anonymous
+
+ QVERIFY(m1.hasFeature(QContactManager::ActionPreferences));
+ QVERIFY(m1.hasFeature(QContactManager::MutableDefinitions));
+ QVERIFY(m1.hasFeature(QContactManager::Anonymous));
+
+ // add a contact to each of m1, m2, m3
+ QContact c;
+ QContactName nc;
+ nc.setFirstName("John");
+ nc.setLastName("Civilian");
+ c.saveDetail(&nc);
+ m1.saveContact(&c);
+ c.setId(QContactId());
+ QContact c2;
+ QContactName nc2 = c2.detail(QContactName::DefinitionName);
+ c2 = c;
+ nc2.setMiddleName("Public");
+ c2.saveDetail(&nc2);
+ m2.saveContact(&c2); // save c2 first; c will be given a higher id
+ m2.saveContact(&c); // save c to m2
+ c.setId(QContactId());
+ nc.setSuffix("MD");
+ c.saveDetail(&nc);
+ m3.saveContact(&c);
+
+ /* test that m1 != m2 != m3 and that m3 == m4 */
+
+ // check the counts are correct - especially note m4 and m3.
+ QCOMPARE(m1.contactIds().count(), 1);
+ QCOMPARE(m2.contactIds().count(), 2);
+ QCOMPARE(m3.contactIds().count(), 1);
+ QCOMPARE(m4.contactIds().count(), 1);
+ QCOMPARE(m5.contactIds().count(), 0);
+
+ // remove c2 from m2 - ensure that this doesn't affect any other manager.
+ m2.removeContact(c2.id().localId());
+ QCOMPARE(m1.contactIds().count(), 1);
+ QCOMPARE(m2.contactIds().count(), 1);
+ QCOMPARE(m3.contactIds().count(), 1);
+ QCOMPARE(m4.contactIds().count(), 1);
+ QCOMPARE(m5.contactIds().count(), 0);
+
+ // check that the contacts contained within are different.
+ // note that in the m1->m2 case, only the id will be different!
+ QVERIFY(m1.contact(m1.contactIds().at(0)) != m2.contact(m2.contactIds().at(0)));
+ QVERIFY(m1.contact(m1.contactIds().at(0)) != m3.contact(m3.contactIds().at(0)));
+ QVERIFY(m2.contact(m2.contactIds().at(0)) != m3.contact(m3.contactIds().at(0)));
+ QVERIFY(m3.contact(m3.contactIds().at(0)) == m4.contact(m4.contactIds().at(0)));
+
+ // now, we should be able to remove from m4, and have m3 empty
+ QVERIFY(m4.removeContact(c.id().localId()));
+ QCOMPARE(m3.contactIds().count(), 0);
+ QCOMPARE(m4.contactIds().count(), 0);
+ QCOMPARE(m5.contactIds().count(), 0);
+}
+
+void tst_QContactManager::nameSynthesis_data()
+{
+ QTest::addColumn<QString>("expected");
+
+ QTest::addColumn<bool>("addname");
+ QTest::addColumn<QString>("prefix");
+ QTest::addColumn<QString>("first");
+ QTest::addColumn<QString>("middle");
+ QTest::addColumn<QString>("last");
+ QTest::addColumn<QString>("suffix");
+
+ QTest::addColumn<bool>("addcompany");
+ QTest::addColumn<QString>("company");
+
+ QTest::addColumn<bool>("addname2");
+ QTest::addColumn<QString>("secondprefix");
+ QTest::addColumn<QString>("secondfirst");
+ QTest::addColumn<QString>("secondmiddle");
+ QTest::addColumn<QString>("secondlast");
+ QTest::addColumn<QString>("secondsuffix");
+
+ QTest::addColumn<bool>("addcompany2");
+ QTest::addColumn<QString>("secondcompany");
+
+ QString e; // empty string.. gets a work out
+
+ /* Various empty ones */
+ QTest::newRow("empty contact") << e
+ << false << e << e << e << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("empty name") << e
+ << true << e << e << e << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("empty names") << e
+ << true << e << e << e << e << e
+ << false << e
+ << true << e << e << e << e << e
+ << false << e;
+ QTest::newRow("empty org") << e
+ << false << e << e << e << e << e
+ << true << e
+ << false << e << e << e << e << e
+ << true << e;
+ QTest::newRow("empty orgs") << e
+ << false << e << e << e << e << e
+ << true << e
+ << false << e << e << e << e << e
+ << true << e;
+ QTest::newRow("empty orgs and names") << e
+ << true << e << e << e << e << e
+ << true << e
+ << true << e << e << e << e << e
+ << true << e;
+
+ /* Single values */
+ QTest::newRow("prefix") << "Prefix"
+ << true << "Prefix" << e << e << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("first") << "First"
+ << true << e << "First" << e << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("middle") << "Middle"
+ << true << e << e << "Middle" << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("last") << "Last"
+ << true << e << e << e << "Last" << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("suffix") << "Suffix"
+ << true << e << e << e << e << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+
+ /* Single values in the second name */
+ QTest::newRow("prefix in second") << "Prefix"
+ << false << "Prefix" << e << e << e << e
+ << false << e
+ << true << "Prefix" << e << e << e << e
+ << false << e;
+ QTest::newRow("first in second") << "First"
+ << false << e << "First" << e << e << e
+ << false << e
+ << true << e << "First" << e << e << e
+ << false << e;
+ QTest::newRow("middle in second") << "Middle"
+ << false << e << e << "Middle" << e << e
+ << false << e
+ << true << e << e << "Middle" << e << e
+ << false << e;
+ QTest::newRow("last in second") << "Last"
+ << false << e << e << e << "Last" << e
+ << false << e
+ << true << e << e << e << "Last" << e
+ << false << e;
+ QTest::newRow("suffix in second") << "Suffix"
+ << false << e << e << e << e << "Suffix"
+ << false << e
+ << true << e << e << e << e << "Suffix"
+ << false << e;
+
+ /* Multiple name values */
+ QTest::newRow("prefix first") << "Prefix First"
+ << true << "Prefix" << "First" << e << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("prefix middle") << "Prefix Middle"
+ << true << "Prefix" << e << "Middle" << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("prefix last") << "Prefix Last"
+ << true << "Prefix" << e << e << "Last" << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("prefix suffix") << "Prefix Suffix"
+ << true << "Prefix" << e << e << e << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("first middle") << "First Middle"
+ << true << e << "First" << "Middle" << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("first last") << "First Last"
+ << true << e << "First" << e << "Last" << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("first suffix") << "First Suffix"
+ << true << e << "First" << e << e << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("middle last") << "Middle Last"
+ << true << e << e << "Middle" << "Last" << e
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("middle suffix") << "Middle Suffix"
+ << true << e << e << "Middle" << e << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("last suffix") << "Last Suffix"
+ << true << e << e << e << "Last" << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+
+ /* Everything.. */
+ QTest::newRow("all name") << "Prefix First Middle Last Suffix"
+ << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("all name second") << "Prefix First Middle Last Suffix"
+ << false << "Prefix" << "First" << "Middle" << "Last" << "Suffix"
+ << false << e
+ << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix"
+ << false << e;
+
+ /* Org */
+ QTest::newRow("org") << "Company"
+ << false << e << e << e << e << e
+ << true << "Company"
+ << false << e << e << e << e << e
+ << false << e;
+ QTest::newRow("second org") << "Company"
+ << false << e << e << e << e << e
+ << false << e
+ << false << e << e << e << e << e
+ << true << "Company";
+
+ /* Mix */
+ QTest::newRow("org and empty name") << "Company"
+ << true << e << e << e << e << e
+ << true << "Company"
+ << false << e << e << e << e << e
+ << false << e;
+
+ QTest::newRow("name and empty org") << "Prefix First Middle Last Suffix"
+ << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix"
+ << false << e
+ << false << e << e << e << e << e
+ << false << e;
+
+ /* names are preferred to orgs */
+ QTest::newRow("name and org") << "Prefix First Middle Last Suffix"
+ << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix"
+ << true << "Company"
+ << false << e << e << e << e << e
+ << false << e;
+
+}
+
+void tst_QContactManager::nameSynthesis()
+{
+ QContactManager cm("memory");
+
+ QFETCH(QString, expected);
+
+ QFETCH(QString, prefix);
+ QFETCH(QString, first);
+ QFETCH(QString, middle);
+ QFETCH(QString, last);
+ QFETCH(QString, suffix);
+ QFETCH(QString, company);
+
+ QFETCH(QString, secondprefix);
+ QFETCH(QString, secondfirst);
+ QFETCH(QString, secondmiddle);
+ QFETCH(QString, secondlast);
+ QFETCH(QString, secondsuffix);
+ QFETCH(QString, secondcompany);
+
+ QFETCH(bool, addname);
+ QFETCH(bool, addname2);
+ QFETCH(bool, addcompany);
+ QFETCH(bool, addcompany2);
+
+ /* Test the default name synthesis code */
+ QContact c;
+
+ QContactName name, name2;
+ QContactOrganization org, org2;
+
+ name.setPrefix(prefix);
+ name.setFirstName(first);
+ name.setMiddleName(middle);
+ name.setLastName(last);
+ name.setSuffix(suffix);
+
+ name2.setPrefix(secondprefix);
+ name2.setFirstName(secondfirst);
+ name2.setMiddleName(secondmiddle);
+ name2.setLastName(secondlast);
+ name2.setSuffix(secondsuffix);
+
+ org.setName(company);
+ org2.setName(secondcompany);
+
+ if (addname)
+ c.saveDetail(&name);
+ if (addname2)
+ c.saveDetail(&name2);
+ if (addcompany)
+ c.saveDetail(&org);
+ if (addcompany2)
+ c.saveDetail(&org2);
+
+ // Finally!
+ QCOMPARE(cm.synthesizedContactDisplayLabel(c), expected);
+}
+
+void tst_QContactManager::compatibleContact_data()
+{
+ QTest::addColumn<QContact>("input");
+ QTest::addColumn<QContact>("expected");
+ QTest::addColumn<QContactManager::Error>("error");
+
+ QContact baseContact;
+ QContactName name;
+ name.setFirstName(QLatin1String("First"));
+ baseContact.saveDetail(&name);
+
+ {
+ QTest::newRow("already compatible") << baseContact << baseContact << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactDetail detail("UnknownDetail");
+ detail.setValue("Key", QLatin1String("Value"));
+ contact.saveDetail(&detail);
+ QTest::newRow("unknown detail") << contact << baseContact << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactType type1;
+ type1.setType(QContactType::TypeContact);
+ contact.saveDetail(&type1);
+ QContactType type2;
+ type2.setType(QContactType::TypeGroup);
+ contact.saveDetail(&type2);
+ QContact expected(baseContact);
+ expected.saveDetail(&type2);
+ QTest::newRow("duplicate unique field") << contact << expected << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactPhoneNumber phoneNumber;
+ phoneNumber.setValue("UnknownKey", "Value");
+ contact.saveDetail(&phoneNumber);
+ QTest::newRow("unknown field") << contact << baseContact << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactDisplayLabel displayLabel;
+ displayLabel.setValue(QContactDisplayLabel::FieldLabel, QStringList("Value"));
+ contact.saveDetail(&displayLabel);
+ QTest::newRow("wrong type") << contact << baseContact << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactPhoneNumber phoneNumber1;
+ phoneNumber1.setNumber(QLatin1String("1234"));
+ phoneNumber1.setSubTypes(QStringList()
+ << QContactPhoneNumber::SubTypeMobile
+ << QContactPhoneNumber::SubTypeVoice
+ << QLatin1String("InvalidSubtype"));
+ contact.saveDetail(&phoneNumber1);
+ QContact expected(baseContact);
+ QContactPhoneNumber phoneNumber2;
+ phoneNumber2.setNumber(QLatin1String("1234"));
+ phoneNumber2.setSubTypes(QStringList()
+ << QContactPhoneNumber::SubTypeMobile
+ << QContactPhoneNumber::SubTypeVoice);
+ expected.saveDetail(&phoneNumber2);
+ QTest::newRow("bad value (list)") << contact << expected << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactPhoneNumber phoneNumber1;
+ phoneNumber1.setNumber(QLatin1String("1234"));
+ phoneNumber1.setSubTypes(QStringList(QLatin1String("InvalidSubtype")));
+ contact.saveDetail(&phoneNumber1);
+ QContact expected(baseContact);
+ QContactPhoneNumber phoneNumber2;
+ phoneNumber2.setNumber(QLatin1String("1234"));
+ expected.saveDetail(&phoneNumber2);
+ QTest::newRow("all bad value (list)") << contact << expected << QContactManager::NoError;
+ }
+
+ {
+ QContact contact(baseContact);
+ QContactGender gender;
+ gender.setGender(QLatin1String("UnknownGender"));
+ contact.saveDetail(&gender);
+ QTest::newRow("bad value (string)") << contact << baseContact << QContactManager::NoError;
+ }
+
+ {
+ QContact contact;
+ QContactGender gender;
+ gender.setGender(QLatin1String("UnknownGender"));
+ contact.saveDetail(&gender);
+ QTest::newRow("bad value (string)") << contact << QContact() << QContactManager::DoesNotExistError;
+ }
+}
+
+void tst_QContactManager::compatibleContact()
+{
+ QContactManager cm("memory");
+
+ QFETCH(QContact, input);
+ QFETCH(QContact, expected);
+ QFETCH(QContactManager::Error, error);
+ QCOMPARE(cm.compatibleContact(input), expected);
+ QCOMPARE(cm.error(), error);
+}
+
+void tst_QContactManager::contactValidation()
+{
+ /* Use the memory engine as a reference (validation is not engine specific) */
+ QScopedPointer<QContactManager> cm(new QContactManager("memory"));
+ QContact c;
+
+ /*
+ * Add some definitions for testing:
+ *
+ * 1) a unique detail
+ * 2) a detail with restricted values
+ * 3) a create only detail
+ * 4) a unique create only detail
+ */
+ QContactDetailDefinition uniqueDef;
+ QMap<QString, QContactDetailFieldDefinition> fields;
+ QContactDetailFieldDefinition field;
+ field.setDataType(QVariant::String);
+ fields.insert("value", field);
+
+ uniqueDef.setName("UniqueDetail");
+ uniqueDef.setFields(fields);
+ uniqueDef.setUnique(true);
+
+ QVERIFY(cm->saveDetailDefinition(uniqueDef));
+
+ QContactDetailDefinition restrictedDef;
+ restrictedDef.setName("RestrictedDetail");
+ fields.clear();
+ field.setAllowableValues(QVariantList() << "One" << "Two" << "Three");
+ fields.insert("value", field);
+ restrictedDef.setFields(fields);
+
+ QVERIFY(cm->saveDetailDefinition(restrictedDef));
+
+ // first, test an invalid definition
+ QContactDetail d1 = QContactDetail("UnknownDefinition");
+ d1.setValue("test", "test");
+ c.saveDetail(&d1);
+ QVERIFY(!cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::InvalidDetailError);
+ c.removeDetail(&d1);
+
+ // second, test an invalid uniqueness constraint
+ QContactDetail d2 = QContactDetail("UniqueDetail");
+ d2.setValue("value", "test");
+ c.saveDetail(&d2);
+
+ // One unique should be ok
+ QVERIFY(cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::NoError);
+
+ // Two uniques should not be ok
+ QContactDetail d3 = QContactDetail("UniqueDetail");
+ d3.setValue("value", "test2");
+ c.saveDetail(&d3);
+ QVERIFY(!cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::AlreadyExistsError);
+ c.removeDetail(&d3);
+ c.removeDetail(&d2);
+
+ // third, test an invalid field name
+ QContactDetail d4 = QContactDetail(QContactPhoneNumber::DefinitionName);
+ d4.setValue("test", "test");
+ c.saveDetail(&d4);
+ QVERIFY(!cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::InvalidDetailError);
+ c.removeDetail(&d4);
+
+ // fourth, test an invalid field data type
+ QContactDetail d5 = QContactDetail(QContactPhoneNumber::DefinitionName);
+ d5.setValue(QContactPhoneNumber::FieldNumber, QDateTime::currentDateTime());
+ c.saveDetail(&d5);
+ QVERIFY(!cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::InvalidDetailError);
+ c.removeDetail(&d5);
+
+ // fifth, test an invalid field value (not in the allowed list)
+ QContactDetail d6 = QContactDetail("RestrictedDetail");
+ d6.setValue("value", "Seven"); // not in One, Two or Three
+ c.saveDetail(&d6);
+ QVERIFY(!cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::InvalidDetailError);
+ c.removeDetail(&d6);
+
+ /* Now a valid value */
+ d6.setValue("value", "Two");
+ c.saveDetail(&d6);
+ QVERIFY(cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::NoError);
+ c.removeDetail(&d6);
+
+ // Test a completely valid one.
+ QContactDetail d7 = QContactDetail(QContactPhoneNumber::DefinitionName);
+ d7.setValue(QContactPhoneNumber::FieldNumber, "0123456");
+ c.saveDetail(&d7);
+ QVERIFY(cm->saveContact(&c));
+ QCOMPARE(cm->error(), QContactManager::NoError);
+ c.removeDetail(&d7);
+}
+
+void tst_QContactManager::signalEmission()
+{
+ QTest::qWait(500); // clear the signal queue
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> m1(QContactManager::fromUri(uri));
+
+ qRegisterMetaType<QContactLocalId>("QContactLocalId");
+ qRegisterMetaType<QList<QContactLocalId> >("QList<QContactLocalId>");
+ QSignalSpy spyCA(m1.data(), SIGNAL(contactsAdded(QList<QContactLocalId>)));
+ QSignalSpy spyCM(m1.data(), SIGNAL(contactsChanged(QList<QContactLocalId>)));
+ QSignalSpy spyCR(m1.data(), SIGNAL(contactsRemoved(QList<QContactLocalId>)));
+
+ QList<QVariant> args;
+ QList<QContactLocalId> arg;
+ QContact c;
+ QList<QContact> batchAdd;
+ QList<QContactLocalId> batchRemove;
+ QList<QContactLocalId> sigids;
+ int addSigCount = 0; // the expected signal counts.
+ int modSigCount = 0;
+ int remSigCount = 0;
+
+ QContactDetailDefinition nameDef = m1->detailDefinition(QContactName::DefinitionName, QContactType::TypeContact);
+
+ // verify add emits signal added
+ QContactName nc;
+ saveContactName(&c, nameDef, &nc, "John");
+ QVERIFY(m1->saveContact(&c));
+ QContactLocalId cid = c.id().localId();
+ addSigCount += 1;
+ QTRY_COMPARE(spyCA.count(), addSigCount);
+ args = spyCA.takeFirst();
+ addSigCount -= 1;
+ arg = args.first().value<QList<quint32> >();
+ QVERIFY(arg.count() == 1);
+ QCOMPARE(QContactLocalId(arg.at(0)), cid);
+
+ // verify save modified emits signal changed
+ saveContactName(&c, nameDef, &nc, "Citizen");
+ QVERIFY(m1->saveContact(&c));
+ modSigCount += 1;
+ QTRY_COMPARE(spyCM.count(), modSigCount);
+ args = spyCM.takeFirst();
+ modSigCount -= 1;
+ arg = args.first().value<QList<quint32> >();
+ QVERIFY(arg.count() == 1);
+ QCOMPARE(QContactLocalId(arg.at(0)), cid);
+
+ // verify remove emits signal removed
+ m1->removeContact(c.id().localId());
+ remSigCount += 1;
+ QTRY_COMPARE(spyCR.count(), remSigCount);
+ args = spyCR.takeFirst();
+ remSigCount -= 1;
+ arg = args.first().value<QList<quint32> >();
+ QVERIFY(arg.count() == 1);
+ QCOMPARE(QContactLocalId(arg.at(0)), cid);
+
+ // verify multiple adds works as advertised
+ QContact c2, c3;
+ QContactName nc2, nc3;
+ saveContactName(&c2, nameDef, &nc2, "Mark");
+ saveContactName(&c3, nameDef, &nc3, "Garry");
+#if defined(Q_OS_SYMBIAN)
+ // TODO: symbiansim backend fails this test currently. Commented out for
+ // now. Will be fixed later.
+ if(!uri.contains("symbiansim")) {
+ QVERIFY(!m1->saveContact(&c)); // saving contact with nonexistent id fails
+ }
+#endif
+ QVERIFY(m1->saveContact(&c2));
+ addSigCount += 1;
+ QVERIFY(m1->saveContact(&c3));
+ addSigCount += 1;
+ QTRY_COMPARE(spyCM.count(), modSigCount);
+ QTRY_COMPARE(spyCA.count(), addSigCount);
+
+ // verify multiple modifies works as advertised
+ saveContactName(&c2, nameDef, &nc2, "M.");
+ QVERIFY(m1->saveContact(&c2));
+ modSigCount += 1;
+ saveContactName(&c2, nameDef, &nc2, "Mark");
+ saveContactName(&c3, nameDef, &nc3, "G.");
+ QVERIFY(m1->saveContact(&c2));
+ modSigCount += 1;
+ QVERIFY(m1->saveContact(&c3));
+ modSigCount += 1;
+ QTRY_COMPARE(spyCM.count(), modSigCount);
+
+ // verify multiple removes works as advertised
+ m1->removeContact(c3.id().localId());
+ remSigCount += 1;
+ m1->removeContact(c2.id().localId());
+ remSigCount += 1;
+ QTRY_COMPARE(spyCR.count(), remSigCount);
+
+ QVERIFY(!m1->removeContact(c.id().localId())); // not saved.
+
+ /* Now test the batch equivalents */
+ spyCA.clear();
+ spyCM.clear();
+ spyCR.clear();
+
+ /* Batch adds - set ids to zero so add succeeds. */
+ c.setId(QContactId());
+ c2.setId(QContactId());
+ c3.setId(QContactId());
+ batchAdd << c << c2 << c3;
+ QMap<int, QContactManager::Error> errorMap;
+ QVERIFY(m1->saveContacts(&batchAdd, &errorMap));
+
+ QVERIFY(batchAdd.count() == 3);
+ c = batchAdd.at(0);
+ c2 = batchAdd.at(1);
+ c3 = batchAdd.at(2);
+
+ /* We basically loop, processing events, until we've seen an Add signal for each contact */
+ sigids.clear();
+
+ QTRY_WAIT( while(spyCA.size() > 0) {sigids += spyCA.takeFirst().at(0).value<QList<QContactLocalId> >(); }, sigids.contains(c.localId()) && sigids.contains(c2.localId()) && sigids.contains(c3.localId()));
+ QTRY_COMPARE(spyCM.count(), 0);
+ QTRY_COMPARE(spyCR.count(), 0);
+
+ /* Batch modifies */
+ QContactName modifiedName = c.detail(QContactName::DefinitionName);
+ saveContactName(&c, nameDef, &modifiedName, "Modified number 1");
+ modifiedName = c2.detail(QContactName::DefinitionName);
+ saveContactName(&c2, nameDef, &modifiedName, "Modified number 2");
+ modifiedName = c3.detail(QContactName::DefinitionName);
+ saveContactName(&c3, nameDef, &modifiedName, "Modified number 3");
+
+ batchAdd.clear();
+ batchAdd << c << c2 << c3;
+ QVERIFY(m1->saveContacts(&batchAdd, &errorMap));
+
+ sigids.clear();
+ QTRY_WAIT( while(spyCM.size() > 0) {sigids += spyCM.takeFirst().at(0).value<QList<QContactLocalId> >(); }, sigids.contains(c.localId()) && sigids.contains(c2.localId()) && sigids.contains(c3.localId()));
+
+ /* Batch removes */
+ batchRemove << c.id().localId() << c2.id().localId() << c3.id().localId();
+ QVERIFY(m1->removeContacts(batchRemove, &errorMap));
+
+ sigids.clear();
+ QTRY_WAIT( while(spyCR.size() > 0) {sigids += spyCR.takeFirst().at(0).value<QList<QContactLocalId> >(); }, sigids.contains(c.localId()) && sigids.contains(c2.localId()) && sigids.contains(c3.localId()));
+
+ QTRY_COMPARE(spyCA.count(), 0);
+ QTRY_COMPARE(spyCM.count(), 0);
+
+ QScopedPointer<QContactManager> m2(QContactManager::fromUri(uri));
+
+ // During construction SIM backend (m2) will try writing contacts with
+ // nickname, email and additional number to find out if the SIM card
+ // will support these fields. The other backend (m1) will then receive
+ // signals about that. These need to be caught so they don't interfere
+ // with the tests. (This trial and error method is used because existing
+ // API for checking the availability of these fields is not public.)
+ // NOTE: This applies only to pre 10.1 platforms (S60 3.1, 3.2, ect.)
+ if (uri.contains("symbiansim")) {
+ QTest::qWait(0);
+ spyCA.clear();
+ spyCM.clear();
+ spyCR.clear();
+ }
+
+ QVERIFY(m1->hasFeature(QContactManager::Anonymous) ==
+ m2->hasFeature(QContactManager::Anonymous));
+
+ /* Now some cross manager testing */
+ if (!m1->hasFeature(QContactManager::Anonymous)) {
+ // verify that signals are emitted for modifications made to other managers (same id).
+ QContactName ncs = c.detail(QContactName::DefinitionName);
+ saveContactName(&c, nameDef, &ncs, "Test");
+ c.setId(QContactId()); // reset id so save can succeed.
+ QVERIFY(m2->saveContact(&c));
+ saveContactName(&c, nameDef, &ncs, "Test2");
+ QVERIFY(m2->saveContact(&c));
+ QTRY_COMPARE(spyCA.count(), 1); // check that we received the update signals.
+ QTRY_COMPARE(spyCM.count(), 1); // check that we received the update signals.
+ m2->removeContact(c.localId());
+ QTRY_COMPARE(spyCR.count(), 1); // check that we received the remove signal.
+ }
+}
+
+void tst_QContactManager::errorStayingPut()
+{
+ /* Make sure that when we clone a manager, we don't clone the error */
+ QMap<QString, QString> params;
+ params.insert("id", "error isolation test");
+ QContactManager m1("memory",params);
+
+ QVERIFY(m1.error() == QContactManager::NoError);
+
+ /* Remove an invalid contact to get an error */
+ QVERIFY(m1.removeContact(0) == false);
+ QVERIFY(m1.error() == QContactManager::DoesNotExistError);
+
+ /* Create a new manager with hopefully the same backend */
+ QContactManager m2("memory", params);
+
+ QVERIFY(m1.error() == QContactManager::DoesNotExistError);
+ QVERIFY(m2.error() == QContactManager::NoError);
+
+ /* Cause an error on the other ones and check the first is not affected */
+ m2.saveContacts(0, 0);
+ QVERIFY(m1.error() == QContactManager::DoesNotExistError);
+ QVERIFY(m2.error() == QContactManager::BadArgumentError);
+
+ QContact c;
+ QContactDetail d("This does not exist and if it does this will break");
+ d.setValue("Value that also doesn't exist", 5);
+ c.saveDetail(&d);
+
+ QVERIFY(m1.saveContact(&c) == false);
+ QVERIFY(m1.error() == QContactManager::InvalidDetailError);
+ QVERIFY(m2.error() == QContactManager::BadArgumentError);
+}
+
+void tst_QContactManager::detailDefinitions()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+ QMap<QString, QContactDetailDefinition> defs = cm->detailDefinitions();
+
+ /* Validate the existing definitions */
+
+ // Do some sanity checking on the definitions first
+ if (defs.keys().count() != defs.uniqueKeys().count()) {
+ qDebug() << "ERROR - duplicate definitions with the same name:";
+
+ QList<QString> defkeys = defs.keys();
+ foreach(QString uniq, defs.uniqueKeys()) {
+ if (defkeys.count(uniq) > 1) {
+ qDebug() << QString(" %1").arg(uniq).toAscii().constData();
+ defkeys.removeAll(uniq);
+ }
+ }
+ QVERIFY(defs.keys().count() == defs.uniqueKeys().count());
+ }
+
+ foreach(QContactDetailDefinition def, defs.values()) {
+ QMap<QString, QContactDetailFieldDefinition> fields = def.fields();
+
+ // Again some sanity checking
+ if (fields.keys().count() != fields.uniqueKeys().count()) {
+ qDebug() << "ERROR - duplicate fields with the same name:";
+
+ QList<QString> defkeys = fields.keys();
+ foreach(QString uniq, fields.uniqueKeys()) {
+ if (defkeys.count(uniq) > 1) {
+ qDebug() << QString(" %2::%1").arg(uniq).arg(def.name()).toAscii().constData();
+ defkeys.removeAll(uniq);
+ }
+ }
+ QVERIFY(fields.keys().count() == fields.uniqueKeys().count());
+ }
+
+ foreach(QContactDetailFieldDefinition field, def.fields().values()) {
+ // Sanity check the allowed values
+ if (field.allowableValues().count() > 0) {
+ if (field.dataType() == QVariant::StringList) {
+ // We accept QString or QStringList allowed values
+ foreach(QVariant var, field.allowableValues()) {
+ if (var.type() != QVariant::String && var.type() != QVariant::StringList) {
+ QString foo;
+ QDebug dbg(&foo);
+ dbg.nospace() << var;
+ qDebug().nospace() << "Field " << QString("%1::%2").arg(def.name()).arg(def.fields().key(field)).toAscii().constData() << " allowable value '" << foo.simplified().toAscii().constData() << "' not supported for field type " << QMetaType::typeName(field.dataType());
+ }
+ QVERIFY(var.type() == QVariant::String || var.type() == QVariant::StringList);
+ }
+ } else if (field.dataType() == QVariant::List || field.dataType() == QVariant::Map || field.dataType() == (QVariant::Type) qMetaTypeId<QVariant>()) {
+ // Well, anything goes
+ } else {
+ // The type of each allowed value must match the data type
+ foreach(QVariant var, field.allowableValues()) {
+ if (var.type() != field.dataType()) {
+ QString foo;
+ QDebug dbg(&foo);
+ dbg.nospace() << var;
+ qDebug().nospace() << "Field " << QString("%1::%2").arg(def.name()).arg(def.fields().key(field)).toAscii().constData() << " allowable value '" << foo.simplified().toAscii().constData() << "' not supported for field type " << QMetaType::typeName(field.dataType());
+ }
+ QVERIFY(var.type() == field.dataType());
+ }
+ }
+ }
+ }
+ }
+
+
+ /* Try to make a credible definition */
+ QContactDetailDefinition newDef;
+ QContactDetailFieldDefinition field;
+ QMap<QString, QContactDetailFieldDefinition> fields;
+ field.setDataType(cm->supportedDataTypes().value(0));
+ fields.insert("New Value", field);
+ newDef.setName("New Definition");
+ newDef.setFields(fields);
+
+ /* Updated version of an existing definition */
+ QContactDetailDefinition updatedDef = defs.begin().value(); // XXX TODO Fixme
+ fields = updatedDef.fields();
+ fields.insert("New Value", field);
+ updatedDef.setFields(fields);
+
+ /* A detail definition with valid allowed values (or really just one) */
+ QContactDetailDefinition allowedDef = newDef;
+ field.setAllowableValues(field.allowableValues() << (QVariant(field.dataType())));
+ fields.clear();
+ fields.insert("Restricted value", field);
+ allowedDef.setFields(fields);
+
+ /* Many invalid definitions */
+ QContactDetailDefinition noIdDef;
+ noIdDef.setFields(fields);
+
+ QContactDetailDefinition noFieldsDef;
+ noFieldsDef.setName("No fields");
+
+ QContactDetailDefinition invalidFieldKeyDef;
+ invalidFieldKeyDef.setName("Invalid field key");
+ QMap<QString, QContactDetailFieldDefinition> badfields;
+ badfields.insert(QString(), field);
+ invalidFieldKeyDef.setFields(badfields);
+
+ QContactDetailDefinition invalidFieldTypeDef;
+ invalidFieldTypeDef.setName("Invalid field type");
+ badfields.clear();
+ QContactDetailFieldDefinition badfield;
+ badfield.setDataType((QVariant::Type) qMetaTypeId<UnsupportedMetatype>());
+ badfields.insert("Bad type", badfield);
+ invalidFieldTypeDef.setFields(badfields);
+
+ QContactDetailDefinition invalidAllowedValuesDef;
+ invalidAllowedValuesDef.setName("Invalid field allowed values");
+ badfields.clear();
+ badfield.setDataType(field.dataType()); // use a supported type
+ badfield.setAllowableValues(QList<QVariant>() << "String" << 5); // but unsupported value
+ badfields.insert("Bad allowed", badfield);
+ invalidAllowedValuesDef.setFields(badfields);
+
+ /* XXX Multiply defined fields.. depends on semantichangeSet. */
+
+ if (cm->hasFeature(QContactManager::MutableDefinitions)) {
+ /* First do some negative testing */
+
+ /* Bad add class */
+ QVERIFY(cm->saveDetailDefinition(QContactDetailDefinition()) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ /* Bad remove string */
+ QVERIFY(cm->removeDetailDefinition(QString()) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ QVERIFY(cm->saveDetailDefinition(noIdDef) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ QVERIFY(cm->saveDetailDefinition(noFieldsDef) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ QVERIFY(cm->saveDetailDefinition(invalidFieldKeyDef) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ QVERIFY(cm->saveDetailDefinition(invalidFieldTypeDef) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ QVERIFY(cm->saveDetailDefinition(invalidAllowedValuesDef) == false);
+ QVERIFY(cm->error() == QContactManager::BadArgumentError);
+
+ /* Check that our new definition doesn't already exist */
+ QVERIFY(cm->detailDefinition(newDef.name()).isEmpty());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+ QVERIFY(cm->removeDetailDefinition(newDef.name()) == false);
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+ /* Add a new definition */
+ QVERIFY(cm->saveDetailDefinition(newDef) == true);
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ /* Now retrieve it */
+ QContactDetailDefinition def = cm->detailDefinition(newDef.name());
+ QVERIFY(def == newDef);
+
+ /* Update it */
+ QMap<QString, QContactDetailFieldDefinition> newFields = def.fields();
+ newFields.insert("Another new value", field);
+ newDef.setFields(newFields);
+
+ QVERIFY(cm->saveDetailDefinition(newDef) == true);
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ QVERIFY(cm->detailDefinition(newDef.name()) == newDef);
+
+ /* Remove it */
+ QVERIFY(cm->removeDetailDefinition(newDef.name()) == true);
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ /* and make sure it does not exist any more */
+ QVERIFY(cm->detailDefinition(newDef.name()) == QContactDetailDefinition());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+ /* Add the other good one */
+ QVERIFY(cm->saveDetailDefinition(allowedDef) == true);
+ QVERIFY(cm->error() == QContactManager::NoError);
+
+ QVERIFY(allowedDef == cm->detailDefinition(allowedDef.name()));
+
+ /* and remove it */
+ QVERIFY(cm->removeDetailDefinition(allowedDef.name()) == true);
+ QVERIFY(cm->detailDefinition(allowedDef.name()) == QContactDetailDefinition());
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+
+ } else {
+ /* Bad add class */
+ QVERIFY(cm->saveDetailDefinition(QContactDetailDefinition()) == false);
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+
+ /* Make sure we can't add/remove/modify detail definitions */
+ QVERIFY(cm->removeDetailDefinition(QString()) == false);
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+
+ /* Try updating an existing definition */
+ QVERIFY(cm->saveDetailDefinition(updatedDef) == false);
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+
+ /* Try removing an existing definition */
+ QVERIFY(cm->removeDetailDefinition(updatedDef.name()) == false);
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ }
+}
+
+void tst_QContactManager::displayName()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ /*
+ * Very similar to the tst_QContact functions, except we test
+ * saving and retrieving contacts updates the display label
+ */
+
+ /* Try to make this a bit more consistent by using a single name */
+ QContact d;
+ QContactName name;
+ saveContactName(&d, cm->detailDefinition(QContactName::DefinitionName, QContactType::TypeContact), &name, "Wesley");
+
+ QVERIFY(d.displayLabel().isEmpty());
+
+ QString synth = cm->synthesizedContactDisplayLabel(d);
+
+ // Make sure this doesn't crash
+ cm->synthesizeContactDisplayLabel(0);
+
+ // Make sure this gives the same results
+ cm->synthesizeContactDisplayLabel(&d);
+ QCOMPARE(d.displayLabel(), synth);
+
+ /*
+ * The display label is not updated until you save the contact or call synthCDL
+ */
+ QVERIFY(cm->saveContact(&d));
+ d = cm->contact(d.id().localId());
+ QVERIFY(!d.isEmpty());
+ QCOMPARE(d.displayLabel(), synth);
+
+ /* Remove the detail via removeDetail */
+ QContactDisplayLabel old;
+ int count = d.details().count();
+ QVERIFY(!d.removeDetail(&old)); // should fail.
+ QVERIFY(d.isEmpty() == false);
+ QVERIFY(d.details().count() == count); // it should not be removed!
+
+ /* Save the contact again */
+ QVERIFY(cm->saveContact(&d));
+ d = cm->contact(d.id().localId());
+ QVERIFY(!d.isEmpty());
+
+ /* Make sure the label is still the synth version */
+ QCOMPARE(d.displayLabel(), synth);
+
+ /* And delete the contact */
+ QVERIFY(cm->removeContact(d.id().localId()));
+}
+
+void tst_QContactManager::actionPreferences()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ // early out if the manager doesn't support action preference saving.
+ if (!cm->hasFeature(QContactManager::ActionPreferences)) {
+ QSKIP("Manager does not support action preferences", SkipSingle);
+ }
+
+ // create a sample contact
+ QContactAvatar a;
+ a.setImageUrl(QUrl("test.png"));
+ QContactPhoneNumber p1;
+ p1.setNumber("12345");
+ QContactPhoneNumber p2;
+ p2.setNumber("34567");
+ QContactPhoneNumber p3;
+ p3.setNumber("56789");
+ QContactUrl u;
+ u.setUrl("http://test.nokia.com");
+ QContactName n;
+ QContact c;
+ saveContactName(&c, cm->detailDefinition(QContactName::DefinitionName, QContactType::TypeContact), &n, "TestContact");
+ c.saveDetail(&a);
+ c.saveDetail(&p1);
+ c.saveDetail(&p2);
+ c.saveDetail(&p3);
+ c.saveDetail(&u);
+
+ QVERIFY(cm->saveContact(&c)); // save the contact
+ QContact loaded = cm->contact(c.id().localId()); // reload the contact
+ cm->removeContact(c.id().localId());
+}
+
+void tst_QContactManager::changeSet()
+{
+ QContactLocalId id(1);
+
+ QContactChangeSet changeSet;
+ QVERIFY(changeSet.addedContacts().isEmpty());
+ QVERIFY(changeSet.changedContacts().isEmpty());
+ QVERIFY(changeSet.removedContacts().isEmpty());
+
+ changeSet.insertAddedContact(id);
+ QVERIFY(!changeSet.addedContacts().isEmpty());
+ QVERIFY(changeSet.changedContacts().isEmpty());
+ QVERIFY(changeSet.removedContacts().isEmpty());
+ QVERIFY(changeSet.addedContacts().contains(id));
+
+ changeSet.insertChangedContact(id);
+ changeSet.insertChangedContacts(QList<QContactLocalId>() << id);
+ QVERIFY(changeSet.changedContacts().size() == 1); // set, should only be added once.
+ QVERIFY(!changeSet.addedContacts().isEmpty());
+ QVERIFY(!changeSet.changedContacts().isEmpty());
+ QVERIFY(changeSet.removedContacts().isEmpty());
+ QVERIFY(changeSet.changedContacts().contains(id));
+ changeSet.clearChangedContacts();
+ QVERIFY(changeSet.changedContacts().isEmpty());
+
+ changeSet.insertRemovedContacts(QList<QContactLocalId>() << id);
+ QVERIFY(changeSet.removedContacts().contains(id));
+ changeSet.clearRemovedContacts();
+ QVERIFY(changeSet.removedContacts().isEmpty());
+
+ QVERIFY(changeSet.dataChanged() == false);
+ QContactChangeSet changeSet2;
+ changeSet2 = changeSet;
+ QVERIFY(changeSet.addedContacts() == changeSet2.addedContacts());
+ changeSet.emitSignals(0);
+
+ changeSet2.clearAddedContacts();
+ QVERIFY(changeSet2.addedContacts().isEmpty());
+ changeSet2.insertAddedContacts(changeSet.addedContacts().toList());
+ QVERIFY(changeSet.addedContacts() == changeSet2.addedContacts());
+
+ changeSet2.clearAll();
+ QVERIFY(changeSet.addedContacts() != changeSet2.addedContacts());
+
+ QContactChangeSet changeSet3(changeSet2);
+ QVERIFY(changeSet.addedContacts() != changeSet3.addedContacts());
+ QVERIFY(changeSet2.addedContacts() == changeSet3.addedContacts());
+
+ changeSet.setDataChanged(true);
+ QVERIFY(changeSet.dataChanged() == true);
+ QVERIFY(changeSet.dataChanged() != changeSet2.dataChanged());
+ QVERIFY(changeSet.dataChanged() != changeSet3.dataChanged());
+ changeSet.emitSignals(0);
+
+ changeSet.addedRelationshipsContacts().insert(id);
+ changeSet.insertAddedRelationshipsContacts(QList<QContactLocalId>() << id);
+ QVERIFY(changeSet.addedRelationshipsContacts().contains(id));
+ changeSet.clearAddedRelationshipsContacts();
+ QVERIFY(changeSet.addedRelationshipsContacts().isEmpty());
+ changeSet.insertRemovedRelationshipsContacts(QList<QContactLocalId>() << id);
+ QVERIFY(changeSet.removedRelationshipsContacts().contains(id));
+ changeSet.clearRemovedRelationshipsContacts();
+ QVERIFY(changeSet.removedRelationshipsContacts().isEmpty());
+ changeSet.emitSignals(0);
+ changeSet.removedRelationshipsContacts().insert(id);
+ changeSet.emitSignals(0);
+
+ changeSet.setOldAndNewSelfContactId(QPair<QContactLocalId, QContactLocalId>(QContactLocalId(0), id));
+ changeSet2 = changeSet;
+ QVERIFY(changeSet2.addedRelationshipsContacts() == changeSet.addedRelationshipsContacts());
+ QVERIFY(changeSet2.removedRelationshipsContacts() == changeSet.removedRelationshipsContacts());
+ QVERIFY(changeSet2.oldAndNewSelfContactId() == changeSet.oldAndNewSelfContactId());
+ changeSet.emitSignals(0);
+ changeSet.setOldAndNewSelfContactId(QPair<QContactLocalId, QContactLocalId>(id, QContactLocalId(0)));
+ QVERIFY(changeSet2.oldAndNewSelfContactId() != changeSet.oldAndNewSelfContactId());
+ changeSet.setDataChanged(true);
+ changeSet.emitSignals(0);
+}
+
+void tst_QContactManager::fetchHint()
+{
+ QContactFetchHint hint;
+ hint.setOptimizationHints(QContactFetchHint::NoBinaryBlobs);
+ QCOMPARE(hint.optimizationHints(), QContactFetchHint::NoBinaryBlobs);
+ QStringList rels;
+ rels << QString(QLatin1String(QContactRelationship::HasMember));
+ hint.setRelationshipTypesHint(rels);
+ QCOMPARE(hint.relationshipTypesHint(), rels);
+}
+
+void tst_QContactManager::selfContactId()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ // early out if the manager doesn't support self contact id saving
+ QContactLocalId selfContact = cm->selfContactId();
+ if (!cm->hasFeature(QContactManager::SelfContact)) {
+ // ensure that the error codes / return values are meaningful failures.
+ QEXPECT_FAIL("mgr='maemo5'", "maemo5 supports getting the self contact but not setting it.", Continue);
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError);
+ QVERIFY(!cm->setSelfContactId(QContactLocalId(123)));
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ QSKIP("Manager does not support the concept of a self-contact", SkipSingle);
+ }
+
+ // create a new "self" contact and retrieve its Id
+ QVERIFY(cm->error() == QContactManager::NoError || cm->error() == QContactManager::DoesNotExistError);
+ QContact self;
+ QContactPhoneNumber selfPhn;
+ selfPhn.setNumber("12345");
+ self.saveDetail(&selfPhn);
+ if (!cm->saveContact(&self)) {
+ QSKIP("Unable to save the generated self contact", SkipSingle);
+ }
+ QContactLocalId newSelfContact = self.localId();
+
+ // Setup signal spy
+ qRegisterMetaType<QContactLocalId>("QContactLocalId");
+ QSignalSpy spy(cm.data(), SIGNAL(selfContactIdChanged(QContactLocalId,QContactLocalId)));
+
+ // Set new self contact
+ QVERIFY(cm->setSelfContactId(newSelfContact));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ QTRY_VERIFY(spy.count() == 1);
+ QVERIFY(spy.at(0).count() == 2);
+ // note: for some reason qvariant_cast<QContactLocalId>(spy.at(0).at(0)) returns always zero
+ // because the type is not recognized. Hence the ugly casting below.
+ QVERIFY(*((const QContactLocalId*) spy.at(0).at(0).constData()) == selfContact);
+ QVERIFY(*((const QContactLocalId*) spy.at(0).at(1).constData()) == newSelfContact);
+ QVERIFY(cm->selfContactId() == newSelfContact);
+
+ // Remove self contact
+ if(!cm->removeContact(self.localId())) {
+ QSKIP("Unable to remove self contact", SkipSingle);
+ }
+ QTRY_VERIFY(spy.count() == 2);
+ QVERIFY(spy.at(1).count() == 2);
+ QVERIFY(*((const QContactLocalId*) spy.at(1).at(0).constData()) == newSelfContact);
+ QVERIFY(*((const QContactLocalId*) spy.at(1).at(1).constData()) == QContactLocalId(0));
+ QVERIFY(cm->selfContactId() == QContactLocalId(0)); // ensure reset after removed.
+
+ // reset to original state.
+ cm->setSelfContactId(selfContact);
+}
+
+QList<QContactDetail> tst_QContactManager::removeAllDefaultDetails(const QList<QContactDetail>& details)
+{
+ QList<QContactDetail> newlist;
+ foreach (const QContactDetail d, details) {
+ if (d.definitionName() != QContactDisplayLabel::DefinitionName
+ && d.definitionName() != QContactType::DefinitionName
+ && d.definitionName() != QContactTimestamp::DefinitionName) {
+ newlist << d;
+ }
+ }
+ return newlist;
+}
+
+void tst_QContactManager::detailOrders()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ if (!cm->hasFeature(QContactManager::DetailOrdering))
+ QSKIP("Skipping: This manager does not support detail ordering!", SkipSingle);
+
+ QContact a;
+ //phone numbers
+
+ QContactDetailDefinition d = cm->detailDefinition(QContactPhoneNumber::DefinitionName, QContactType::TypeContact);
+ QContactDetailFieldDefinition supportedContexts = d.fields().value(QContactDetail::FieldContext);
+ QString contextOther = QContactDetail::ContextOther;
+ if (!supportedContexts.allowableValues().contains(contextOther)) {
+ contextOther = QString();
+ }
+
+ QContactPhoneNumber number1, number2, number3;
+
+ number1.setNumber("11111111");
+ number1.setContexts(QContactPhoneNumber::ContextHome);
+
+ number2.setNumber("22222222");
+ number2.setContexts(QContactPhoneNumber::ContextWork);
+
+ number3.setNumber("33333333");
+ if (!contextOther.isEmpty())
+ number3.setContexts(contextOther);
+
+ a.saveDetail(&number1);
+ a.saveDetail(&number2);
+ a.saveDetail(&number3);
+
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+
+ QList<QContactDetail> details = a.details(QContactPhoneNumber::DefinitionName);
+ QVERIFY(details.count() == 3);
+ QVERIFY(details.at(0).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextHome);
+ QVERIFY(details.at(1).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextWork);
+ QVERIFY(details.at(2).value(QContactPhoneNumber::FieldContext) == contextOther);
+
+ QVERIFY(a.removeDetail(&number2));
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+ details = a.details(QContactPhoneNumber::DefinitionName);
+ QVERIFY(details.count() == 2);
+ QVERIFY(details.at(0).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextHome);
+ QVERIFY(details.at(1).value(QContactPhoneNumber::FieldContext) == contextOther);
+
+ a.saveDetail(&number2);
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+
+ details = a.details(QContactPhoneNumber::DefinitionName);
+ QVERIFY(details.count() == 3);
+ QVERIFY(details.at(0).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextHome);
+ QVERIFY(details.at(1).value(QContactPhoneNumber::FieldContext) == contextOther);
+ QVERIFY(details.at(2).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextWork);
+
+ //addresses
+
+ d = cm->detailDefinition(QContactAddress::DefinitionName, QContactType::TypeContact);
+ supportedContexts = d.fields().value(QContactDetail::FieldContext);
+ contextOther = QString(QLatin1String(QContactDetail::ContextOther));
+ if (!supportedContexts.allowableValues().contains(contextOther)) {
+ contextOther = QString();
+ }
+
+ QContactAddress address1, address2, address3;
+
+ address1.setStreet("Brandl St");
+ address1.setRegion("Brisbane");
+ address3 = address2 = address1;
+
+ address1.setContexts(QContactAddress::ContextHome);
+ address2.setContexts(QContactAddress::ContextWork);
+ if (!contextOther.isEmpty())
+ address3.setContexts(contextOther);
+
+ a.saveDetail(&address1);
+ a.saveDetail(&address2);
+ a.saveDetail(&address3);
+
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+
+ details = a.details(QContactAddress::DefinitionName);
+ QVERIFY(details.count() == 3);
+
+ QVERIFY(details.at(0).value(QContactAddress::FieldContext) == QContactAddress::ContextHome);
+ QVERIFY(details.at(1).value(QContactAddress::FieldContext) == QContactAddress::ContextWork);
+ QVERIFY(details.at(2).value(QContactAddress::FieldContext) == contextOther);
+
+ QVERIFY(a.removeDetail(&address2));
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+ details = a.details(QContactAddress::DefinitionName);
+ QVERIFY(details.count() == 2);
+ QVERIFY(details.at(0).value(QContactAddress::FieldContext) == QContactAddress::ContextHome);
+ QVERIFY(details.at(1).value(QContactAddress::FieldContext) == contextOther);
+
+ a.saveDetail(&address2);
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+
+ details = a.details(QContactAddress::DefinitionName);
+ QVERIFY(details.count() == 3);
+ QVERIFY(details.at(0).value(QContactAddress::FieldContext) == QContactAddress::ContextHome);
+ QVERIFY(details.at(1).value(QContactAddress::FieldContext) == contextOther);
+ QVERIFY(details.at(2).value(QContactAddress::FieldContext) == QContactAddress::ContextWork);
+
+
+ //emails
+ d = cm->detailDefinition(QContactEmailAddress::DefinitionName, QContactType::TypeContact);
+ supportedContexts = d.fields().value(QContactDetail::FieldContext);
+ contextOther = QString(QLatin1String(QContactDetail::ContextOther));
+ if (!supportedContexts.allowableValues().contains(contextOther)) {
+ contextOther = QString();
+ }
+
+ QContactEmailAddress email1, email2, email3;
+
+ email1.setEmailAddress("aaron@example.com");
+ email3 = email2 = email1;
+ email1.setContexts(QContactEmailAddress::ContextHome);
+ email2.setContexts(QContactEmailAddress::ContextWork);
+ if (!contextOther.isEmpty())
+ email3.setContexts(contextOther);
+
+ a.saveDetail(&email1);
+ a.saveDetail(&email2);
+ a.saveDetail(&email3);
+
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+
+ details = a.details(QContactEmailAddress::DefinitionName);
+ QVERIFY(details.count() == 3);
+
+ QVERIFY(details.at(0).value(QContactEmailAddress::FieldContext) == QContactEmailAddress::ContextHome);
+ QVERIFY(details.at(1).value(QContactEmailAddress::FieldContext) == QContactEmailAddress::ContextWork);
+ QVERIFY(details.at(2).value(QContactEmailAddress::FieldContext) == contextOther);
+
+ QVERIFY(a.removeDetail(&email2));
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+ details = a.details(QContactEmailAddress::DefinitionName);
+ QVERIFY(details.count() == 2);
+ QVERIFY(details.at(0).value(QContactEmailAddress::FieldContext) == QContactEmailAddress::ContextHome);
+ QVERIFY(details.at(1).value(QContactEmailAddress::FieldContext) == contextOther);
+
+ a.saveDetail(&email2);
+ QVERIFY(cm->saveContact(&a));
+ a = cm->contact(a.id().localId());
+
+ details = a.details(QContactEmailAddress::DefinitionName);
+ QVERIFY(details.count() == 3);
+ QVERIFY(details.at(0).value(QContactEmailAddress::FieldContext) == QContactEmailAddress::ContextHome);
+ QVERIFY(details.at(1).value(QContactEmailAddress::FieldContext) == contextOther);
+ QVERIFY(details.at(2).value(QContactEmailAddress::FieldContext) == QContactEmailAddress::ContextWork);
+
+ QVERIFY(cm->removeContact(a.id().localId()));
+ QVERIFY(cm->error() == QContactManager::NoError);
+}
+
+void tst_QContactManager::relationships()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ // save some contacts
+ QContact source;
+ QContact dest1, dest2, dest3, dest4;
+ QContactPhoneNumber n1, n2, n3, n4;
+ n1.setNumber("1");
+ n2.setNumber("2");
+ n3.setNumber("3");
+ n4.setNumber("4");
+
+ dest1.saveDetail(&n1);
+ dest2.saveDetail(&n2);
+ dest3.saveDetail(&n3);
+ dest4.saveDetail(&n3);
+
+ cm->saveContact(&source);
+ cm->saveContact(&dest1);
+ cm->saveContact(&dest2);
+ cm->saveContact(&dest3);
+ cm->saveContact(&dest4);
+
+ // check if manager supports relationships
+ if (!cm->hasFeature(QContactManager::Relationships)) {
+ // ensure that the operations all fail as required.
+ QContactRelationship r1, r2, r3;
+ r1.setFirst(source.id());
+ r1.setSecond(dest1.id());
+ r1.setRelationshipType(QContactRelationship::HasManager);
+ r2.setFirst(source.id());
+ r2.setSecond(dest2.id());
+ r2.setRelationshipType(QContactRelationship::HasManager);
+ r3.setFirst(source.id());
+ r3.setSecond(dest3.id());
+ r3.setRelationshipType(QContactRelationship::HasManager);
+
+ QList<QContactRelationship> batchList;
+ batchList << r2 << r3;
+
+ // test save and remove
+ QVERIFY(!cm->saveRelationship(&r1));
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ QVERIFY(!cm->removeRelationship(r1));
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ cm->saveRelationships(&batchList, NULL);
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+
+ // test retrieval
+ QList<QContactRelationship> retrieveList;
+ retrieveList = cm->relationships(source.id(), QContactRelationship::First);
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ retrieveList = cm->relationships(source.id(), QContactRelationship::Second);
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ retrieveList = cm->relationships(source.id(), QContactRelationship::Either); // Either
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+
+
+ retrieveList = cm->relationships(QContactRelationship::HasManager, source.id(), QContactRelationship::First);
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ retrieveList = cm->relationships(QContactRelationship::HasManager, source.id(), QContactRelationship::Second);
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ retrieveList = cm->relationships(QContactRelationship::HasManager, source.id(), QContactRelationship::Either);
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ retrieveList = cm->relationships(QContactRelationship::HasManager, source.id());
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ retrieveList = cm->relationships(QContactRelationship::HasManager);
+ QVERIFY(retrieveList.isEmpty());
+ QVERIFY(cm->error() == QContactManager::NotSupportedError);
+ return;
+ }
+
+ // Get supported relationship types
+ QStringList availableRelationshipTypes;
+ if (cm->isRelationshipTypeSupported(QContactRelationship::HasMember))
+ availableRelationshipTypes << QContactRelationship::HasMember;
+ if (cm->isRelationshipTypeSupported(QContactRelationship::HasAssistant))
+ availableRelationshipTypes << QContactRelationship::HasAssistant;
+ if (cm->isRelationshipTypeSupported(QContactRelationship::HasManager))
+ availableRelationshipTypes << QContactRelationship::HasManager;
+ if (cm->isRelationshipTypeSupported(QContactRelationship::HasSpouse))
+ availableRelationshipTypes << QContactRelationship::HasSpouse;
+ if (cm->isRelationshipTypeSupported(QContactRelationship::IsSameAs))
+ availableRelationshipTypes << QContactRelationship::IsSameAs;
+
+
+ // Check arbitary relationship support
+ if (cm->hasFeature(QContactManager::ArbitraryRelationshipTypes)) {
+ // add some arbitary type for testing
+ if (availableRelationshipTypes.count())
+ availableRelationshipTypes.insert(0, "test-arbitrary-relationship-type");
+ else {
+ availableRelationshipTypes.append("test-arbitrary-relationship-type");
+ availableRelationshipTypes.append(QContactRelationship::HasMember);
+ availableRelationshipTypes.append(QContactRelationship::HasAssistant);
+ }
+ }
+
+ // Verify that we have relationship types. If there are none then the manager
+ // is saying it supports relationships but does not actually implement any
+ // relationship type.
+ QVERIFY(!availableRelationshipTypes.isEmpty());
+
+ // Some backends (eg. symbian) require that when type is "HasMember"
+ // then "first" contact must be a group.
+ if (availableRelationshipTypes.at(0) == QContactRelationship::HasMember) {
+ cm->removeContact(source.localId());
+ source.setId(QContactId());
+ source.setType(QContactType::TypeGroup);
+ cm->saveContact(&source);
+ }
+
+ // Create some common contact id's for testing
+ QContactId dest1Uri = dest1.id();
+ QContactId dest1EmptyUri;
+ dest1EmptyUri.setManagerUri(QString());
+ dest1EmptyUri.setLocalId(dest1.id().localId());
+ QContactId dest2Uri = dest2.id();
+ QContactId dest3Uri = dest3.id();
+ QContactId dest3EmptyUri;
+ dest3EmptyUri.setManagerUri(QString());
+ dest3EmptyUri.setLocalId(dest3.id().localId());
+
+ // build our relationship - source is the manager all of the dest contacts.
+ QContactRelationship customRelationshipOne;
+ customRelationshipOne.setFirst(source.id());
+ customRelationshipOne.setSecond(dest1EmptyUri);
+ customRelationshipOne.setRelationshipType(availableRelationshipTypes.at(0));
+ QCOMPARE(customRelationshipOne.first(), source.id());
+ QCOMPARE(customRelationshipOne.second(), dest1EmptyUri);
+ QVERIFY(customRelationshipOne.relationshipType() == availableRelationshipTypes.at(0));
+
+ // save the relationship
+ int managerRelationshipsCount = cm->relationships(availableRelationshipTypes.at(0)).count();
+ QVERIFY(cm->saveRelationship(&customRelationshipOne));
+ // test that the empty manager URI has been updated.
+ QCOMPARE(customRelationshipOne.second(), dest1Uri); // updated with correct manager URI
+
+ // test our accessors.
+ QCOMPARE(cm->relationships(availableRelationshipTypes.at(0)).count(), (managerRelationshipsCount + 1));
+ QVERIFY(cm->relationships(availableRelationshipTypes.at(0), source.id()).count() == 1);
+
+ // remove the dest1 contact, relationship should be removed.
+ cm->removeContact(dest1.localId());
+ QCOMPARE(cm->relationships(availableRelationshipTypes.at(0), dest1Uri, QContactRelationship::Second).count(), 0);
+
+ // modify and save the relationship
+ customRelationshipOne.setSecond(dest2Uri);
+ QVERIFY(cm->saveRelationship(&customRelationshipOne));
+
+ // attempt to save the relationship again. XXX TODO: what should the result be? currently succeeds (overwrites)
+ int relationshipsCount = cm->relationships().count();
+ QVERIFY(cm->saveRelationship(&customRelationshipOne)); // succeeds, but just overwrites
+ QCOMPARE(relationshipsCount, cm->relationships().count()); // shouldn't change; save should have overwritten.
+
+ // removing the source contact should result in removal of the relationship.
+ QVERIFY(cm->removeContact(source.id().localId()));
+ QCOMPARE(cm->relationships().count(), relationshipsCount - 1); // the relationship should have been removed.
+
+ // now ensure that qcontact relationship caching works as required - perhaps this should be in tst_QContact?
+ source.setId(QContactId()); // reset id so we can resave
+ QVERIFY(cm->saveContact(&source)); // save source again.
+ customRelationshipOne.setFirst(source.id());
+ customRelationshipOne.setSecond(dest2.id());
+ QVERIFY(cm->saveRelationship(&customRelationshipOne));
+
+ // Add a second relationship
+ QContactRelationship customRelationshipTwo;
+ customRelationshipTwo.setFirst(source.id());
+ if (availableRelationshipTypes.count() > 1)
+ customRelationshipTwo.setRelationshipType(availableRelationshipTypes.at(1));
+ else
+ customRelationshipTwo.setRelationshipType(availableRelationshipTypes.at(0));
+ customRelationshipTwo.setSecond(dest3.id());
+ QVERIFY(cm->saveRelationship(&customRelationshipTwo));
+
+ // currently, the contacts are "stale" - no cached relationships
+ QVERIFY(dest3.relatedContacts().isEmpty());
+ QVERIFY(dest3.relationships().isEmpty());
+ QVERIFY(dest2.relatedContacts().isEmpty());
+ QVERIFY(dest2.relationships().isEmpty());
+
+ // now refresh the contacts
+ dest3 = cm->contact(dest3.localId());
+ dest2 = cm->contact(dest2.localId());
+ source = cm->contact(source.localId());
+
+ // and test again.
+ QVERIFY(source.relatedContacts(QString(), QContactRelationship::First).isEmpty()); // source is always the first, so this should be empty.
+ QVERIFY(source.relatedContacts(QString(), QContactRelationship::Second).contains(dest2.id()));
+ QVERIFY(source.relatedContacts(QString(), QContactRelationship::Either).contains(dest2.id()));
+ QVERIFY(source.relatedContacts(QString(), QContactRelationship::Second).contains(dest3.id()));
+ QVERIFY(source.relatedContacts(QString(), QContactRelationship::Either).contains(dest3.id()));
+ QVERIFY(source.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).contains(dest2.id()));
+ QVERIFY(source.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::First).isEmpty());
+
+ QVERIFY(dest2.relatedContacts().contains(source.id()));
+ QVERIFY(dest2.relationships().contains(customRelationshipOne));
+ QVERIFY(!dest2.relationships().contains(customRelationshipTwo));
+ QVERIFY(dest2.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipOne));
+ QVERIFY(!dest2.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipTwo));
+ QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(0)).contains(source.id()));
+ QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::First).contains(source.id()));
+ QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).isEmpty());
+ QVERIFY(!dest2.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).contains(source.id()));
+
+ QVERIFY(dest3.relatedContacts().contains(source.id()));
+ QVERIFY(!dest3.relationships().contains(customRelationshipOne));
+ QVERIFY(dest3.relationships().contains(customRelationshipTwo));
+ QVERIFY(!dest3.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipOne));
+
+ // Test iteration
+ QList<QContactRelationship> relats = source.relationships();
+ QList<QContactRelationship>::iterator it = relats.begin();
+
+ while (it != relats.end()) {
+ QContactId firstId = it->first();
+ QVERIFY(firstId == source.id());
+ QVERIFY(it->second() == dest2.id() || it->second() == dest3.id());
+ it++;
+ }
+
+ if (availableRelationshipTypes.count() > 1) {
+ QVERIFY(source.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::Second).contains(dest3.id()));
+ QVERIFY(source.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::First).isEmpty());
+
+ QVERIFY(dest2.relationships(availableRelationshipTypes.at(1)).isEmpty());
+
+ QVERIFY(!dest3.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipTwo));
+ QVERIFY(dest3.relationships(availableRelationshipTypes.at(1)).contains(customRelationshipTwo));
+ QVERIFY(!dest3.relationships(availableRelationshipTypes.at(1)).contains(customRelationshipOne));
+ QVERIFY(dest3.relatedContacts(availableRelationshipTypes.at(1)).contains(source.id()));
+ QVERIFY(!dest3.relatedContacts(availableRelationshipTypes.at(0)).contains(source.id()));
+ QVERIFY(dest3.relatedContacts(availableRelationshipTypes.at(1)).contains(source.id())); // role = either
+ QVERIFY(!dest3.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::Second).contains(source.id()));
+ QVERIFY(dest3.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::First).contains(source.id()));
+ QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(1)).isEmpty());
+ }
+ else {
+ QVERIFY(source.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).contains(dest3.id()));
+ }
+
+ // Cleanup a bit
+ QMap<int, QContactManager::Error> errorMap;
+ QList<QContactRelationship> moreRels;
+
+ moreRels << customRelationshipOne << customRelationshipTwo;
+ errorMap.insert(5, QContactManager::BadArgumentError);
+ QVERIFY(cm->removeRelationships(moreRels, &errorMap));
+ QVERIFY(errorMap.count() == 0);
+
+ // test batch API and ordering in contacts
+ QList<QContactRelationship> currentRelationships = cm->relationships(source.id(), QContactRelationship::First);
+ QList<QContactRelationship> batchList;
+ QContactRelationship br1, br2, br3;
+ br1.setFirst(source.id());
+ br1.setSecond(dest2.id());
+ br1.setRelationshipType(availableRelationshipTypes.at(0));
+ br2.setFirst(source.id());
+ br2.setSecond(dest3.id());
+ br2.setRelationshipType(availableRelationshipTypes.at(0));
+ if (availableRelationshipTypes.count() > 1)
+ {
+ br3.setFirst(source.id());
+ br3.setSecond(dest3.id());
+ br3.setRelationshipType(availableRelationshipTypes.at(1));
+ }
+ else
+ {
+ br3.setFirst(source.id());
+ br3.setSecond(dest4.id());
+ br3.setRelationshipType(availableRelationshipTypes.at(0));
+ }
+ batchList << br1 << br2 << br3;
+
+ // ensure that the batch save works properly
+ cm->saveRelationships(&batchList, NULL);
+ QCOMPARE(cm->error(), QContactManager::NoError);
+ QList<QContactRelationship> batchRetrieve = cm->relationships(source.id(), QContactRelationship::First);
+ QVERIFY(batchRetrieve.contains(br1));
+ QVERIFY(batchRetrieve.contains(br2));
+ QVERIFY(batchRetrieve.contains(br3));
+
+ // remove a single relationship
+ QVERIFY(cm->removeRelationship(br3));
+ batchRetrieve = cm->relationships(source.id(), QContactRelationship::First);
+ QVERIFY(batchRetrieve.contains(br1));
+ QVERIFY(batchRetrieve.contains(br2));
+ QVERIFY(!batchRetrieve.contains(br3)); // has already been removed.
+
+ // now ensure that the batch remove works and we get returned to the original state.
+ batchList.removeOne(br3);
+ cm->removeRelationships(batchList, NULL);
+ QVERIFY(cm->error() == QContactManager::NoError);
+ QCOMPARE(cm->relationships(source.id(), QContactRelationship::First), currentRelationships);
+
+ // attempt to save relationships between an existing source but non-existent destination
+ QContactId nonexistentDest;
+ quint32 idSeed = 0x5544;
+ QContactLocalId nonexistentLocalId = QContactLocalId(idSeed);
+ nonexistentDest.setManagerUri(cm->managerUri());
+ while (true) {
+ nonexistentLocalId = cm->contact(nonexistentLocalId).localId();
+ if (nonexistentLocalId == QContactLocalId(0)) {
+ // found a "spare" local id (no contact with that id)
+ break;
+ }
+
+ // keep looking...
+ idSeed += 1;
+ nonexistentLocalId = QContactLocalId(idSeed);
+ QVERIFY(nonexistentLocalId != QContactLocalId(0)); // integer overflow check.
+ }
+ nonexistentDest.setLocalId(nonexistentLocalId);
+ QContactRelationship maliciousRel;
+ maliciousRel.setFirst(source.id());
+ maliciousRel.setSecond(nonexistentDest);
+ maliciousRel.setRelationshipType("nokia-test-invalid-relationship-type");
+ QVERIFY(!cm->saveRelationship(&maliciousRel));
+
+ // attempt to save a circular relationship - should fail!
+ maliciousRel.setFirst(source.id());
+ maliciousRel.setSecond(source.id());
+ maliciousRel.setRelationshipType(availableRelationshipTypes.at(0));
+ QVERIFY(!cm->saveRelationship(&maliciousRel));
+
+ // more negative testing, but force manager to recognise the empty URI
+ QContactId circularId = source.id();
+ circularId.setManagerUri(QString());
+ maliciousRel.setFirst(circularId);
+ maliciousRel.setSecond(circularId);
+ maliciousRel.setRelationshipType(availableRelationshipTypes.at(0));
+ QVERIFY(!cm->saveRelationship(&maliciousRel));
+ maliciousRel.setFirst(source.id());
+ maliciousRel.setSecond(circularId);
+ maliciousRel.setRelationshipType(availableRelationshipTypes.at(0));
+ QVERIFY(!cm->saveRelationship(&maliciousRel));
+ maliciousRel.setFirst(circularId);
+ maliciousRel.setSecond(source.id());
+ maliciousRel.setRelationshipType(availableRelationshipTypes.at(0));
+ QVERIFY(!cm->saveRelationship(&maliciousRel));
+
+ // attempt to save a relationship where the source contact comes from another manager
+ circularId.setManagerUri("test-nokia-invalid-manager-uri");
+ maliciousRel.setFirst(circularId); // an invalid source contact
+ maliciousRel.setSecond(dest2.id()); // a valid destination contact
+ maliciousRel.setRelationshipType(availableRelationshipTypes.at(0));
+ QVERIFY(!cm->saveRelationship(&maliciousRel));
+
+ // remove the nonexistent relationship
+ relationshipsCount = cm->relationships().count();
+ QVERIFY(!cm->removeRelationship(maliciousRel)); // does not exist; fail remove.
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError || cm->error() == QContactManager::InvalidRelationshipError);
+ QCOMPARE(cm->relationships().count(), relationshipsCount); // should be unchanged.
+
+ // now we want to ensure that a relationship is removed if one of the contacts is removed.
+ customRelationshipOne.setFirst(source.id());
+ customRelationshipOne.setSecond(dest2.id());
+ customRelationshipOne.setRelationshipType(availableRelationshipTypes.at(0));
+
+ // Test batch save with an error map
+ moreRels.clear();
+ moreRels << customRelationshipOne;
+ errorMap.insert(0, QContactManager::BadArgumentError);
+ QVERIFY(cm->saveRelationships(&moreRels, &errorMap));
+ QVERIFY(cm->error() == QContactManager::NoError);
+ QVERIFY(errorMap.count() == 0); // should be reset
+ source = cm->contact(source.localId());
+ dest2 = cm->contact(dest2.localId());
+ QVERIFY(cm->removeContact(dest2.localId())); // remove dest2, the relationship should be removed
+ QVERIFY(cm->relationships(availableRelationshipTypes.at(0), dest2.id(), QContactRelationship::Second).isEmpty());
+ source = cm->contact(source.localId());
+ QVERIFY(!source.relatedContacts().contains(dest2.id())); // and it shouldn't appear in cache.
+
+ // now clean up and remove our dests.
+ QVERIFY(cm->removeContact(source.localId()));
+ QVERIFY(cm->removeContact(dest3.localId()));
+
+ // attempt to save relationships with nonexistent contacts
+ QVERIFY(!cm->saveRelationship(&br1));
+ QVERIFY(cm->error() == QContactManager::InvalidRelationshipError);
+ cm->saveRelationships(&batchList, NULL);
+ QVERIFY(cm->error() == QContactManager::InvalidRelationshipError);
+ QVERIFY(!cm->removeRelationship(br1));
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError || cm->error() == QContactManager::InvalidRelationshipError);
+ cm->removeRelationships(batchList, NULL);
+ QVERIFY(cm->error() == QContactManager::DoesNotExistError || cm->error() == QContactManager::InvalidRelationshipError);
+}
+
+void tst_QContactManager::contactType()
+{
+ QFETCH(QString, uri);
+ QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+ if (!cm->hasFeature(QContactManager::Groups))
+ QSKIP("Skipping: This manager does not support group contacts!", SkipSingle);
+
+ QContact g1, g2, c;
+ g1.setType(QContactType::TypeGroup);
+ g2.setType(QContactType::TypeGroup);
+
+ QContactPhoneNumber g1p, g2p, cp;
+ g1p.setNumber("22222");
+ g2p.setNumber("11111");
+ cp.setNumber("33333");
+
+ g1.saveDetail(&g1p);
+ g2.saveDetail(&g2p);
+ c.saveDetail(&cp);
+
+ QVERIFY(cm->saveContact(&g1));
+ QVERIFY(cm->saveContact(&g2));
+ QVERIFY(cm->saveContact(&c));
+
+ // test that the accessing by type works properly
+ QContactDetailFilter groupFilter;
+ groupFilter.setDetailDefinitionName(QContactType::DefinitionName, QContactType::FieldType);
+ groupFilter.setValue(QString(QLatin1String(QContactType::TypeGroup)));
+ QVERIFY(cm->contactIds(groupFilter).contains(g1.localId()));
+ QVERIFY(cm->contactIds(groupFilter).contains(g2.localId()));
+ QVERIFY(!cm->contactIds(groupFilter).contains(c.localId()));
+
+ QList<QContactSortOrder> sortOrders;
+ QContactSortOrder byPhoneNumber;
+ byPhoneNumber.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
+ sortOrders.append(byPhoneNumber);
+
+ // and ensure that sorting works properly with typed contacts also
+ QList<QContactLocalId> sortedIds = cm->contactIds(groupFilter, sortOrders);
+ QVERIFY(sortedIds.indexOf(g2.localId()) < sortedIds.indexOf(g1.localId()));
+
+ cm->removeContact(g1.localId());
+ cm->removeContact(g2.localId());
+ cm->removeContact(c.localId());
+}
+
+QTEST_MAIN(tst_QContactManager)
+#include "tst_qcontactmanager.moc"