qtmobility/tests/auto/qcontactasync/unittest/tst_qcontactasync.cpp
changeset 1 2b40d63a9c3d
child 4 90517678cc4f
equal deleted inserted replaced
0:cfcbf08528c4 1:2b40d63a9c3d
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QtTest/QtTest>
       
    43 
       
    44 #include <QCoreApplication>
       
    45 #include <QScopedPointer>
       
    46 
       
    47 #include "qtcontacts.h"
       
    48 #include "qcontactmanagerdataholder.h" //QContactManagerDataHolder
       
    49 
       
    50 QTM_USE_NAMESPACE
       
    51 /* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */
       
    52 #define FILL_QUEUE_WITH_FETCH_REQUESTS() QContactFetchRequest fqcfr1, fqcfr2, fqcfr3; \
       
    53                                          QContactDetailDefinitionFetchRequest fqdfr1, fqdfr2, fqdfr3; \
       
    54                                          fqcfr1.start(); \
       
    55                                          fqcfr2.start(); \
       
    56                                          fqcfr3.start(); \
       
    57                                          fqdfr1.start(); \
       
    58                                          fqdfr2.start(); \
       
    59                                          fqdfr3.start();
       
    60 
       
    61 
       
    62 //TESTED_CLASS=
       
    63 //TESTED_FILES=
       
    64 
       
    65 // Unfortunately the plumbing isn't in place to allow cancelling requests at arbitrary points
       
    66 // in their processing.  So we do multiple loops until things work out.. or not
       
    67 #define MAX_OPTIMISTIC_SCHEDULING_LIMIT 100
       
    68 
       
    69 
       
    70 // Thread capable QThreadSignalSpy (to avoid data races with count/appendArgS)
       
    71 class QThreadSignalSpy: public QObject
       
    72 {
       
    73 public:
       
    74     QThreadSignalSpy(QObject *obj, const char *aSignal)
       
    75     {
       
    76 #ifdef Q_CC_BOR
       
    77         const int memberOffset = QObject::staticMetaObject.methodCount();
       
    78 #else
       
    79         static const int memberOffset = QObject::staticMetaObject.methodCount();
       
    80 #endif
       
    81         Q_ASSERT(obj);
       
    82         Q_ASSERT(aSignal);
       
    83 
       
    84         if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
       
    85             qWarning("QThreadSignalSpy: Not a valid signal, use the SIGNAL macro");
       
    86             return;
       
    87         }
       
    88 
       
    89         QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
       
    90         const QMetaObject *mo = obj->metaObject();
       
    91         int sigIndex = mo->indexOfMethod(ba.constData());
       
    92         if (sigIndex < 0) {
       
    93             qWarning("QThreadSignalSpy: No such signal: '%s'", ba.constData());
       
    94             return;
       
    95         }
       
    96 
       
    97         if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
       
    98                     Qt::DirectConnection, 0)) {
       
    99             qWarning("QThreadSignalSpy: QMetaObject::connect returned false. Unable to connect.");
       
   100             return;
       
   101         }
       
   102         sig = ba;
       
   103         initArgs(mo->method(sigIndex));
       
   104     }
       
   105 
       
   106     inline bool isValid() const { return !sig.isEmpty(); }
       
   107     inline QByteArray signal() const { return sig; }
       
   108 
       
   109     int qt_metacall(QMetaObject::Call call, int methodId, void **a)
       
   110     {
       
   111         methodId = QObject::qt_metacall(call, methodId, a);
       
   112         if (methodId < 0)
       
   113             return methodId;
       
   114 
       
   115         if (call == QMetaObject::InvokeMetaMethod) {
       
   116             if (methodId == 0) {
       
   117                 appendArgs(a);
       
   118             }
       
   119             --methodId;
       
   120         }
       
   121         return methodId;
       
   122     }
       
   123 
       
   124     // The QList<QVariantList> API we actually use
       
   125     int count() const
       
   126     {
       
   127         QMutexLocker m(&lock);
       
   128         return savedArgs.count();
       
   129     }
       
   130     void clear()
       
   131     {
       
   132         QMutexLocker m(&lock);
       
   133         savedArgs.clear();
       
   134     }
       
   135 
       
   136 private:
       
   137     void initArgs(const QMetaMethod &member)
       
   138     {
       
   139         QMutexLocker m(&lock);
       
   140         QList<QByteArray> params = member.parameterTypes();
       
   141         for (int i = 0; i < params.count(); ++i) {
       
   142             int tp = QMetaType::type(params.at(i).constData());
       
   143             if (tp == QMetaType::Void)
       
   144                 qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
       
   145                          params.at(i).constData());
       
   146             args << tp;
       
   147         }
       
   148     }
       
   149 
       
   150     void appendArgs(void **a)
       
   151     {
       
   152         QMutexLocker m(&lock);
       
   153         QList<QVariant> list;
       
   154         for (int i = 0; i < args.count(); ++i) {
       
   155             QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
       
   156             list << QVariant(type, a[i + 1]);
       
   157         }
       
   158         savedArgs.append(list);
       
   159     }
       
   160 
       
   161     // the full, normalized signal name
       
   162     QByteArray sig;
       
   163     // holds the QMetaType types for the argument list of the signal
       
   164     QList<int> args;
       
   165 
       
   166     mutable QMutex lock;
       
   167     // Different API
       
   168     QList< QVariantList> savedArgs;
       
   169 };
       
   170 
       
   171 class tst_QContactAsync : public QObject
       
   172 {
       
   173     Q_OBJECT
       
   174 
       
   175 public:
       
   176     tst_QContactAsync();
       
   177     virtual ~tst_QContactAsync();
       
   178 
       
   179 public slots:
       
   180     void init();
       
   181     void cleanup();
       
   182 
       
   183 private:
       
   184     void addManagers(); // add standard managers to the data
       
   185 
       
   186 private slots:
       
   187     void testDestructor();
       
   188     void testDestructor_data() { addManagers(); }
       
   189 
       
   190     void contactFetch();
       
   191     void contactFetch_data() { addManagers(); }
       
   192     void contactIdFetch();
       
   193     void contactIdFetch_data() { addManagers(); }
       
   194     void contactRemove();
       
   195     void contactRemove_data() { addManagers(); }
       
   196     void contactSave();
       
   197     void contactSave_data() { addManagers(); }
       
   198 
       
   199     void definitionFetch();
       
   200     void definitionFetch_data() { addManagers(); }
       
   201     void definitionRemove();
       
   202     void definitionRemove_data() { addManagers(); }
       
   203     void definitionSave();
       
   204     void definitionSave_data() { addManagers(); }
       
   205 
       
   206     void relationshipFetch();
       
   207     void relationshipFetch_data() { addManagers(); }
       
   208     void relationshipRemove();
       
   209     void relationshipRemove_data() { addManagers(); }
       
   210     void relationshipSave();
       
   211     void relationshipSave_data() { addManagers(); }
       
   212 
       
   213     void maliciousManager(); // uses it's own custom data (manager)
       
   214 
       
   215     void testQuickDestruction();
       
   216     void testQuickDestruction_data() { addManagers(); }
       
   217 
       
   218     void threadDelivery();
       
   219     void progressReceived(QContactFetchRequest* request, bool appendOnly);
       
   220     void threadDelivery_data() { addManagers(); }
       
   221 
       
   222 private:
       
   223     bool containsIgnoringTimestamps(const QList<QContact>& list, const QContact& c);
       
   224     bool compareIgnoringTimestamps(const QContact& ca, const QContact& cb);
       
   225     QContactManager* prepareModel(const QString& uri);
       
   226 
       
   227     Qt::HANDLE m_mainThreadId;
       
   228     Qt::HANDLE m_progressSlotThreadId;
       
   229     QContactManagerDataHolder managerDataHolder;
       
   230 };
       
   231 
       
   232 tst_QContactAsync::tst_QContactAsync()
       
   233 {
       
   234     // ensure we can load all of the plugins we need to.
       
   235     QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins";
       
   236     QApplication::addLibraryPath(path);
       
   237 
       
   238     qRegisterMetaType<QContactAbstractRequest::State>("QContactAbstractRequest::State");
       
   239 
       
   240 }
       
   241 
       
   242 tst_QContactAsync::~tst_QContactAsync()
       
   243 {
       
   244     QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins";
       
   245     QApplication::removeLibraryPath(path);
       
   246 }
       
   247 
       
   248 void tst_QContactAsync::init()
       
   249 {
       
   250 }
       
   251 
       
   252 void tst_QContactAsync::cleanup()
       
   253 {
       
   254 }
       
   255 
       
   256 bool tst_QContactAsync::containsIgnoringTimestamps(const QList<QContact>& list, const QContact& c)
       
   257 {
       
   258     QList<QContact> cl = list;
       
   259     QContact a(c);
       
   260     for (int i = 0; i < cl.size(); i++) {
       
   261         QContact b(cl.at(i));
       
   262         if (compareIgnoringTimestamps(a, b))
       
   263             return true;
       
   264     }
       
   265 
       
   266     return false;
       
   267 }
       
   268 
       
   269 bool tst_QContactAsync::compareIgnoringTimestamps(const QContact& ca, const QContact& cb)
       
   270 {
       
   271     // Compares two contacts, ignoring any timestamp details
       
   272     QContact a(ca);
       
   273     QContact b(cb);
       
   274     QList<QContactDetail> aDetails = a.details();
       
   275     QList<QContactDetail> bDetails = b.details();
       
   276 
       
   277     // They can be in any order, so loop
       
   278     // First remove any matches, and any timestamps
       
   279     foreach (QContactDetail d, aDetails) {
       
   280         foreach (QContactDetail d2, bDetails) {
       
   281             if (d == d2) {
       
   282                 a.removeDetail(&d);
       
   283                 b.removeDetail(&d2);
       
   284                 break;
       
   285             }
       
   286 
       
   287             if (d.definitionName() == QContactTimestamp::DefinitionName) {
       
   288                 a.removeDetail(&d);
       
   289             }
       
   290 
       
   291             if (d2.definitionName() == QContactTimestamp::DefinitionName) {
       
   292                 b.removeDetail(&d2);
       
   293             }
       
   294         }
       
   295     }
       
   296 
       
   297     if (a == b)
       
   298         return true;
       
   299     return false;
       
   300 }
       
   301 
       
   302 void tst_QContactAsync::testDestructor()
       
   303 {
       
   304     QFETCH(QString, uri);
       
   305     QContactManager* cm = prepareModel(uri);
       
   306     QContactFetchRequest* req = new QContactFetchRequest;
       
   307     req->setManager(cm);
       
   308 
       
   309     QContactManager* cm2 = prepareModel(uri);
       
   310     QContactFetchRequest* req2 = new QContactFetchRequest;
       
   311     req2->setManager(cm2);
       
   312 
       
   313     // first, delete manager then request
       
   314     delete cm;
       
   315     delete req;
       
   316 
       
   317     // second, delete request then manager
       
   318     delete req2;
       
   319     delete cm2;
       
   320 }
       
   321 
       
   322 void tst_QContactAsync::contactFetch()
       
   323 {
       
   324     QFETCH(QString, uri);
       
   325     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
   326 
       
   327     QContactFetchRequest cfr;
       
   328     QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchRequest);
       
   329 
       
   330     // initial state - not started, no manager.
       
   331     QVERIFY(!cfr.isActive());
       
   332     QVERIFY(!cfr.isFinished());
       
   333     QVERIFY(!cfr.start());
       
   334     QVERIFY(!cfr.cancel());
       
   335     QVERIFY(!cfr.waitForFinished());
       
   336 
       
   337     // "all contacts" retrieval
       
   338     QContactFilter fil;
       
   339     cfr.setManager(cm.data());
       
   340     QCOMPARE(cfr.manager(), cm.data());
       
   341     QVERIFY(!cfr.isActive());
       
   342     QVERIFY(!cfr.isFinished());
       
   343     QVERIFY(!cfr.cancel());
       
   344     QVERIFY(!cfr.waitForFinished());
       
   345     qRegisterMetaType<QContactFetchRequest*>("QContactFetchRequest*");
       
   346     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
   347     cfr.setFilter(fil);
       
   348     QCOMPARE(cfr.filter(), fil);
       
   349     QVERIFY(!cfr.cancel()); // not started
       
   350 
       
   351     QVERIFY(cfr.start());
       
   352     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   353     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   354     QVERIFY(cfr.waitForFinished());
       
   355     QVERIFY(cfr.isFinished());
       
   356 
       
   357     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   358     spy.clear();
       
   359 
       
   360     QList<QContactLocalId> contactIds = cm->contactIds();
       
   361     QList<QContact> contacts = cfr.contacts();
       
   362     QCOMPARE(contactIds.size(), contacts.size());
       
   363     for (int i = 0; i < contactIds.size(); i++) {
       
   364         QContact curr = cm->contact(contactIds.at(i));
       
   365         QVERIFY(contacts.at(i) == curr);
       
   366     }
       
   367 
       
   368     // asynchronous detail filtering
       
   369     QContactDetailFilter dfil;
       
   370     dfil.setDetailDefinitionName(QContactUrl::DefinitionName, QContactUrl::FieldUrl);
       
   371     cfr.setFilter(dfil);
       
   372     QVERIFY(cfr.filter() == dfil);
       
   373     QVERIFY(!cfr.cancel()); // not started
       
   374 
       
   375     QVERIFY(cfr.start());
       
   376     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   377     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   378     QVERIFY(cfr.waitForFinished());
       
   379     QVERIFY(cfr.isFinished());
       
   380     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   381     spy.clear();
       
   382 
       
   383     contactIds = cm->contactIds(dfil);
       
   384     contacts = cfr.contacts();
       
   385     QCOMPARE(contactIds.size(), contacts.size());
       
   386     for (int i = 0; i < contactIds.size(); i++) {
       
   387         QContact curr = cm->contact(contactIds.at(i));
       
   388         QVERIFY(contacts.at(i) == curr);
       
   389     }
       
   390 
       
   391     // sort order
       
   392     QContactSortOrder sortOrder;
       
   393     sortOrder.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
       
   394     QList<QContactSortOrder> sorting;
       
   395     sorting.append(sortOrder);
       
   396     cfr.setFilter(fil);
       
   397     cfr.setSorting(sorting);
       
   398     QCOMPARE(cfr.sorting(), sorting);
       
   399     QVERIFY(!cfr.cancel()); // not started
       
   400     QVERIFY(cfr.start());
       
   401     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   402     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   403     QVERIFY(cfr.waitForFinished());
       
   404     QVERIFY(cfr.isFinished());
       
   405 
       
   406     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   407     spy.clear();
       
   408 
       
   409     contactIds = cm->contactIds(sorting);
       
   410     contacts = cfr.contacts();
       
   411     QCOMPARE(contactIds.size(), contacts.size());
       
   412     for (int i = 0; i < contactIds.size(); i++) {
       
   413         QContact curr = cm->contact(contactIds.at(i));
       
   414         QVERIFY(contacts.at(i) == curr);
       
   415     }
       
   416 
       
   417     // restrictions
       
   418     sorting.clear();
       
   419     cfr.setFilter(fil);
       
   420     cfr.setSorting(sorting);
       
   421     cfr.setDefinitionRestrictions(QStringList(QContactName::DefinitionName));
       
   422     QCOMPARE(cfr.definitionRestrictions(), QStringList(QContactName::DefinitionName));
       
   423     QVERIFY(!cfr.cancel()); // not started
       
   424     QVERIFY(cfr.start());
       
   425     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   426     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   427     QVERIFY(cfr.waitForFinished());
       
   428     QVERIFY(cfr.isFinished());
       
   429 
       
   430     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   431     spy.clear();
       
   432 
       
   433     contactIds = cm->contactIds(sorting);
       
   434     contacts = cfr.contacts();
       
   435     QCOMPARE(contactIds.size(), contacts.size());
       
   436     for (int i = 0; i < contactIds.size(); i++) {
       
   437         // create a contact from the restricted data only (id + display label)
       
   438         QContact currFull = cm->contact(contactIds.at(i));
       
   439         QContact currRestricted;
       
   440         currRestricted.setId(currFull.id());
       
   441         QList<QContactName> names = currFull.details<QContactName>();
       
   442         foreach (const QContactName& name, names) {
       
   443             QContactName fullName = name;
       
   444             if (!fullName.isEmpty()) {
       
   445                 currRestricted.saveDetail(&fullName);
       
   446             }
       
   447         }
       
   448 
       
   449         // now find the contact in the retrieved list which our restricted contact mimics
       
   450         QContact retrievedRestricted;
       
   451         bool found = false;
       
   452         foreach (const QContact& retrieved, contacts) {
       
   453             if (retrieved.id() == currRestricted.id()) {
       
   454                 retrievedRestricted = retrieved;
       
   455                 found = true;
       
   456             }
       
   457         }
       
   458 
       
   459         QVERIFY(found); // must exist or fail.
       
   460 
       
   461         // ensure that the contact is the same (except synth fields)
       
   462         QList<QContactDetail> retrievedDetails = retrievedRestricted.details();
       
   463         QList<QContactDetail> expectedDetails = currRestricted.details();
       
   464         foreach (const QContactDetail& det, expectedDetails) {
       
   465             // ignore backend synthesised details
       
   466             // again, this requires a "default contact details" function to work properly.
       
   467             if (det.definitionName() == QContactDisplayLabel::DefinitionName
       
   468                 || det.definitionName() == QContactTimestamp::DefinitionName) {
       
   469                 continue;
       
   470             }
       
   471 
       
   472             // everything else in the expected contact should be in the retrieved one.
       
   473             QVERIFY(retrievedDetails.contains(det));
       
   474         }
       
   475     }
       
   476 
       
   477     // cancelling
       
   478     sorting.clear();
       
   479     cfr.setFilter(fil);
       
   480     cfr.setSorting(sorting);
       
   481     cfr.setDefinitionRestrictions(QStringList());
       
   482 
       
   483     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
   484     while (true) {
       
   485         QVERIFY(!cfr.cancel()); // not started
       
   486         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   487         QVERIFY(cfr.start());
       
   488         if (!cfr.cancel()) {
       
   489             // due to thread scheduling, async cancel might be attempted
       
   490             // after the request has already finished.. so loop and try again.
       
   491             spy.clear();
       
   492             cfr.waitForFinished();
       
   493             sorting.clear();
       
   494             cfr.setFilter(fil);
       
   495             cfr.setSorting(sorting);
       
   496             cfr.setDefinitionRestrictions(QStringList());
       
   497             bailoutCount -= 1;
       
   498             if (!bailoutCount) {
       
   499                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   500                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   501                 break;
       
   502             }
       
   503             continue;
       
   504         }
       
   505 
       
   506         // if we get here, then we are cancelling the request.
       
   507         QVERIFY(cfr.waitForFinished());
       
   508         QVERIFY(cfr.isCanceled());
       
   509 
       
   510         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   511         spy.clear();
       
   512         break;
       
   513     }
       
   514 
       
   515     // restart, and wait for progress after cancel.
       
   516     while (true) {
       
   517         QVERIFY(!cfr.cancel()); // not started
       
   518         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   519         QVERIFY(cfr.start());
       
   520         if (!cfr.cancel()) {
       
   521             // due to thread scheduling, async cancel might be attempted
       
   522             // after the request has already finished.. so loop and try again.
       
   523             cfr.waitForFinished();
       
   524             sorting.clear();
       
   525             cfr.setFilter(fil);
       
   526             cfr.setSorting(sorting);
       
   527             cfr.setDefinitionRestrictions(QStringList());
       
   528             bailoutCount -= 1;
       
   529             spy.clear();
       
   530             if (!bailoutCount) {
       
   531                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   532                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   533                 break;
       
   534             }
       
   535             continue;
       
   536         }
       
   537         cfr.waitForFinished();
       
   538         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   539         spy.clear();
       
   540         QVERIFY(!cfr.isActive());
       
   541         QVERIFY(cfr.state() == QContactAbstractRequest::CanceledState);
       
   542         break;
       
   543     }
       
   544 
       
   545 }
       
   546 
       
   547 void tst_QContactAsync::contactIdFetch()
       
   548 {
       
   549     QFETCH(QString, uri);
       
   550     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
   551     QContactLocalIdFetchRequest cfr;
       
   552     QVERIFY(cfr.type() == QContactAbstractRequest::ContactLocalIdFetchRequest);
       
   553 
       
   554     // initial state - not started, no manager.
       
   555     QVERIFY(!cfr.isActive());
       
   556     QVERIFY(!cfr.isFinished());
       
   557     QVERIFY(!cfr.start());
       
   558     QVERIFY(!cfr.cancel());
       
   559     QVERIFY(!cfr.waitForFinished());
       
   560 
       
   561     // "all contacts" retrieval
       
   562     QContactFilter fil;
       
   563     cfr.setManager(cm.data());
       
   564     QCOMPARE(cfr.manager(), cm.data());
       
   565     QVERIFY(!cfr.isActive());
       
   566     QVERIFY(!cfr.isFinished());
       
   567     QVERIFY(!cfr.cancel());
       
   568     QVERIFY(!cfr.waitForFinished());
       
   569     qRegisterMetaType<QContactLocalIdFetchRequest*>("QContactLocalIdFetchRequest*");
       
   570 
       
   571     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
   572     cfr.setFilter(fil);
       
   573     QCOMPARE(cfr.filter(), fil);
       
   574     QVERIFY(!cfr.cancel()); // not started
       
   575     QVERIFY(cfr.start());
       
   576 
       
   577     QVERIFY((cfr.isActive() &&cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   578     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   579     QVERIFY(cfr.waitForFinished());
       
   580     QVERIFY(cfr.isFinished());
       
   581 
       
   582     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   583     spy.clear();
       
   584 
       
   585     QList<QContactLocalId> contactIds = cm->contactIds();
       
   586     QList<QContactLocalId> result = cfr.ids();
       
   587     QCOMPARE(contactIds, result);
       
   588 
       
   589     // asynchronous detail filtering
       
   590     QContactDetailFilter dfil;
       
   591     dfil.setDetailDefinitionName(QContactUrl::DefinitionName, QContactUrl::FieldUrl);
       
   592     cfr.setFilter(dfil);
       
   593     QVERIFY(cfr.filter() == dfil);
       
   594     QVERIFY(!cfr.cancel()); // not started
       
   595 
       
   596     QVERIFY(cfr.start());
       
   597     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   598     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   599     QVERIFY(cfr.waitForFinished());
       
   600     QVERIFY(cfr.isFinished());
       
   601 
       
   602     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   603     spy.clear();
       
   604 
       
   605     contactIds = cm->contactIds(dfil);
       
   606     result = cfr.ids();
       
   607     QCOMPARE(contactIds, result);
       
   608 
       
   609     // sort order
       
   610     QContactSortOrder sortOrder;
       
   611     sortOrder.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
       
   612     QList<QContactSortOrder> sorting;
       
   613     sorting.append(sortOrder);
       
   614     cfr.setFilter(fil);
       
   615     cfr.setSorting(sorting);
       
   616     QCOMPARE(cfr.sorting(), sorting);
       
   617     QVERIFY(!cfr.cancel()); // not started
       
   618     QVERIFY(cfr.start());
       
   619     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
       
   620     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
       
   621     QVERIFY(cfr.waitForFinished());
       
   622     QVERIFY(cfr.isFinished());
       
   623 
       
   624     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   625     spy.clear();
       
   626 
       
   627     contactIds = cm->contactIds(sorting);
       
   628     result = cfr.ids();
       
   629     QCOMPARE(contactIds, result);
       
   630 
       
   631     // cancelling
       
   632     sorting.clear();
       
   633     cfr.setFilter(fil);
       
   634     cfr.setSorting(sorting);
       
   635 
       
   636     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
   637     while (true) {
       
   638         QVERIFY(!cfr.cancel()); // not started
       
   639         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   640         QVERIFY(cfr.start());
       
   641         if (!cfr.cancel()) {
       
   642             // due to thread scheduling, async cancel might be attempted
       
   643             // after the request has already finished.. so loop and try again.
       
   644             cfr.waitForFinished();
       
   645             sorting.clear();
       
   646             cfr.setFilter(fil);
       
   647             cfr.setSorting(sorting);
       
   648             bailoutCount -= 1;
       
   649             spy.clear();
       
   650             if (!bailoutCount) {
       
   651                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   652                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   653                 break;
       
   654             }
       
   655             continue;
       
   656         }
       
   657 
       
   658         // if we get here, then we are cancelling the request.
       
   659         QVERIFY(cfr.waitForFinished());
       
   660         QVERIFY(cfr.isCanceled());
       
   661 
       
   662         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   663         spy.clear();
       
   664 
       
   665         break;
       
   666     }
       
   667 
       
   668     // restart, and wait for progress after cancel.
       
   669     while (true) {
       
   670         QVERIFY(!cfr.cancel()); // not started
       
   671         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   672         QVERIFY(cfr.start());
       
   673         if (!cfr.cancel()) {
       
   674             // due to thread scheduling, async cancel might be attempted
       
   675             // after the request has already finished.. so loop and try again.
       
   676             cfr.waitForFinished();
       
   677             sorting.clear();
       
   678             cfr.setFilter(fil);
       
   679             cfr.setSorting(sorting);
       
   680             bailoutCount -= 1;
       
   681             if (!bailoutCount) {
       
   682                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   683                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   684                 break;
       
   685             }
       
   686             continue;
       
   687         }
       
   688         cfr.waitForFinished();
       
   689         QVERIFY(cfr.isCanceled());
       
   690 
       
   691         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   692         spy.clear();
       
   693         break;
       
   694     }
       
   695 
       
   696 }
       
   697 
       
   698 void tst_QContactAsync::contactRemove()
       
   699 {
       
   700     QFETCH(QString, uri);
       
   701     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
   702     QContactRemoveRequest crr;
       
   703     QVERIFY(crr.type() == QContactAbstractRequest::ContactRemoveRequest);
       
   704 
       
   705     // initial state - not started, no manager.
       
   706     QVERIFY(!crr.isActive());
       
   707     QVERIFY(!crr.isFinished());
       
   708     QVERIFY(!crr.start());
       
   709     QVERIFY(!crr.cancel());
       
   710     QVERIFY(!crr.waitForFinished());
       
   711 
       
   712     // specific contact removal via detail filter
       
   713     int originalCount = cm->contactIds().size();
       
   714     QContactDetailFilter dfil;
       
   715     dfil.setDetailDefinitionName(QContactUrl::DefinitionName, QContactUrl::FieldUrl);
       
   716     crr.setContactIds(cm->contactIds(dfil));
       
   717     crr.setManager(cm.data());
       
   718     QCOMPARE(crr.manager(), cm.data());
       
   719     QVERIFY(!crr.isActive());
       
   720     QVERIFY(!crr.isFinished());
       
   721     QVERIFY(!crr.cancel());
       
   722     QVERIFY(!crr.waitForFinished());
       
   723     qRegisterMetaType<QContactRemoveRequest*>("QContactRemoveRequest*");
       
   724     QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
   725     QVERIFY(!crr.cancel()); // not started
       
   726 
       
   727     QVERIFY(!cm->contactIds(dfil).isEmpty());
       
   728 
       
   729     QVERIFY(crr.start());
       
   730 
       
   731     QVERIFY((crr.isActive() &&crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished());
       
   732     //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable
       
   733     QVERIFY(crr.waitForFinished());
       
   734     QVERIFY(crr.isFinished());
       
   735 
       
   736     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   737     spy.clear();
       
   738 
       
   739     QCOMPARE(cm->contactIds().size(), originalCount - 1);
       
   740     QVERIFY(cm->contactIds(dfil).isEmpty());
       
   741 
       
   742     // remove all contacts
       
   743     dfil.setDetailDefinitionName(QContactDisplayLabel::DefinitionName); // delete everything.
       
   744     crr.setContactIds(cm->contactIds(dfil));
       
   745     
       
   746     QVERIFY(!crr.cancel()); // not started
       
   747     QVERIFY(crr.start());
       
   748 
       
   749     QVERIFY((crr.isActive() && crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished());
       
   750     //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable
       
   751     QVERIFY(crr.waitForFinished());
       
   752     QVERIFY(crr.isFinished());
       
   753 
       
   754     QCOMPARE(cm->contactIds().size(), 0); // no contacts should be left.
       
   755     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   756     spy.clear();
       
   757 
       
   758     // cancelling
       
   759     QContact temp;
       
   760     QContactName nameDetail;
       
   761     nameDetail.setFirstName("Should not be removed");
       
   762     temp.saveDetail(&nameDetail);
       
   763     cm->saveContact(&temp);
       
   764     crr.setContactIds(cm->contactIds(dfil));
       
   765 
       
   766     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
   767     while (true) {
       
   768         QVERIFY(!crr.cancel()); // not started
       
   769         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   770         QVERIFY(spy.count() == 0);
       
   771         QVERIFY(crr.start());
       
   772         if (!crr.cancel()) {
       
   773             // due to thread scheduling, async cancel might be attempted
       
   774             // after the request has already finished.. so loop and try again.
       
   775             crr.waitForFinished();
       
   776             crr.setContactIds(cm->contactIds(dfil));
       
   777             temp.setId(QContactId());
       
   778             if (!cm->saveContact(&temp)) {
       
   779                 QSKIP("Unable to save temporary contact for remove request cancellation test!", SkipSingle);
       
   780             }
       
   781             bailoutCount -= 1;
       
   782             if (!bailoutCount) {
       
   783                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   784                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   785                 break;
       
   786             }
       
   787             spy.clear();
       
   788             continue;
       
   789         }
       
   790 
       
   791         // if we get here, then we are cancelling the request.
       
   792         QVERIFY(crr.waitForFinished());
       
   793         QVERIFY(crr.isCanceled());
       
   794         QCOMPARE(cm->contactIds().size(), 1);
       
   795         QCOMPARE(cm->contact(cm->contactIds().first()), temp);
       
   796         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   797         spy.clear();
       
   798         break;
       
   799     }
       
   800 
       
   801     // restart, and wait for progress after cancel.
       
   802     while (true) {
       
   803         QVERIFY(!crr.cancel()); // not started
       
   804         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   805         QVERIFY(crr.start());
       
   806         if (!crr.cancel()) {
       
   807             // due to thread scheduling, async cancel might be attempted
       
   808             // after the request has already finished.. so loop and try again.
       
   809             crr.waitForFinished();
       
   810             crr.setContactIds(cm->contactIds(dfil));
       
   811             temp.setId(QContactId());
       
   812             cm->saveContact(&temp);
       
   813             bailoutCount -= 1;
       
   814             if (!bailoutCount) {
       
   815                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   816                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   817                 break;
       
   818             }
       
   819             spy.clear();
       
   820             continue;
       
   821         }
       
   822         crr.waitForFinished();
       
   823         QVERIFY(crr.isCanceled());
       
   824         QCOMPARE(cm->contactIds().size(), 1);
       
   825         QCOMPARE(cm->contact(cm->contactIds().first()), temp);
       
   826         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   827         spy.clear();
       
   828         break;
       
   829     }
       
   830 
       
   831 }
       
   832 
       
   833 void tst_QContactAsync::contactSave()
       
   834 {
       
   835     QFETCH(QString, uri);
       
   836     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
   837     QContactSaveRequest csr;
       
   838     QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest);
       
   839 
       
   840     // initial state - not started, no manager.
       
   841     QVERIFY(!csr.isActive());
       
   842     QVERIFY(!csr.isFinished());
       
   843     QVERIFY(!csr.start());
       
   844     QVERIFY(!csr.cancel());
       
   845     QVERIFY(!csr.waitForFinished());
       
   846 
       
   847     // save a new contact
       
   848     int originalCount = cm->contactIds().size();
       
   849     QContact testContact;
       
   850     QContactName nameDetail;
       
   851     nameDetail.setFirstName("Test Contact");
       
   852     testContact.saveDetail(&nameDetail);
       
   853     QList<QContact> saveList;
       
   854     saveList << testContact;
       
   855     csr.setManager(cm.data());
       
   856     QCOMPARE(csr.manager(), cm.data());
       
   857     QVERIFY(!csr.isActive());
       
   858     QVERIFY(!csr.isFinished());
       
   859     QVERIFY(!csr.cancel());
       
   860     QVERIFY(!csr.waitForFinished());
       
   861     qRegisterMetaType<QContactSaveRequest*>("QContactSaveRequest*");
       
   862     QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
   863     csr.setContacts(saveList);
       
   864     QCOMPARE(csr.contacts(), saveList);
       
   865     QVERIFY(!csr.cancel()); // not started
       
   866     QVERIFY(csr.start());
       
   867 
       
   868     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished());
       
   869     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable
       
   870     QVERIFY(csr.waitForFinished());
       
   871     QVERIFY(csr.isFinished());
       
   872     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   873     spy.clear();
       
   874 
       
   875     QList<QContact> expected;
       
   876     expected << cm->contact(cm->contactIds().last());
       
   877     QList<QContact> result = csr.contacts();
       
   878     QCOMPARE(expected, result);
       
   879     QCOMPARE(cm->contactIds().size(), originalCount + 1);
       
   880 
       
   881     // update a previously saved contact
       
   882     QContactPhoneNumber phn;
       
   883     phn.setNumber("12345678");
       
   884     testContact = expected.first();
       
   885     testContact.saveDetail(&phn);
       
   886     saveList.clear();
       
   887     saveList << testContact;
       
   888     csr.setContacts(saveList);
       
   889     QCOMPARE(csr.contacts(), saveList);
       
   890     QVERIFY(!csr.cancel()); // not started
       
   891     QVERIFY(csr.start());
       
   892 
       
   893     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished());
       
   894     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable
       
   895     QVERIFY(csr.waitForFinished());
       
   896 
       
   897     QVERIFY(csr.isFinished());
       
   898     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
   899     spy.clear();
       
   900 
       
   901     expected.clear();
       
   902     expected << cm->contact(cm->contactIds().last());
       
   903     result = csr.contacts();
       
   904     QCOMPARE(expected, result);
       
   905 
       
   906     //here we can't compare the whole contact details, testContact would be updated by async call because we just use QThreadSignalSpy to receive signals.
       
   907     //QVERIFY(containsIgnoringTimestamps(expected, testContact));
       
   908     QVERIFY(expected.at(0).detail<QContactPhoneNumber>().number() == phn.number());
       
   909     
       
   910     QCOMPARE(cm->contactIds().size(), originalCount + 1);
       
   911 
       
   912     // cancelling
       
   913     QContact temp = testContact;
       
   914     QContactUrl url;
       
   915     url.setUrl("should not get saved");
       
   916     temp.saveDetail(&url);
       
   917     saveList.clear();
       
   918     saveList << temp;
       
   919     csr.setContacts(saveList);
       
   920 
       
   921     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
   922     while (true) {
       
   923         QVERIFY(!csr.cancel()); // not started
       
   924         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   925         QVERIFY(csr.start());
       
   926         if (!csr.cancel()) {
       
   927             // due to thread scheduling, async cancel might be attempted
       
   928             // after the request has already finished.. so loop and try again.
       
   929             csr.waitForFinished();
       
   930             saveList = csr.contacts();
       
   931             if (cm->contactIds().size() > (originalCount + 1) && !cm->removeContact(saveList.at(0).localId())) {
       
   932                 QSKIP("Unable to remove saved contact to test cancellation of contact save request", SkipSingle);
       
   933             }
       
   934             saveList.clear();
       
   935             saveList << temp;
       
   936             csr.setContacts(saveList);
       
   937             bailoutCount -= 1;
       
   938             if (!bailoutCount) {
       
   939                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   940                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   941                 break;
       
   942             }
       
   943             spy.clear();
       
   944             continue;
       
   945         }
       
   946 
       
   947         // if we get here, then we are cancelling the request.
       
   948         QVERIFY(csr.waitForFinished());
       
   949         QVERIFY(csr.isCanceled());
       
   950         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   951         spy.clear();
       
   952 
       
   953         // verify that the changes were not saved
       
   954         expected.clear();
       
   955         QList<QContactLocalId> allContacts = cm->contactIds();
       
   956         for (int i = 0; i < allContacts.size(); i++) {
       
   957             expected.append(cm->contact(allContacts.at(i)));
       
   958         }
       
   959         QVERIFY(!expected.contains(temp));
       
   960         QCOMPARE(cm->contactIds().size(), originalCount + 1);
       
   961         break;
       
   962     }
       
   963     // restart, and wait for progress after cancel.
       
   964 
       
   965     while (true) {
       
   966         QVERIFY(!csr.cancel()); // not started
       
   967         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
   968         QVERIFY(csr.start());
       
   969         if (!csr.cancel()) {
       
   970             // due to thread scheduling, async cancel might be attempted
       
   971             // after the request has already finished.. so loop and try again.
       
   972             csr.waitForFinished();
       
   973             saveList = csr.contacts();
       
   974             if (cm->contactIds().size() > (originalCount + 1) && !cm->removeContact(saveList.at(0).localId())) {
       
   975                 QSKIP("Unable to remove saved contact to test cancellation of contact save request", SkipSingle);
       
   976             }
       
   977             saveList.clear();
       
   978             saveList << temp;
       
   979             csr.setContacts(saveList);
       
   980             bailoutCount -= 1;
       
   981             if (!bailoutCount) {
       
   982                 qWarning("Unable to test cancelling due to thread scheduling!");
       
   983                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
   984                 break;
       
   985             }
       
   986             spy.clear();
       
   987             continue;
       
   988         }
       
   989         csr.waitForFinished(); // now wait until finished (if it hasn't already).
       
   990         QVERIFY(csr.isCanceled());
       
   991         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
   992         spy.clear();
       
   993 
       
   994         // verify that the changes were not saved
       
   995         expected.clear();
       
   996         QList<QContactLocalId> allContacts = cm->contactIds();
       
   997         for (int i = 0; i < allContacts.size(); i++) {
       
   998             expected.append(cm->contact(allContacts.at(i)));
       
   999         }
       
  1000         QVERIFY(!expected.contains(temp));
       
  1001         QCOMPARE(cm->contactIds().size(), originalCount + 1);
       
  1002         break;
       
  1003     }
       
  1004 }
       
  1005 
       
  1006 void tst_QContactAsync::definitionFetch()
       
  1007 {
       
  1008     QFETCH(QString, uri);
       
  1009     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  1010     QContactDetailDefinitionFetchRequest dfr;
       
  1011     QVERIFY(dfr.type() == QContactAbstractRequest::DetailDefinitionFetchRequest);
       
  1012     dfr.setContactType(QContactType::TypeContact);
       
  1013     QVERIFY(dfr.contactType() == QString(QLatin1String(QContactType::TypeContact)));
       
  1014 
       
  1015     // initial state - not started, no manager.
       
  1016     QVERIFY(!dfr.isActive());
       
  1017     QVERIFY(!dfr.isFinished());
       
  1018     QVERIFY(!dfr.start());
       
  1019     QVERIFY(!dfr.cancel());
       
  1020     QVERIFY(!dfr.waitForFinished());
       
  1021 
       
  1022     // "all definitions" retrieval
       
  1023     dfr.setManager(cm.data());
       
  1024     QCOMPARE(dfr.manager(), cm.data());
       
  1025     QVERIFY(!dfr.isActive());
       
  1026     QVERIFY(!dfr.isFinished());
       
  1027     QVERIFY(!dfr.cancel());
       
  1028     QVERIFY(!dfr.waitForFinished());
       
  1029     qRegisterMetaType<QContactDetailDefinitionFetchRequest*>("QContactDetailDefinitionFetchRequest*");
       
  1030     QThreadSignalSpy spy(&dfr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
  1031     dfr.setDefinitionNames(QStringList());
       
  1032     QVERIFY(!dfr.cancel()); // not started
       
  1033     QVERIFY(dfr.start());
       
  1034 
       
  1035     QVERIFY((dfr.isActive() && dfr.state() == QContactAbstractRequest::ActiveState) || dfr.isFinished());
       
  1036     //QVERIFY(dfr.isFinished() || !dfr.start());  // already started. // thread scheduling means this is untestable
       
  1037     QVERIFY(dfr.waitForFinished());
       
  1038     QVERIFY(dfr.isFinished());
       
  1039     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1040     spy.clear();
       
  1041 
       
  1042     QMap<QString, QContactDetailDefinition> defs = cm->detailDefinitions();
       
  1043     QMap<QString, QContactDetailDefinition> result = dfr.definitions();
       
  1044     QCOMPARE(defs, result);
       
  1045 
       
  1046     // specific definition retrieval
       
  1047     QStringList specific;
       
  1048     specific << QContactUrl::DefinitionName;
       
  1049     dfr.setDefinitionNames(specific);
       
  1050     QVERIFY(!dfr.cancel()); // not started
       
  1051     QVERIFY(dfr.start());
       
  1052 
       
  1053     QVERIFY((dfr.isActive() && dfr.state() == QContactAbstractRequest::ActiveState) || dfr.isFinished());
       
  1054     //QVERIFY(dfr.isFinished() || !dfr.start());  // already started. // thread scheduling means this is untestable
       
  1055     QVERIFY(dfr.waitForFinished());
       
  1056     QVERIFY(dfr.isFinished());
       
  1057     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1058     spy.clear();
       
  1059 
       
  1060     defs.clear();
       
  1061     defs.insert(QContactUrl::DefinitionName, cm->detailDefinition(QContactUrl::DefinitionName));
       
  1062     result = dfr.definitions();
       
  1063     QCOMPARE(defs, result);
       
  1064 
       
  1065     // cancelling
       
  1066     dfr.setDefinitionNames(QStringList());
       
  1067 
       
  1068     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
  1069     while (true) {
       
  1070         QVERIFY(!dfr.cancel()); // not started
       
  1071         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1072         QVERIFY(dfr.start());
       
  1073         if (!dfr.cancel()) {
       
  1074             // due to thread scheduling, async cancel might be attempted
       
  1075             // after the request has already finished.. so loop and try again.
       
  1076             dfr.waitForFinished();
       
  1077             dfr.setDefinitionNames(QStringList());
       
  1078             bailoutCount -= 1;
       
  1079             if (!bailoutCount) {
       
  1080                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1081                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1082                 break;
       
  1083             }
       
  1084             spy.clear();
       
  1085             continue;
       
  1086         }
       
  1087 
       
  1088         // if we get here, then we are cancelling the request.
       
  1089         QVERIFY(dfr.waitForFinished());
       
  1090         QVERIFY(dfr.isCanceled());
       
  1091         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1092         spy.clear();
       
  1093         break;
       
  1094     }
       
  1095 
       
  1096     // restart, and wait for progress after cancel.
       
  1097     while (true) {
       
  1098         QVERIFY(!dfr.cancel()); // not started
       
  1099         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1100         QVERIFY(dfr.start());
       
  1101         if (!dfr.cancel()) {
       
  1102             // due to thread scheduling, async cancel might be attempted
       
  1103             // after the request has already finished.. so loop and try again.
       
  1104             dfr.waitForFinished();
       
  1105             dfr.setDefinitionNames(QStringList());
       
  1106             bailoutCount -= 1;
       
  1107             if (!bailoutCount) {
       
  1108                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1109                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1110                 break;
       
  1111             }
       
  1112             spy.clear();
       
  1113             continue;
       
  1114         }
       
  1115         dfr.waitForFinished();
       
  1116         QVERIFY(dfr.isCanceled());
       
  1117         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1118         spy.clear();
       
  1119 
       
  1120         break;
       
  1121     }
       
  1122 
       
  1123 }
       
  1124 
       
  1125 void tst_QContactAsync::definitionRemove()
       
  1126 {
       
  1127     QFETCH(QString, uri);
       
  1128 
       
  1129     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  1130     if (!cm->hasFeature(QContactManager::MutableDefinitions)) {
       
  1131        QSKIP("This contact manager doest not support mutable definitions, can't remove a definition!", SkipSingle);
       
  1132     }
       
  1133     QContactDetailDefinitionRemoveRequest drr;
       
  1134     QVERIFY(drr.type() == QContactAbstractRequest::DetailDefinitionRemoveRequest);
       
  1135     drr.setDefinitionNames(QContactType::TypeContact, QStringList());
       
  1136     QVERIFY(drr.contactType() == QString(QLatin1String(QContactType::TypeContact)));
       
  1137 
       
  1138     // initial state - not started, no manager.
       
  1139     QVERIFY(!drr.isActive());
       
  1140     QVERIFY(!drr.isFinished());
       
  1141     QVERIFY(!drr.start());
       
  1142     QVERIFY(!drr.cancel());
       
  1143     QVERIFY(!drr.waitForFinished());
       
  1144 
       
  1145     // specific group removal
       
  1146     int originalCount = cm->detailDefinitions().keys().size();
       
  1147     QStringList removeIds;
       
  1148     removeIds << cm->detailDefinitions().keys().first();
       
  1149     drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1150     drr.setManager(cm.data());
       
  1151     QCOMPARE(drr.manager(), cm.data());
       
  1152     QVERIFY(!drr.isActive());
       
  1153     QVERIFY(!drr.isFinished());
       
  1154     QVERIFY(!drr.cancel());
       
  1155     QVERIFY(!drr.waitForFinished());
       
  1156     qRegisterMetaType<QContactDetailDefinitionRemoveRequest*>("QContactDetailDefinitionRemoveRequest*");
       
  1157     QThreadSignalSpy spy(&drr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
  1158     QVERIFY(drr.definitionNames() == removeIds);
       
  1159     QVERIFY(!drr.cancel()); // not started
       
  1160     QVERIFY(drr.start());
       
  1161 
       
  1162     QVERIFY((drr.isActive() && drr.state() == QContactAbstractRequest::ActiveState) || drr.isFinished());
       
  1163     //QVERIFY(drr.isFinished() || !drr.start());  // already started. // thread scheduling means this is untestable
       
  1164     QVERIFY(drr.waitForFinished());
       
  1165     QVERIFY(drr.isFinished());
       
  1166     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1167     spy.clear();
       
  1168 
       
  1169     QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 1);
       
  1170     cm->detailDefinition(removeIds.first()); // check that it has already been removed.
       
  1171     QCOMPARE(cm->error(), QContactManager::DoesNotExistError);
       
  1172 
       
  1173     // remove (asynchronously) a nonexistent group - should fail.
       
  1174     drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1175     QVERIFY(!drr.cancel()); // not started
       
  1176     QVERIFY(drr.start());
       
  1177 
       
  1178     QVERIFY((drr.isActive() && drr.state() == QContactAbstractRequest::ActiveState) || drr.isFinished());
       
  1179     //QVERIFY(drr.isFinished() || !drr.start());  // already started. // thread scheduling means this is untestable
       
  1180     QVERIFY(drr.waitForFinished());
       
  1181     QVERIFY(drr.isFinished());
       
  1182     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1183     spy.clear();
       
  1184 
       
  1185     QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 1); // hasn't changed
       
  1186     QCOMPARE(drr.error(), QContactManager::DoesNotExistError);
       
  1187 
       
  1188     // remove with list containing one valid and one invalid id.
       
  1189     removeIds << cm->detailDefinitions().keys().first();
       
  1190     drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1191     QVERIFY(!drr.cancel()); // not started
       
  1192     QVERIFY(drr.start());
       
  1193 
       
  1194     QVERIFY((drr.isActive() && drr.state() == QContactAbstractRequest::ActiveState) || drr.isFinished());
       
  1195     //QVERIFY(drr.isFinished() || !drr.start());  // already started. // thread scheduling means this is untestable
       
  1196     QVERIFY(drr.waitForFinished());
       
  1197     QVERIFY(drr.isFinished());
       
  1198     QVERIFY(spy.count() >= 1); // active + finished signals
       
  1199     spy.clear();
       
  1200 
       
  1201     QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // only one more has been removed
       
  1202     QVERIFY(drr.errorMap().count() == 1);
       
  1203     QVERIFY(drr.errorMap().keys().contains(0));
       
  1204     QCOMPARE(drr.errorMap().value(0), QContactManager::DoesNotExistError);
       
  1205 
       
  1206     // remove with empty list - nothing should happen.
       
  1207     removeIds.clear();
       
  1208     drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1209     QVERIFY(!drr.cancel()); // not started
       
  1210     QVERIFY(drr.start());
       
  1211 
       
  1212     QVERIFY(drr.isActive() || drr.isFinished());
       
  1213     //QVERIFY(drr.isFinished() || !drr.start());  // already started. // thread scheduling means this is untestable
       
  1214     QVERIFY(drr.waitForFinished());
       
  1215 
       
  1216     QVERIFY(drr.isFinished());
       
  1217     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1218     spy.clear();
       
  1219 
       
  1220     QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed
       
  1221     QCOMPARE(drr.error(), QContactManager::NoError);  // no error but no effect.
       
  1222 
       
  1223     // cancelling
       
  1224     removeIds.clear();
       
  1225     removeIds << cm->detailDefinitions().keys().first();
       
  1226     drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1227 
       
  1228     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
  1229     while (true) {
       
  1230         QVERIFY(!drr.cancel()); // not started
       
  1231         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1232         QVERIFY(drr.start());
       
  1233         if (!drr.cancel()) {
       
  1234             // due to thread scheduling, async cancel might be attempted
       
  1235             // after the request has already finished.. so loop and try again.
       
  1236             drr.waitForFinished();
       
  1237             drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1238 
       
  1239             QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed
       
  1240             bailoutCount -= 1;
       
  1241             if (!bailoutCount) {
       
  1242                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1243                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1244                 break;
       
  1245             }
       
  1246             spy.clear();
       
  1247             continue;
       
  1248         }
       
  1249 
       
  1250         // if we get here, then we are cancelling the request.
       
  1251         QVERIFY(drr.waitForFinished());
       
  1252         QVERIFY(drr.isCanceled());
       
  1253         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1254         spy.clear();
       
  1255 
       
  1256         QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed
       
  1257         break;
       
  1258     }
       
  1259 
       
  1260     // restart, and wait for progress after cancel.
       
  1261     while (true) {
       
  1262         QVERIFY(!drr.cancel()); // not started
       
  1263         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1264         QVERIFY(drr.start());
       
  1265         if (!drr.cancel()) {
       
  1266             // due to thread scheduling, async cancel might be attempted
       
  1267             // after the request has already finished.. so loop and try again.
       
  1268             drr.waitForFinished();
       
  1269             drr.setDefinitionNames(QContactType::TypeContact, removeIds);
       
  1270             bailoutCount -= 1;
       
  1271             if (!bailoutCount) {
       
  1272                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1273                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1274                 break;
       
  1275             }
       
  1276             spy.clear();
       
  1277             continue;
       
  1278         }
       
  1279         drr.waitForFinished();
       
  1280         QVERIFY(drr.isCanceled());
       
  1281         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1282         spy.clear();
       
  1283 
       
  1284         QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed
       
  1285         break;
       
  1286     }
       
  1287 
       
  1288 }
       
  1289 
       
  1290 void tst_QContactAsync::definitionSave()
       
  1291 {
       
  1292     QFETCH(QString, uri);
       
  1293 
       
  1294     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  1295 
       
  1296     if (!cm->hasFeature(QContactManager::MutableDefinitions)) {
       
  1297 
       
  1298        QSKIP("This contact manager doest not support mutable definitions, can't save a definition!", SkipSingle);
       
  1299     }
       
  1300     
       
  1301     QContactDetailDefinitionSaveRequest dsr;
       
  1302     QVERIFY(dsr.type() == QContactAbstractRequest::DetailDefinitionSaveRequest);
       
  1303     dsr.setContactType(QContactType::TypeContact);
       
  1304     QVERIFY(dsr.contactType() == QString(QLatin1String(QContactType::TypeContact)));
       
  1305 
       
  1306     // initial state - not started, no manager.
       
  1307     QVERIFY(!dsr.isActive());
       
  1308     QVERIFY(!dsr.isFinished());
       
  1309     QVERIFY(!dsr.start());
       
  1310     QVERIFY(!dsr.cancel());
       
  1311     QVERIFY(!dsr.waitForFinished());
       
  1312 
       
  1313     // save a new detail definition
       
  1314     int originalCount = cm->detailDefinitions().keys().size();
       
  1315     QContactDetailDefinition testDef;
       
  1316     testDef.setName("TestDefinitionId");
       
  1317     QMap<QString, QContactDetailFieldDefinition> fields;
       
  1318     QContactDetailFieldDefinition f;
       
  1319     f.setDataType(QVariant::String);
       
  1320     fields.insert("TestDefinitionField", f);
       
  1321     testDef.setFields(fields);
       
  1322     QList<QContactDetailDefinition> saveList;
       
  1323     saveList << testDef;
       
  1324     dsr.setManager(cm.data());
       
  1325     QCOMPARE(dsr.manager(), cm.data());
       
  1326     QVERIFY(!dsr.isActive());
       
  1327     QVERIFY(!dsr.isFinished());
       
  1328     QVERIFY(!dsr.cancel());
       
  1329     QVERIFY(!dsr.waitForFinished());
       
  1330     qRegisterMetaType<QContactDetailDefinitionSaveRequest*>("QContactDetailDefinitionSaveRequest*");
       
  1331     QThreadSignalSpy spy(&dsr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
  1332     dsr.setDefinitions(saveList);
       
  1333     QCOMPARE(dsr.definitions(), saveList);
       
  1334     QVERIFY(!dsr.cancel()); // not started
       
  1335     QVERIFY(dsr.start());
       
  1336 
       
  1337     QVERIFY((dsr.isActive() && dsr.state() == QContactAbstractRequest::ActiveState) || dsr.isFinished());
       
  1338     //QVERIFY(dsr.isFinished() || !dsr.start());  // already started. // thread scheduling means this is untestable
       
  1339     QVERIFY(dsr.waitForFinished());
       
  1340     QVERIFY(dsr.isFinished());
       
  1341     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1342     spy.clear();
       
  1343 
       
  1344     QList<QContactDetailDefinition> expected;
       
  1345     expected << cm->detailDefinition("TestDefinitionId");
       
  1346     QList<QContactDetailDefinition> result = dsr.definitions();
       
  1347     QCOMPARE(expected, result);
       
  1348     QVERIFY(expected.contains(testDef));
       
  1349     QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1);
       
  1350 
       
  1351     // update a previously saved group
       
  1352     fields.insert("TestDefinitionFieldTwo", f);
       
  1353     testDef.setFields(fields);
       
  1354     saveList.clear();
       
  1355     saveList << testDef;
       
  1356     dsr.setDefinitions(saveList);
       
  1357     QCOMPARE(dsr.definitions(), saveList);
       
  1358     QVERIFY(!dsr.cancel()); // not started
       
  1359     QVERIFY(dsr.start());
       
  1360 
       
  1361     QVERIFY((dsr.isActive() && dsr.state() == QContactAbstractRequest::ActiveState) || dsr.isFinished());
       
  1362     //QVERIFY(dsr.isFinished() || !dsr.start());  // already started. // thread scheduling means this is untestable
       
  1363     QVERIFY(dsr.waitForFinished());
       
  1364     QVERIFY(dsr.isFinished());
       
  1365     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1366     spy.clear();
       
  1367 
       
  1368     expected.clear();
       
  1369     expected << cm->detailDefinition("TestDefinitionId");
       
  1370     result = dsr.definitions();
       
  1371     QCOMPARE(expected, result);
       
  1372     QVERIFY(expected.contains(testDef));
       
  1373     QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1);
       
  1374 
       
  1375     // cancelling
       
  1376     fields.insert("TestDefinitionFieldThree - shouldn't get saved", f);
       
  1377     testDef.setFields(fields);
       
  1378     saveList.clear();
       
  1379     saveList << testDef;
       
  1380     dsr.setDefinitions(saveList);
       
  1381 
       
  1382     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
  1383     while (true) {
       
  1384         QVERIFY(!dsr.cancel()); // not started
       
  1385         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1386         QVERIFY(dsr.start());
       
  1387         if (!dsr.cancel()) {
       
  1388             // due to thread scheduling, async cancel might be attempted
       
  1389             // after the request has already finished.. so loop and try again.
       
  1390             dsr.waitForFinished();
       
  1391             saveList.clear();
       
  1392             saveList << testDef;
       
  1393             dsr.setDefinitions(saveList);
       
  1394             cm->removeDetailDefinition(testDef.name());
       
  1395             bailoutCount -= 1;
       
  1396             if (!bailoutCount) {
       
  1397                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1398                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1399                 break;
       
  1400             }
       
  1401             spy.clear();
       
  1402             continue;
       
  1403         }
       
  1404 
       
  1405         // if we get here, then we are cancelling the request.
       
  1406         QVERIFY(dsr.waitForFinished());
       
  1407         QVERIFY(dsr.isCanceled());
       
  1408         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1409         spy.clear();
       
  1410 
       
  1411         // verify that the changes were not saved
       
  1412         QList<QContactDetailDefinition> allDefs = cm->detailDefinitions().values();
       
  1413         QVERIFY(!allDefs.contains(testDef));
       
  1414         QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1);
       
  1415 
       
  1416         break;
       
  1417     }
       
  1418 
       
  1419     // restart, and wait for progress after cancel.
       
  1420     while (true) {
       
  1421         QVERIFY(!dsr.cancel()); // not started
       
  1422         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1423         QVERIFY(dsr.start());
       
  1424         if (!dsr.cancel()) {
       
  1425             // due to thread scheduling, async cancel might be attempted
       
  1426             // after the request has already finished.. so loop and try again.
       
  1427             dsr.waitForFinished();
       
  1428             saveList.clear();
       
  1429             saveList << testDef;
       
  1430             dsr.setDefinitions(saveList);
       
  1431             cm->removeDetailDefinition(testDef.name());
       
  1432             bailoutCount -= 1;
       
  1433             if (!bailoutCount) {
       
  1434                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1435                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1436                 break;
       
  1437             }
       
  1438             spy.clear();
       
  1439             continue;
       
  1440         }
       
  1441         dsr.waitForFinished();
       
  1442         QVERIFY(dsr.isCanceled());
       
  1443         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1444         spy.clear();
       
  1445 
       
  1446         // verify that the changes were not saved
       
  1447         QList<QContactDetailDefinition> allDefs = cm->detailDefinitions().values();
       
  1448         QVERIFY(!allDefs.contains(testDef));
       
  1449         QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1);
       
  1450 
       
  1451         break;
       
  1452     }
       
  1453 
       
  1454 }
       
  1455 
       
  1456 void tst_QContactAsync::relationshipFetch()
       
  1457 {
       
  1458     QFETCH(QString, uri);
       
  1459     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  1460     QContactRelationshipFetchRequest rfr;
       
  1461     QVERIFY(rfr.type() == QContactAbstractRequest::RelationshipFetchRequest);
       
  1462 
       
  1463     // initial state - not started, no manager.
       
  1464     QVERIFY(!rfr.isActive());
       
  1465     QVERIFY(!rfr.isFinished());
       
  1466     QVERIFY(!rfr.start());
       
  1467     QVERIFY(!rfr.cancel());
       
  1468     QVERIFY(!rfr.waitForFinished());
       
  1469 
       
  1470     // "all relationships" retrieval
       
  1471     rfr.setManager(cm.data());
       
  1472     QCOMPARE(rfr.manager(), cm.data());
       
  1473     QVERIFY(!rfr.isActive());
       
  1474     QVERIFY(!rfr.isFinished());
       
  1475     QVERIFY(!rfr.cancel());
       
  1476     QVERIFY(!rfr.waitForFinished());
       
  1477     qRegisterMetaType<QContactRelationshipFetchRequest*>("QContactRelationshipFetchRequest*");
       
  1478     QThreadSignalSpy spy(&rfr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
  1479     QVERIFY(!rfr.cancel()); // not started
       
  1480     QVERIFY(rfr.start());
       
  1481 
       
  1482     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished());
       
  1483     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable
       
  1484     QVERIFY(rfr.waitForFinished());
       
  1485 
       
  1486     QVERIFY(rfr.isFinished());
       
  1487     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1488     spy.clear();
       
  1489 
       
  1490     QList<QContactRelationship> rels = cm->relationships();
       
  1491     QList<QContactRelationship> result = rfr.relationships();
       
  1492     QCOMPARE(rels, result);
       
  1493 
       
  1494     // specific relationship type retrieval
       
  1495     rfr.setRelationshipType(QContactRelationship::HasManager);
       
  1496     QVERIFY(!rfr.cancel()); // not started
       
  1497     QVERIFY(rfr.start());
       
  1498 
       
  1499     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished());
       
  1500     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable
       
  1501     QVERIFY(rfr.waitForFinished());
       
  1502     QVERIFY(rfr.isFinished());
       
  1503     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1504     spy.clear();
       
  1505 
       
  1506     rels = cm->relationships(QContactRelationship::HasManager);
       
  1507     result = rfr.relationships();
       
  1508     QCOMPARE(rels, result);
       
  1509 
       
  1510     // specific source contact retrieval
       
  1511     rfr.setRelationshipType(QString());
       
  1512     QList<QContactLocalId> contacts = cm->contactIds();
       
  1513     QContactId aId;
       
  1514     foreach (const QContactLocalId& currId, contacts) {
       
  1515         QContact curr = cm->contact(currId);
       
  1516         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Aaron")) {
       
  1517             aId = curr.id();
       
  1518             break;
       
  1519         }
       
  1520     }
       
  1521     rfr.setFirst(aId);
       
  1522     QVERIFY(!rfr.cancel()); // not started
       
  1523     QVERIFY(rfr.start());
       
  1524 
       
  1525     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished());
       
  1526     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable
       
  1527     QVERIFY(rfr.waitForFinished());
       
  1528     QVERIFY(rfr.isFinished());
       
  1529     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1530     spy.clear();
       
  1531 
       
  1532     rels = cm->relationships(aId, QContactRelationshipFilter::First);
       
  1533     result = rfr.relationships();
       
  1534     QCOMPARE(rels, result);
       
  1535 
       
  1536     // specific participant retrieval #1 - destination participant
       
  1537     rfr.setFirst(QContactId());
       
  1538     contacts = cm->contactIds();
       
  1539     QContactId bId;
       
  1540     foreach (const QContactLocalId& currId, contacts) {
       
  1541         QContact curr = cm->contact(currId);
       
  1542         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Bob")) {
       
  1543             bId = curr.id();
       
  1544             break;
       
  1545         }
       
  1546     }
       
  1547     rfr.setSecond(bId);
       
  1548     
       
  1549     QVERIFY(!rfr.cancel()); // not started
       
  1550     QVERIFY(rfr.start());
       
  1551 
       
  1552     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished());
       
  1553     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable
       
  1554     QVERIFY(rfr.waitForFinished());
       
  1555     QVERIFY(rfr.isFinished());
       
  1556     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1557     spy.clear();
       
  1558 
       
  1559     // retrieve rels where second = id of B, and ensure that we get the same results
       
  1560     rels = cm->relationships(bId, QContactRelationshipFilter::Second);
       
  1561     result = rfr.relationships();
       
  1562     QCOMPARE(rels, result);
       
  1563 
       
  1564     // specific participant retrieval #2 - source participant
       
  1565     rfr.setFirst(QContactId());
       
  1566     contacts = cm->contactIds();
       
  1567     QContactId cId;
       
  1568     foreach (const QContactLocalId& currId, contacts) {
       
  1569         QContact curr = cm->contact(currId);
       
  1570         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Borris")) {
       
  1571             cId = curr.id();
       
  1572             break;
       
  1573         }
       
  1574     }
       
  1575     rfr.setSecond(cId);
       
  1576     
       
  1577     QVERIFY(!rfr.cancel()); // not started
       
  1578     QVERIFY(rfr.start());
       
  1579 
       
  1580     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished());
       
  1581     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable
       
  1582     QVERIFY(rfr.waitForFinished());
       
  1583     QVERIFY(rfr.isFinished());
       
  1584     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1585     spy.clear();
       
  1586 
       
  1587     // retrieve rels where first = id of C and compare the results
       
  1588     rfr.setFirst(cId);
       
  1589     rfr.setSecond(QContactId());
       
  1590     QVERIFY(rfr.start());
       
  1591     QVERIFY(rfr.waitForFinished());
       
  1592     result = rfr.relationships();
       
  1593     rels = cm->relationships(cId, QContactRelationshipFilter::First);
       
  1594     QCOMPARE(rels, result);
       
  1595 
       
  1596     // cancelling
       
  1597     rfr.setRelationshipType(QString());
       
  1598 
       
  1599     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
  1600     while (true) {
       
  1601         QVERIFY(!rfr.cancel()); // not started
       
  1602         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1603         QVERIFY(rfr.start());
       
  1604         if (!rfr.cancel()) {
       
  1605             // due to thread scheduling, async cancel might be attempted
       
  1606             // after the request has already finished.. so loop and try again.
       
  1607             rfr.waitForFinished();
       
  1608             rfr.setRelationshipType(QString());
       
  1609             bailoutCount -= 1;
       
  1610             if (!bailoutCount) {
       
  1611                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1612                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1613                 break;
       
  1614             }
       
  1615             spy.clear();
       
  1616             continue;
       
  1617         }
       
  1618 
       
  1619         // if we get here, then we are cancelling the request.
       
  1620         QVERIFY(rfr.waitForFinished());
       
  1621         QVERIFY(rfr.isCanceled());
       
  1622         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1623         spy.clear();
       
  1624         break;
       
  1625     }
       
  1626 
       
  1627     // restart, and wait for progress after cancel.
       
  1628     while (true) {
       
  1629         QVERIFY(!rfr.cancel()); // not started
       
  1630         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1631         QVERIFY(rfr.start());
       
  1632         if (!rfr.cancel()) {
       
  1633             // due to thread scheduling, async cancel might be attempted
       
  1634             // after the request has already finished.. so loop and try again.
       
  1635             rfr.waitForFinished();
       
  1636             rfr.setRelationshipType(QString());
       
  1637             bailoutCount -= 1;
       
  1638             if (!bailoutCount) {
       
  1639                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1640                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1641                 break;
       
  1642             }
       
  1643             spy.clear();
       
  1644             continue;
       
  1645         }
       
  1646         rfr.waitForFinished();
       
  1647         QVERIFY(rfr.isCanceled());
       
  1648         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1649         spy.clear();
       
  1650         break;
       
  1651     }
       
  1652 }
       
  1653 
       
  1654 void tst_QContactAsync::relationshipRemove()
       
  1655 {
       
  1656     QFETCH(QString, uri);
       
  1657     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  1658     QContactRelationshipRemoveRequest rrr;
       
  1659     QVERIFY(rrr.type() == QContactAbstractRequest::RelationshipRemoveRequest);
       
  1660 
       
  1661     // initial state - not started, no manager.
       
  1662     QVERIFY(!rrr.isActive());
       
  1663     QVERIFY(!rrr.isFinished());
       
  1664     QVERIFY(!rrr.start());
       
  1665     QVERIFY(!rrr.cancel());
       
  1666     QVERIFY(!rrr.waitForFinished());
       
  1667 
       
  1668     QList<QContactLocalId> contacts = cm->contactIds();
       
  1669     QContactId aId, bId, cId;
       
  1670     foreach (const QContactLocalId& currId, contacts) {
       
  1671         QContact curr = cm->contact(currId);
       
  1672         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Aaron")) {
       
  1673             aId = curr.id();
       
  1674             continue;
       
  1675         }
       
  1676         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Bob")) {
       
  1677             bId = curr.id();
       
  1678             continue;
       
  1679         }
       
  1680         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Borris")) {
       
  1681             cId = curr.id();
       
  1682             continue;
       
  1683         }
       
  1684     }
       
  1685 
       
  1686     // specific source, destination and type removal
       
  1687     QList<QContactRelationship> relationships;
       
  1688     QContactRelationship r;
       
  1689     r.setFirst(aId);
       
  1690     r.setSecond(cId);
       
  1691     r.setRelationshipType(QContactRelationship::HasAssistant);
       
  1692     relationships.push_back(r);
       
  1693 
       
  1694     rrr.setRelationships(relationships);
       
  1695     rrr.setManager(cm.data());
       
  1696     qRegisterMetaType<QContactRelationshipRemoveRequest*>("QContactRelationshipRemoveRequest*");
       
  1697     QThreadSignalSpy spy(&rrr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
  1698     QCOMPARE(rrr.manager(), cm.data());
       
  1699     QVERIFY(!rrr.isActive());
       
  1700     QVERIFY(!rrr.isFinished());
       
  1701     QVERIFY(!rrr.cancel());
       
  1702     QVERIFY(!rrr.waitForFinished());
       
  1703     
       
  1704     QVERIFY(!rrr.cancel()); // not started
       
  1705     QVERIFY(rrr.start());
       
  1706 
       
  1707     QVERIFY((rrr.isActive() && rrr.state() == QContactAbstractRequest::ActiveState) || rrr.isFinished());
       
  1708     //QVERIFY(rrr.isFinished() || !rrr.start());  // already started. // thread scheduling means this is untestable
       
  1709     QVERIFY(rrr.waitForFinished());
       
  1710     QVERIFY(rrr.isFinished());
       
  1711     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1712     spy.clear();
       
  1713     QCOMPARE(cm->relationships(QContactRelationship::HasAssistant, cId, QContactRelationshipFilter::Second).size(), 1);
       
  1714 
       
  1715     // remove (asynchronously) a nonexistent relationship - should fail.
       
  1716     r.setFirst(cId);
       
  1717     r.setSecond(aId);
       
  1718     r.setRelationshipType(QContactRelationship::HasManager);
       
  1719     relationships.clear();
       
  1720     relationships.push_back(r);
       
  1721     rrr.setRelationships(relationships);
       
  1722     rrr.setManager(cm.data());
       
  1723     QVERIFY(!rrr.cancel()); // not started
       
  1724     QVERIFY(rrr.start());
       
  1725 
       
  1726     QVERIFY((rrr.isActive() && rrr.state() == QContactAbstractRequest::ActiveState) || rrr.isFinished());
       
  1727     //QVERIFY(rrr.isFinished() || !rrr.start());  // already started. // thread scheduling means this is untestable
       
  1728     QVERIFY(rrr.waitForFinished());
       
  1729     QVERIFY(rrr.isFinished());
       
  1730     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1731     spy.clear();
       
  1732 
       
  1733     QCOMPARE(cm->relationships(QContactRelationship::HasManager, cId, QContactRelationshipFilter::First).size(), 0);
       
  1734 //    QCOMPARE(rrr.error(), QContactManager::DoesNotExistError);
       
  1735 
       
  1736     // cancelling
       
  1737     r.setFirst(cId);
       
  1738     r.setSecond(QContactId());
       
  1739     relationships.clear();
       
  1740     relationships.push_back(r);
       
  1741     
       
  1742 
       
  1743     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
  1744     while (true) {
       
  1745         QVERIFY(!rrr.cancel()); // not started
       
  1746         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1747         QVERIFY(rrr.start());
       
  1748         if (!rrr.cancel()) {
       
  1749             // due to thread scheduling, async cancel might be attempted
       
  1750             // after the request has already finished.. so loop and try again.
       
  1751             rrr.waitForFinished();
       
  1752             rrr.setRelationships(relationships);
       
  1753             bailoutCount -= 1;
       
  1754             if (!bailoutCount) {
       
  1755                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1756                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1757                 break;
       
  1758             }
       
  1759             spy.clear();
       
  1760             continue;
       
  1761         }
       
  1762 
       
  1763         // if we get here, then we are cancelling the request.
       
  1764         QVERIFY(rrr.waitForFinished());
       
  1765         QVERIFY(rrr.isCanceled());
       
  1766         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1767         spy.clear();
       
  1768 
       
  1769         QVERIFY(cm->relationships(cId).size() != 0); // didn't remove them.
       
  1770         break;
       
  1771     }
       
  1772 
       
  1773     // restart, and wait for progress after cancel.
       
  1774     while (true) {
       
  1775         QVERIFY(!rrr.cancel()); // not started
       
  1776         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1777         QVERIFY(rrr.start());
       
  1778         if (!rrr.cancel()) {
       
  1779             // due to thread scheduling, async cancel might be attempted
       
  1780             // after the request has already finished.. so loop and try again.
       
  1781             rrr.waitForFinished();
       
  1782             rrr.setRelationships(relationships);
       
  1783             bailoutCount -= 1;
       
  1784             if (!bailoutCount) {
       
  1785                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1786                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1787                 break;
       
  1788             }
       
  1789             spy.clear();
       
  1790             continue;
       
  1791         }
       
  1792         rrr.waitForFinished();
       
  1793         QVERIFY(rrr.isCanceled());
       
  1794         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1795         spy.clear();
       
  1796 
       
  1797         QVERIFY(cm->relationships(cId).size() != 0); // didn't remove them.
       
  1798         break;
       
  1799     }
       
  1800 }
       
  1801 
       
  1802 void tst_QContactAsync::relationshipSave()
       
  1803 {
       
  1804     QFETCH(QString, uri);
       
  1805     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  1806     QContactRelationshipSaveRequest rsr;
       
  1807     QVERIFY(rsr.type() == QContactAbstractRequest::RelationshipSaveRequest);
       
  1808 
       
  1809     // initial state - not started, no manager.
       
  1810     QVERIFY(!rsr.isActive());
       
  1811     QVERIFY(!rsr.isFinished());
       
  1812     QVERIFY(!rsr.start());
       
  1813     QVERIFY(!rsr.cancel());
       
  1814     QVERIFY(!rsr.waitForFinished());
       
  1815 
       
  1816     QList<QContactLocalId> contacts = cm->contactIds();
       
  1817     QContactId cId, aId, bId;
       
  1818     foreach (const QContactLocalId& currId, contacts) {
       
  1819         QContact curr = cm->contact(currId);
       
  1820         if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Borris")) {
       
  1821             cId = curr.id();
       
  1822         } else if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Bob")) {
       
  1823             bId = curr.id();
       
  1824         } else if (curr.detail(QContactName::DefinitionName).value(QContactName::FieldFirstName) == QString("Aaron")) {
       
  1825             aId = curr.id();
       
  1826         }
       
  1827     }
       
  1828 
       
  1829     // save a new relationship
       
  1830     int originalCount = cm->relationships(aId).size();
       
  1831     QContactRelationship testRel;
       
  1832     testRel.setFirst(aId);
       
  1833     testRel.setRelationshipType(QContactRelationship::HasSpouse);
       
  1834     testRel.setSecond(bId);
       
  1835     QList<QContactRelationship> saveList;
       
  1836     saveList << testRel;
       
  1837     rsr.setManager(cm.data());
       
  1838     QCOMPARE(rsr.manager(), cm.data());
       
  1839     QVERIFY(!rsr.isActive());
       
  1840     QVERIFY(!rsr.isFinished());
       
  1841     QVERIFY(!rsr.cancel());
       
  1842     QVERIFY(!rsr.waitForFinished());
       
  1843     qRegisterMetaType<QContactRelationshipSaveRequest*>("QContactRelationshipSaveRequest*");
       
  1844     QThreadSignalSpy spy(&rsr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
       
  1845     rsr.setRelationships(saveList);
       
  1846     QCOMPARE(rsr.relationships(), saveList);
       
  1847     QVERIFY(!rsr.cancel()); // not started
       
  1848     QVERIFY(rsr.start());
       
  1849 
       
  1850     QVERIFY((rsr.isActive() && rsr.state() == QContactAbstractRequest::ActiveState) || rsr.isFinished());
       
  1851     //QVERIFY(rsr.isFinished() || !rsr.start());  // already started. // thread scheduling means this is untestable
       
  1852     QVERIFY(rsr.waitForFinished());
       
  1853     QVERIFY(rsr.isFinished());
       
  1854     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1855     spy.clear();
       
  1856 
       
  1857     QList<QContactRelationship> expected = cm->relationships(QContactRelationship::HasSpouse, aId, QContactRelationshipFilter::First);
       
  1858     QList<QContactRelationship> result = rsr.relationships();
       
  1859     QCOMPARE(expected, result);
       
  1860     QVERIFY(result.contains(testRel));
       
  1861     QCOMPARE(cm->relationships(aId).size(), originalCount + 1); // should be one extra
       
  1862 
       
  1863     // save a new relationship
       
  1864     testRel.setSecond(cId);
       
  1865     saveList.clear();
       
  1866     saveList << testRel;
       
  1867     rsr.setRelationships(saveList);
       
  1868     QCOMPARE(rsr.relationships(), saveList);
       
  1869     QVERIFY(!rsr.cancel()); // not started
       
  1870     QVERIFY(rsr.start());
       
  1871 
       
  1872     QVERIFY((rsr.isActive() && rsr.state() == QContactAbstractRequest::ActiveState) || rsr.isFinished());
       
  1873     //QVERIFY(rsr.isFinished() || !rsr.start());  // already started. // thread scheduling means this is untestable
       
  1874     QVERIFY(rsr.waitForFinished());
       
  1875     QVERIFY(rsr.isFinished());
       
  1876     QVERIFY(spy.count() >= 1); // active + finished progress signals
       
  1877     spy.clear();
       
  1878 
       
  1879     expected.clear();
       
  1880     expected = cm->relationships(QContactRelationship::HasSpouse, aId, QContactRelationshipFilter::First);
       
  1881     result = rsr.relationships();
       
  1882     QCOMPARE(result, QList<QContactRelationship>() << testRel);
       
  1883     QVERIFY(expected.contains(testRel));
       
  1884     QCOMPARE(cm->relationships(aId).size(), originalCount + 2); // should now be two extra
       
  1885 
       
  1886     // cancelling
       
  1887     testRel.setSecond(aId); // shouldn't get saved (circular anyway)
       
  1888     saveList.clear();
       
  1889     saveList << testRel;
       
  1890     rsr.setRelationships(saveList);
       
  1891     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
       
  1892     while (true) {
       
  1893         QVERIFY(!rsr.cancel()); // not started
       
  1894         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1895         QVERIFY(rsr.start());
       
  1896         if (!rsr.cancel()) {
       
  1897             // due to thread scheduling, async cancel might be attempted
       
  1898             // after the request has already finished.. so loop and try again.
       
  1899             rsr.waitForFinished();
       
  1900             saveList.clear();
       
  1901             saveList << testRel;
       
  1902             rsr.setRelationships(saveList);
       
  1903             cm->removeRelationship(testRel); // probably shouldn't have been saved anyway (circular)
       
  1904             bailoutCount -= 1;
       
  1905             if (!bailoutCount) {
       
  1906                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1907                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1908                 break;
       
  1909             }
       
  1910             spy.clear();
       
  1911             continue;
       
  1912         }
       
  1913 
       
  1914         // if we get here, then we are cancelling the request.
       
  1915         QVERIFY(rsr.waitForFinished());
       
  1916         QVERIFY(rsr.isCanceled());
       
  1917         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1918         spy.clear();
       
  1919 
       
  1920         // verify that the changes were not saved
       
  1921         QList<QContactRelationship> aRels = cm->relationships(aId, QContactRelationshipFilter::First);
       
  1922         QVERIFY(!aRels.contains(testRel));
       
  1923         QCOMPARE(cm->relationships(aId).size(), originalCount + 2); // should still only be two extra
       
  1924 
       
  1925         break;
       
  1926     }
       
  1927 
       
  1928     // restart, and wait for progress after cancel.
       
  1929     while (true) {
       
  1930         QVERIFY(!rsr.cancel()); // not started
       
  1931         FILL_QUEUE_WITH_FETCH_REQUESTS();
       
  1932         QVERIFY(rsr.start());
       
  1933         if (!rsr.cancel()) {
       
  1934             // due to thread scheduling, async cancel might be attempted
       
  1935             // after the request has already finished.. so loop and try again.
       
  1936             rsr.waitForFinished();
       
  1937             saveList.clear();
       
  1938             saveList << testRel;
       
  1939             rsr.setRelationships(saveList);
       
  1940             cm->removeRelationship(testRel); // probably shouldn't have been saved anyway (circular)
       
  1941             bailoutCount -= 1;
       
  1942             if (!bailoutCount) {
       
  1943                 qWarning("Unable to test cancelling due to thread scheduling!");
       
  1944                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
       
  1945                 break;
       
  1946             }
       
  1947             spy.clear();
       
  1948             continue;
       
  1949         }
       
  1950         rsr.waitForFinished();
       
  1951         QVERIFY(rsr.isCanceled());
       
  1952         QVERIFY(spy.count() >= 1); // active + cancelled progress signals
       
  1953         spy.clear();
       
  1954 
       
  1955         // verify that the changes were not saved
       
  1956         QList<QContactRelationship> aRels = cm->relationships(aId, QContactRelationshipFilter::First);
       
  1957         QVERIFY(!aRels.contains(testRel));
       
  1958         QCOMPARE(cm->relationships(aId).size(), originalCount + 2); // should still only be two extra
       
  1959 
       
  1960         break;
       
  1961     }
       
  1962 }
       
  1963 
       
  1964 void tst_QContactAsync::maliciousManager()
       
  1965 {
       
  1966     // use the invalid manager: passes all requests through to base class
       
  1967     QContactManager cm("invalid");
       
  1968     QContactFilter fil; // matches everything
       
  1969     QContactFetchRequest cfr;
       
  1970     cfr.setFilter(fil);
       
  1971     cfr.setManager(&cm);
       
  1972     QVERIFY(!cfr.start());
       
  1973     QVERIFY(!cfr.cancel());
       
  1974     QVERIFY(!cfr.waitForFinished());
       
  1975     QVERIFY(!cfr.start());
       
  1976 
       
  1977     // ensure that the base class implementation of requestDestroyed() is called
       
  1978     QContactFetchRequest *destroyedRequest = new QContactFetchRequest;
       
  1979     destroyedRequest->setManager(&cm);
       
  1980     destroyedRequest->setFilter(fil);
       
  1981     QVERIFY(!destroyedRequest->start());
       
  1982     delete destroyedRequest;
       
  1983 
       
  1984     // now use a malicious manager that deliberately calls
       
  1985     // incorrect "updateRequest" functions in base class:
       
  1986     QContactManager mcm("maliciousplugin");
       
  1987     QCOMPARE(mcm.managerName(), QString("maliciousplugin"));
       
  1988     QList<QContact> emptyCList;
       
  1989     QList<QContactLocalId> emptyIList;
       
  1990     QList<QContactDetailDefinition> emptyDList;
       
  1991     QStringList emptyDNList;
       
  1992     QMap<QString, QContactDetailDefinition> emptyDMap;
       
  1993     cfr.setFilter(fil);
       
  1994     cfr.setManager(&mcm);
       
  1995     QVERIFY(cfr.start());
       
  1996     QVERIFY(cfr.cancel());
       
  1997     QVERIFY(cfr.waitForFinished(100));
       
  1998     QVERIFY(cfr.start());
       
  1999     QVERIFY(!cfr.waitForFinished(100));
       
  2000     QVERIFY(cfr.cancel());
       
  2001 
       
  2002     QContactLocalIdFetchRequest cifr;
       
  2003     cifr.setFilter(fil);
       
  2004     cifr.setManager(&mcm);
       
  2005     QVERIFY(cifr.start());
       
  2006     QVERIFY(cifr.cancel());
       
  2007     QVERIFY(cifr.waitForFinished(100));
       
  2008     QVERIFY(cifr.start());
       
  2009     QVERIFY(!cifr.waitForFinished(100));
       
  2010     QVERIFY(cifr.cancel());
       
  2011 
       
  2012     QContactRemoveRequest crr;
       
  2013     crr.setContactIds(mcm.contactIds(fil));
       
  2014     crr.setManager(&mcm);
       
  2015     QVERIFY(crr.start());
       
  2016     QVERIFY(crr.cancel());
       
  2017     QVERIFY(crr.waitForFinished(100));
       
  2018     QVERIFY(crr.start());
       
  2019     QVERIFY(!crr.waitForFinished(100));
       
  2020     QVERIFY(crr.cancel());
       
  2021 
       
  2022     QContactSaveRequest csr;
       
  2023     csr.setContacts(emptyCList);
       
  2024     csr.setManager(&mcm);
       
  2025     QVERIFY(csr.start());
       
  2026     QVERIFY(csr.cancel());
       
  2027     QVERIFY(csr.waitForFinished(100));
       
  2028     QVERIFY(csr.start());
       
  2029     QVERIFY(!csr.waitForFinished(100));
       
  2030     QVERIFY(csr.cancel());
       
  2031 
       
  2032     QContactDetailDefinitionFetchRequest dfr;
       
  2033     dfr.setDefinitionNames(emptyDNList);
       
  2034     dfr.setManager(&mcm);
       
  2035     QVERIFY(dfr.start());
       
  2036     QVERIFY(dfr.cancel());
       
  2037     QVERIFY(dfr.waitForFinished(100));
       
  2038     QVERIFY(dfr.start());
       
  2039     QVERIFY(!dfr.waitForFinished(100));
       
  2040     QVERIFY(dfr.cancel());
       
  2041 
       
  2042     QContactDetailDefinitionSaveRequest dsr;
       
  2043     dsr.setDefinitions(emptyDList);
       
  2044     dsr.setManager(&mcm);
       
  2045     QVERIFY(dsr.start());
       
  2046     QVERIFY(dsr.cancel());
       
  2047     QVERIFY(dsr.waitForFinished(100));
       
  2048     QVERIFY(dsr.start());
       
  2049     QVERIFY(!dsr.waitForFinished(100));
       
  2050     QVERIFY(dsr.cancel());
       
  2051 
       
  2052     QContactDetailDefinitionRemoveRequest drr;
       
  2053     drr.setDefinitionNames(QContactType::TypeContact, emptyDNList);
       
  2054     drr.setManager(&mcm);
       
  2055     QVERIFY(drr.start());
       
  2056     QVERIFY(drr.cancel());
       
  2057     QVERIFY(drr.waitForFinished(100));
       
  2058     QVERIFY(drr.start());
       
  2059     QVERIFY(!drr.waitForFinished(100));
       
  2060     QVERIFY(drr.cancel());
       
  2061 }
       
  2062 
       
  2063 void tst_QContactAsync::testQuickDestruction()
       
  2064 {
       
  2065     QFETCH(QString, uri);
       
  2066 
       
  2067     // in this test, we create a manager, fire off a request, and delete the manager, all in quick succession
       
  2068     // this is to test for segfaults etc.
       
  2069     for (int i = 0; i < 10; i++) {
       
  2070         QContactFetchRequest cfr;
       
  2071         QContactManager *cm = prepareModel(uri);
       
  2072         cfr.setManager(cm);
       
  2073         cfr.start();
       
  2074         delete cm;
       
  2075     }
       
  2076     // in this test, we create a manager, fire off a request, delete the request, then delete the manager, all in quick succession
       
  2077     // this is to test for segfaults, etc.
       
  2078     for (int i = 0; i < 10; i++) {
       
  2079         QContactFetchRequest *cfr = new QContactFetchRequest;
       
  2080         QContactManager *cm = prepareModel(uri);
       
  2081         cfr->setManager(cm);
       
  2082         cfr->start();
       
  2083         delete cfr;
       
  2084         delete cm;
       
  2085     }
       
  2086     // in this test, we create a manager, fire off a request, delete the manager, then delete the request, all in quick succession
       
  2087     // this is to test for segfaults, etc.
       
  2088     for (int i = 0; i < 10; i++) {
       
  2089         QContactFetchRequest *cfr = new QContactFetchRequest;
       
  2090         QContactManager *cm = prepareModel(uri);
       
  2091         cfr->setManager(cm);
       
  2092         cfr->start();
       
  2093         delete cm;
       
  2094         delete cfr;
       
  2095     }
       
  2096     // in this test, we create a manager, fire off a request, and delete the request, all in quick succession
       
  2097     // this is to test for segfaults, etc.
       
  2098     QContactManager *cm = prepareModel(uri);
       
  2099     for (int i = 0; i < 10; i++) {
       
  2100         QContactFetchRequest *cfr = new QContactFetchRequest;
       
  2101         cfr->setManager(cm);
       
  2102         cfr->start();
       
  2103         delete cfr;
       
  2104     }
       
  2105     delete cm;
       
  2106 }
       
  2107 
       
  2108 void tst_QContactAsync::threadDelivery()
       
  2109 {
       
  2110     QFETCH(QString, uri);
       
  2111     QScopedPointer<QContactManager> cm(prepareModel(uri));
       
  2112     m_mainThreadId = cm->thread()->currentThreadId();
       
  2113     m_progressSlotThreadId = m_mainThreadId;
       
  2114 
       
  2115     // now perform a fetch request and check that the progress is delivered to the correct thread.
       
  2116     QContactFetchRequest *req = new QContactFetchRequest;
       
  2117     req->setManager(cm.data());
       
  2118     connect(req, SIGNAL(progress(QContactFetchRequest*,bool)), this, SLOT(progressReceived(QContactFetchRequest*, bool)));
       
  2119     req->start();
       
  2120 
       
  2121     int totalWaitTime = 0;
       
  2122     while (req->state() != QContactAbstractRequest::FinishedState) {
       
  2123         // ensure that the progress signal was delivered to the main thread.
       
  2124         QCOMPARE(m_mainThreadId, m_progressSlotThreadId);
       
  2125 
       
  2126         QTest::qWait(5); // spin until done
       
  2127         totalWaitTime += 5;
       
  2128 
       
  2129         // break after 30 seconds.
       
  2130         if (totalWaitTime > 30000) {
       
  2131             delete req;
       
  2132             QSKIP("Asynchronous request not complete after 30 seconds!", SkipSingle);
       
  2133         }
       
  2134     }
       
  2135 
       
  2136     // ensure that the progress signal was delivered to the main thread.
       
  2137     QCOMPARE(m_mainThreadId, m_progressSlotThreadId);
       
  2138     delete req;
       
  2139 }
       
  2140 
       
  2141 void tst_QContactAsync::progressReceived(QContactFetchRequest* request, bool appendOnly)
       
  2142 {
       
  2143     Q_UNUSED(appendOnly);
       
  2144     m_progressSlotThreadId = request->thread()->currentThreadId();
       
  2145 }
       
  2146 
       
  2147 void tst_QContactAsync::addManagers()
       
  2148 {
       
  2149     QTest::addColumn<QString>("uri");
       
  2150 
       
  2151     // retrieve the list of available managers
       
  2152     QStringList managers = QContactManager::availableManagers();
       
  2153 
       
  2154     // remove ones that we know will not pass
       
  2155     managers.removeAll("invalid");
       
  2156     managers.removeAll("maliciousplugin");
       
  2157     managers.removeAll("testdummy");
       
  2158 
       
  2159     foreach(QString mgr, managers) {
       
  2160         QMap<QString, QString> params;
       
  2161         QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params);
       
  2162         if (mgr == "memory") {
       
  2163             params.insert("id", "tst_QContactManager");
       
  2164             QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params);
       
  2165         }
       
  2166     }
       
  2167 }
       
  2168 
       
  2169 QContactManager* tst_QContactAsync::prepareModel(const QString& managerUri)
       
  2170 {
       
  2171     QContactManager* cm = QContactManager::fromUri(managerUri);
       
  2172 
       
  2173     // XXX TODO: ensure that this is the case:
       
  2174     // there should be no contacts in the database.
       
  2175     QList<QContactLocalId> toRemove = cm->contactIds();
       
  2176     foreach (const QContactLocalId& removeId, toRemove)
       
  2177         cm->removeContact(removeId);
       
  2178 
       
  2179     QContact a, b, c;
       
  2180     QContactName aNameDetail;
       
  2181     aNameDetail.setFirstName("Aaron");
       
  2182     aNameDetail.setLastName("Aaronson");
       
  2183     a.saveDetail(&aNameDetail);
       
  2184     QContactName bNameDetail;
       
  2185     bNameDetail.setFirstName("Bob");
       
  2186     bNameDetail.setLastName("Aaronsen");
       
  2187     b.saveDetail(&bNameDetail);
       
  2188     QContactName cNameDetail;
       
  2189     cNameDetail.setFirstName("Borris");
       
  2190     cNameDetail.setLastName("Aaronsun");
       
  2191     c.saveDetail(&cNameDetail);
       
  2192 
       
  2193     QContactPhoneNumber phn;
       
  2194     phn.setNumber("0123");
       
  2195     c.saveDetail(&phn);
       
  2196     phn.setNumber("3456");
       
  2197     b.saveDetail(&phn);
       
  2198     phn.setNumber("6789");
       
  2199     a.saveDetail(&phn);
       
  2200 
       
  2201     QContactUrl url;
       
  2202     url.setUrl("http://test.nokia.com");
       
  2203     a.saveDetail(&url);
       
  2204 
       
  2205     cm->saveContact(&a);
       
  2206     cm->saveContact(&b);
       
  2207     cm->saveContact(&c);
       
  2208 
       
  2209     QContactRelationship arb;
       
  2210     arb.setFirst(a.id());
       
  2211     arb.setSecond(b.id());
       
  2212     arb.setRelationshipType(QContactRelationship::HasManager);
       
  2213     cm->saveRelationship(&arb);
       
  2214 
       
  2215     QContactRelationship brc;
       
  2216     brc.setFirst(b.id());
       
  2217     brc.setSecond(c.id());
       
  2218     brc.setRelationshipType(QContactRelationship::HasAssistant);
       
  2219     cm->saveRelationship(&brc);
       
  2220 
       
  2221     QContactRelationship cra;
       
  2222     cra.setFirst(c.id());
       
  2223     cra.setSecond(a.id());
       
  2224     cra.setRelationshipType(QContactRelationship::HasSpouse);
       
  2225     cm->saveRelationship(&cra);
       
  2226 
       
  2227     QContactRelationship arc;
       
  2228     arc.setFirst(a.id());
       
  2229     arc.setSecond(c.id());
       
  2230     arc.setRelationshipType(QContactRelationship::HasAssistant);
       
  2231     cm->saveRelationship(&arc);
       
  2232 
       
  2233     QContactRelationship crb;
       
  2234     crb.setFirst(c.id());
       
  2235     crb.setSecond(b.id());
       
  2236     crb.setRelationshipType(QContactRelationship::IsSameAs);
       
  2237     cm->saveRelationship(&crb);
       
  2238 
       
  2239     return cm;
       
  2240 
       
  2241     // TODO: cleanup once test is complete
       
  2242 }
       
  2243 
       
  2244 QTEST_MAIN(tst_QContactAsync)
       
  2245 #include "tst_qcontactasync.moc"