| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. | 
| 4 | ** Contact: http://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the test suite of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL21$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at http://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 2.1 or version 3 as published by the Free | 
| 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and | 
| 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the | 
| 22 | ** following information to ensure the GNU Lesser General Public License | 
| 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and | 
| 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | 
| 25 | ** | 
| 26 | ** As a special exception, The Qt Company gives you certain additional | 
| 27 | ** rights. These rights are described in The Qt Company LGPL Exception | 
| 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | 
| 29 | ** | 
| 30 | ** $QT_END_LICENSE$ | 
| 31 | ** | 
| 32 | ****************************************************************************/ | 
| 33 |  | 
| 34 | #include <QtTest/QtTest> | 
| 35 |  | 
| 36 | #include <QCoreApplication> | 
| 37 | #include <QScopedPointer> | 
| 38 |  | 
| 39 | #include <QtContacts/qcontacts.h> | 
| 40 |  | 
| 41 | #include "qcontactmanagerdataholder.h" //QContactManagerDataHolder | 
| 42 |  | 
| 43 | QTCONTACTS_USE_NAMESPACE | 
| 44 |  | 
| 45 | /* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */ | 
| 46 | #define FILL_QUEUE_WITH_FETCH_REQUESTS() QContactFetchRequest fqcfr1, fqcfr2, fqcfr3; \ | 
| 47 |                                          fqcfr1.start(); \ | 
| 48 |                                          fqcfr2.start(); \ | 
| 49 |                                          fqcfr3.start(); | 
| 50 |  | 
| 51 | /* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */ | 
| 52 | #define FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(manager) QContactFetchRequest fqifr1, fqifr2, fqifr3; \ | 
| 53 |                                                 fqifr1.setManager(manager); fqifr1.start(); \ | 
| 54 |                                                 fqifr2.setManager(manager); fqifr2.start(); \ | 
| 55 |                                                 fqifr3.setManager(manager); fqifr3.start(); | 
| 56 |  | 
| 57 |  | 
| 58 | //TESTED_COMPONENT=src/contacts | 
| 59 |  | 
| 60 | // Unfortunately the plumbing isn't in place to allow cancelling requests at arbitrary points | 
| 61 | // in their processing.  So we do multiple loops until things work out.. or not | 
| 62 | #define MAX_OPTIMISTIC_SCHEDULING_LIMIT 100 | 
| 63 |  | 
| 64 |  | 
| 65 | // Thread capable QThreadSignalSpy (to avoid data races with count/appendArgS) | 
| 66 | class QThreadSignalSpy: public QObject | 
| 67 | { | 
| 68 | public: | 
| 69 |     QThreadSignalSpy(QObject *obj, const char *aSignal) | 
| 70 |     { | 
| 71 |         QMutexLocker m(&lock); | 
| 72 | #ifdef Q_CC_BOR | 
| 73 |         const int memberOffset = QObject::staticMetaObject.methodCount(); | 
| 74 | #else | 
| 75 |         static const int memberOffset = QObject::staticMetaObject.methodCount(); | 
| 76 | #endif | 
| 77 |         Q_ASSERT(obj); | 
| 78 |         Q_ASSERT(aSignal); | 
| 79 |  | 
| 80 |         if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) { | 
| 81 |             qWarning(msg: "QThreadSignalSpy: Not a valid signal, use the SIGNAL macro" ); | 
| 82 |             return; | 
| 83 |         } | 
| 84 |  | 
| 85 |         QByteArray ba = QMetaObject::normalizedSignature(method: aSignal + 1); | 
| 86 |         const QMetaObject *mo = obj->metaObject(); | 
| 87 |         int sigIndex = mo->indexOfMethod(method: ba.constData()); | 
| 88 |         if (sigIndex < 0) { | 
| 89 |             qWarning(msg: "QThreadSignalSpy: No such signal: '%s'" , ba.constData()); | 
| 90 |             return; | 
| 91 |         } | 
| 92 |  | 
| 93 |         if (!QMetaObject::connect(sender: obj, signal_index: sigIndex, receiver: this, method_index: memberOffset, | 
| 94 |                     type: Qt::DirectConnection, types: 0)) { | 
| 95 |             qWarning(msg: "QThreadSignalSpy: QMetaObject::connect returned false. Unable to connect." ); | 
| 96 |             return; | 
| 97 |         } | 
| 98 |         sig = ba; | 
| 99 |         initArgs(member: mo->method(index: sigIndex)); | 
| 100 |     } | 
| 101 |  | 
| 102 |     inline bool isValid() const { return !sig.isEmpty(); } | 
| 103 |     inline QByteArray signal() const { return sig; } | 
| 104 |  | 
| 105 |     int qt_metacall(QMetaObject::Call call, int methodId, void **a) | 
| 106 |     { | 
| 107 |         methodId = QObject::qt_metacall(call, methodId, a); | 
| 108 |         if (methodId < 0) | 
| 109 |             return methodId; | 
| 110 |  | 
| 111 |         if (call == QMetaObject::InvokeMetaMethod) { | 
| 112 |             if (methodId == 0) { | 
| 113 |                 appendArgs(a); | 
| 114 |             } | 
| 115 |             --methodId; | 
| 116 |         } | 
| 117 |         return methodId; | 
| 118 |     } | 
| 119 |  | 
| 120 |     // The QList<QVariantList> API we actually use | 
| 121 |     int count() const | 
| 122 |     { | 
| 123 |         QMutexLocker m(&lock); | 
| 124 |         return savedArgs.count(); | 
| 125 |     } | 
| 126 |     void clear() | 
| 127 |     { | 
| 128 |         QMutexLocker m(&lock); | 
| 129 |         savedArgs.clear(); | 
| 130 |     } | 
| 131 |  | 
| 132 | private: | 
| 133 |     void initArgs(const QMetaMethod &member) | 
| 134 |     { | 
| 135 |         QList<QByteArray> params = member.parameterTypes(); | 
| 136 |         for (int i = 0; i < params.count(); ++i) { | 
| 137 |             int tp = QMetaType::type(typeName: params.at(i).constData()); | 
| 138 |             if (tp == QMetaType::Void) | 
| 139 |                 qWarning(msg: "Don't know how to handle '%s', use qRegisterMetaType to register it." , | 
| 140 |                          params.at(i).constData()); | 
| 141 |             args << tp; | 
| 142 |         } | 
| 143 |     } | 
| 144 |  | 
| 145 |     void appendArgs(void **a) | 
| 146 |     { | 
| 147 |         QMutexLocker m(&lock); | 
| 148 |         QList<QVariant> list; | 
| 149 |         for (int i = 0; i < args.count(); ++i) { | 
| 150 |             QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i)); | 
| 151 |             list << QVariant(type, a[i + 1]); | 
| 152 |         } | 
| 153 |         savedArgs.append(t: list); | 
| 154 |     } | 
| 155 |  | 
| 156 |     // the full, normalized signal name | 
| 157 |     QByteArray sig; | 
| 158 |     // holds the QMetaType types for the argument list of the signal | 
| 159 |     QList<int> args; | 
| 160 |  | 
| 161 |     mutable QMutex lock; | 
| 162 |     // Different API | 
| 163 |     QList< QVariantList> savedArgs; | 
| 164 | }; | 
| 165 |  | 
| 166 |  | 
| 167 | static inline QContactId makeId(const QString &managerName, uint id) | 
| 168 | { | 
| 169 |     return QContactId(QStringLiteral("qtcontacts:basic%1:" ).arg(a: managerName), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint))); | 
| 170 | } | 
| 171 |  | 
| 172 |  | 
| 173 | class tst_QContactAsync : public QObject | 
| 174 | { | 
| 175 |     Q_OBJECT | 
| 176 |  | 
| 177 | public: | 
| 178 |     tst_QContactAsync(); | 
| 179 |     virtual ~tst_QContactAsync(); | 
| 180 |  | 
| 181 | public slots: | 
| 182 |     void initTestCase(); | 
| 183 |     void cleanupTestCase(); | 
| 184 |  | 
| 185 | private: | 
| 186 |     void addManagers(QStringList includes = QStringList()); // add standard managers to the data | 
| 187 |  | 
| 188 | private slots: | 
| 189 |     void testDestructor(); | 
| 190 |     void testDestructor_data() { addManagers(includes: QStringList(QString("maliciousplugin" ))); } | 
| 191 |  | 
| 192 |     void contactFetch(); | 
| 193 |     void contactFetch_data() { addManagers(); } | 
| 194 |     void contactFetchById(); | 
| 195 |     void contactFetchById_data() { addManagers(); } | 
| 196 |  | 
| 197 |     void contactFetchByIdWithEmptyIds(); | 
| 198 |     void contactFetchByIdWithEmptyIds_data() { addManagers(); } | 
| 199 |     void contactFetchByIdMixingEmptyIds(); | 
| 200 |     void contactFetchByIdMixingEmptyIds_data() { addManagers(); } | 
| 201 |     void contactFetchByIdWithNonExistingButValidIds(); | 
| 202 |     void contactFetchByIdWithNonExistingButValidIds_data() { addManagers(); } | 
| 203 |     void contactFetchByIdErrorHandling(); | 
| 204 |     void contactFetchByIdErrorHandling_data() { addManagers(); } | 
| 205 |  | 
| 206 |     void contactIdFetch(); | 
| 207 |     void contactIdFetch_data() { addManagers(); } | 
| 208 |     void contactRemove(); | 
| 209 |     void contactRemove_data() { addManagers(); } | 
| 210 |     void contactRemoveErrorHandling(); | 
| 211 |     void contactRemoveErrorHandling_data() {addManagers();} | 
| 212 |     void contactSave(); | 
| 213 |     void contactSave_data() { addManagers(); } | 
| 214 |     void contactSaveErrorHandling(); | 
| 215 |     void contactSaveErrorHandling_data() { addManagers(); } | 
| 216 |     void contactSaveRemovedContacts(); | 
| 217 |     void contactSaveRemovedContacts_data() { addManagers(); } | 
| 218 |     void contactSaveRemovedContactsWithCleanIds(); | 
| 219 |     void contactSaveRemovedContactsWithCleanIds_data() { addManagers(); } | 
| 220 |     void contactPartialSave(); | 
| 221 |     void contactPartialSave_data() { addManagers(); } | 
| 222 |     void contactPartialSaveAsync(); | 
| 223 |     void contactPartialSaveAsync_data() {addManagers();} | 
| 224 |  | 
| 225 |     void relationshipFetch(); | 
| 226 |     void relationshipFetch_data() { addManagers(); } | 
| 227 |     void relationshipRemove(); | 
| 228 |     void relationshipRemove_data() { addManagers(); } | 
| 229 |     void relationshipSave(); | 
| 230 |     void relationshipSave_data() { addManagers(); } | 
| 231 |  | 
| 232 |     void collectionFetch(); | 
| 233 |     void collectionFetch_data() { addManagers(); } | 
| 234 |     void collectionRemove(); | 
| 235 |     void collectionRemove_data() { addManagers(); } | 
| 236 |     void collectionSave(); | 
| 237 |     void collectionSave_data() { addManagers(); } | 
| 238 |  | 
| 239 |     void maliciousManager(); // uses it's own custom data (manager) | 
| 240 |  | 
| 241 |     void testQuickDestruction(); | 
| 242 |     void testQuickDestruction_data() { addManagers(includes: QStringList(QString("maliciousplugin" ))); } | 
| 243 |  | 
| 244 |     void threadDelivery(); | 
| 245 |     void threadDelivery_data() { addManagers(includes: QStringList(QString("maliciousplugin" ))); } | 
| 246 | protected slots: | 
| 247 |     void resultsAvailableReceived(); | 
| 248 |  | 
| 249 | private: | 
| 250 |     bool compareContactLists(QList<QContact> lista, QList<QContact> listb); | 
| 251 |     bool compareContacts(QContact ca, QContact cb); | 
| 252 |     bool containsIgnoringTimestamps(const QList<QContact>& list, const QContact& c); | 
| 253 |     bool compareIgnoringTimestamps(const QContact& ca, const QContact& cb); | 
| 254 |     bool containsAllCollectionIds(const QList<QContactCollectionId>& target, const QList<QContactCollectionId>& ids); | 
| 255 |     QContactManager* prepareModel(const QString& uri); | 
| 256 |  | 
| 257 |     Qt::HANDLE m_mainThreadId; | 
| 258 |     Qt::HANDLE m_resultsAvailableSlotThreadId; | 
| 259 |     QScopedPointer<QContactManagerDataHolder> managerDataHolder; | 
| 260 |  | 
| 261 | }; | 
| 262 |  | 
| 263 | tst_QContactAsync::tst_QContactAsync() | 
| 264 | { | 
| 265 |     // ensure we can load all of the plugins we need to. | 
| 266 |     QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/dummyplugin/plugins" ); | 
| 267 |     QCoreApplication::addLibraryPath(path); | 
| 268 |  | 
| 269 |     qRegisterMetaType<QContactAbstractRequest::State>(typeName: "QContactAbstractRequest::State" ); | 
| 270 | } | 
| 271 |  | 
| 272 | tst_QContactAsync::~tst_QContactAsync() | 
| 273 | { | 
| 274 | } | 
| 275 |  | 
| 276 | void tst_QContactAsync::initTestCase() | 
| 277 | { | 
| 278 |     managerDataHolder.reset(other: new QContactManagerDataHolder()); | 
| 279 | } | 
| 280 |  | 
| 281 | void tst_QContactAsync::cleanupTestCase() | 
| 282 | { | 
| 283 |     managerDataHolder.reset(other: 0); | 
| 284 | } | 
| 285 |  | 
| 286 | bool tst_QContactAsync::compareContactLists(QList<QContact> lista, QList<QContact> listb) | 
| 287 | { | 
| 288 |     // NOTE: This compare is contact order insensitive. | 
| 289 |  | 
| 290 |     // Remove matching contacts | 
| 291 |     foreach (QContact a, lista) { | 
| 292 |         foreach (QContact b, listb) { | 
| 293 |             if (compareContacts(ca: a, cb: b)) { | 
| 294 |                 lista.removeOne(t: a); | 
| 295 |                 listb.removeOne(t: b); | 
| 296 |                 break; | 
| 297 |             } | 
| 298 |         } | 
| 299 |     } | 
| 300 |     return (lista.count() == 0 && listb.count() == 0); | 
| 301 | } | 
| 302 |  | 
| 303 | bool tst_QContactAsync::compareContacts(QContact ca, QContact cb) | 
| 304 | { | 
| 305 |     // NOTE: This compare is contact detail order insensitive. | 
| 306 |  | 
| 307 |     if (ca.id() != cb.id()) | 
| 308 |         return false; | 
| 309 |  | 
| 310 |     QList<QContactDetail> aDetails = ca.details(); | 
| 311 |     QList<QContactDetail> bDetails = cb.details(); | 
| 312 |  | 
| 313 |     // Remove matching details | 
| 314 |     foreach (QContactDetail ad, aDetails) { | 
| 315 |         foreach (QContactDetail bd, bDetails) { | 
| 316 |             if (ad == bd) { | 
| 317 |                 ca.removeDetail(detail: &ad); | 
| 318 |                 cb.removeDetail(detail: &bd); | 
| 319 |                 break; | 
| 320 |             } | 
| 321 |  | 
| 322 |             // Special handling for timestamp | 
| 323 |             if (ad.type() == QContactTimestamp::Type && | 
| 324 |                 bd.type() == QContactTimestamp::Type) { | 
| 325 |                 QContactTimestamp at = static_cast<QContactTimestamp>(ad); | 
| 326 |                 QContactTimestamp bt = static_cast<QContactTimestamp>(bd); | 
| 327 |                 if (at.created().toString() == bt.created().toString() && | 
| 328 |                     at.lastModified().toString() == bt.lastModified().toString()) { | 
| 329 |                     ca.removeDetail(detail: &ad); | 
| 330 |                     cb.removeDetail(detail: &bd); | 
| 331 |                     break; | 
| 332 |                 } | 
| 333 |  | 
| 334 |             } | 
| 335 |         } | 
| 336 |     } | 
| 337 |     return (ca == cb); | 
| 338 | } | 
| 339 |  | 
| 340 | bool tst_QContactAsync::containsIgnoringTimestamps(const QList<QContact>& list, const QContact& c) | 
| 341 | { | 
| 342 |     QList<QContact> cl = list; | 
| 343 |     QContact a(c); | 
| 344 |     for (int i = 0; i < cl.size(); i++) { | 
| 345 |         QContact b(cl.at(i)); | 
| 346 |         if (compareIgnoringTimestamps(ca: a, cb: b)) | 
| 347 |             return true; | 
| 348 |     } | 
| 349 |  | 
| 350 |     return false; | 
| 351 | } | 
| 352 |  | 
| 353 | bool tst_QContactAsync::compareIgnoringTimestamps(const QContact& ca, const QContact& cb) | 
| 354 | { | 
| 355 |     // Compares two contacts, ignoring any timestamp details | 
| 356 |     QContact a(ca); | 
| 357 |     QContact b(cb); | 
| 358 |     QList<QContactDetail> aDetails = a.details(); | 
| 359 |     QList<QContactDetail> bDetails = b.details(); | 
| 360 |  | 
| 361 |     // They can be in any order, so loop | 
| 362 |     // First remove any matches, and any timestamps | 
| 363 |     foreach (QContactDetail d, aDetails) { | 
| 364 |         foreach (QContactDetail d2, bDetails) { | 
| 365 |             if (d == d2) { | 
| 366 |                 a.removeDetail(detail: &d); | 
| 367 |                 b.removeDetail(detail: &d2); | 
| 368 |                 break; | 
| 369 |             } | 
| 370 |  | 
| 371 |             if (d.type() == QContactTimestamp::Type) { | 
| 372 |                 a.removeDetail(detail: &d); | 
| 373 |             } | 
| 374 |  | 
| 375 |             if (d2.type() == QContactTimestamp::Type) { | 
| 376 |                 b.removeDetail(detail: &d2); | 
| 377 |             } | 
| 378 |         } | 
| 379 |     } | 
| 380 |  | 
| 381 |     if (a == b) | 
| 382 |         return true; | 
| 383 |     return false; | 
| 384 | } | 
| 385 |  | 
| 386 | bool tst_QContactAsync::containsAllCollectionIds(const QList<QContactCollectionId> &target, const QList<QContactCollectionId> &ids) | 
| 387 | { | 
| 388 |     bool containsAllIds = true; | 
| 389 |     foreach (QContactCollectionId id, ids) { | 
| 390 |         if (!target.contains(t: id)) { | 
| 391 |             containsAllIds = false; | 
| 392 |             break; | 
| 393 |         } | 
| 394 |     } | 
| 395 |     return containsAllIds; | 
| 396 | } | 
| 397 |  | 
| 398 | void tst_QContactAsync::testDestructor() | 
| 399 | { | 
| 400 |     QFETCH(QString, uri); | 
| 401 |     QContactManager* cm = prepareModel(uri); | 
| 402 |     QContactFetchRequest* req = new QContactFetchRequest; | 
| 403 |     req->setManager(cm); | 
| 404 |  | 
| 405 |     QContactManager* cm2 = prepareModel(uri); | 
| 406 |     QContactFetchRequest* req2 = new QContactFetchRequest; | 
| 407 |     req2->setManager(cm2); | 
| 408 |  | 
| 409 |     // first, delete manager then request | 
| 410 |     delete cm; | 
| 411 |     delete req; | 
| 412 |  | 
| 413 |     // second, delete request then manager | 
| 414 |     delete req2; | 
| 415 |     delete cm2; | 
| 416 | } | 
| 417 |  | 
| 418 | void tst_QContactAsync::contactFetch() | 
| 419 | { | 
| 420 |     QFETCH(QString, uri); | 
| 421 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 422 |  | 
| 423 |     QContactFetchRequest cfr; | 
| 424 |     QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchRequest); | 
| 425 |  | 
| 426 |     // initial state - not started, no manager. | 
| 427 |     QVERIFY(!cfr.isActive()); | 
| 428 |     QVERIFY(!cfr.isFinished()); | 
| 429 |     QVERIFY(!cfr.start()); | 
| 430 |     QVERIFY(!cfr.cancel()); | 
| 431 |     QVERIFY(!cfr.waitForFinished()); | 
| 432 |  | 
| 433 |     // "all contacts" retrieval | 
| 434 |     QContactFilter fil; | 
| 435 |     cfr.setManager(cm.data()); | 
| 436 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 437 |     QVERIFY(!cfr.isActive()); | 
| 438 |     QVERIFY(!cfr.isFinished()); | 
| 439 |     QVERIFY(!cfr.cancel()); | 
| 440 |     QVERIFY(!cfr.waitForFinished()); | 
| 441 |     qRegisterMetaType<QContactFetchRequest*>(typeName: "QContactFetchRequest*" ); | 
| 442 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 443 |     cfr.setFilter(fil); | 
| 444 |     QCOMPARE(cfr.filter(), fil); | 
| 445 |     QVERIFY(!cfr.cancel()); // not started | 
| 446 |  | 
| 447 |     QVERIFY(cfr.start()); | 
| 448 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 449 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 450 |     QVERIFY(cfr.waitForFinished()); | 
| 451 |     QVERIFY(cfr.isFinished()); | 
| 452 |  | 
| 453 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 454 |     spy.clear(); | 
| 455 |  | 
| 456 |     QList<QContactId> contactIds = cm->contactIds(); | 
| 457 |     QList<QContact> contacts = cfr.contacts(); | 
| 458 |     QCOMPARE(contactIds.size(), contacts.size()); | 
| 459 |     for (int i = 0; i < contactIds.size(); i++) { | 
| 460 |         QContact curr = cm->contact(contactId: contactIds.at(i)); | 
| 461 |         QVERIFY(contacts.at(i) == curr); | 
| 462 |     } | 
| 463 |  | 
| 464 |     // asynchronous detail filtering | 
| 465 |     QContactDetailFilter dfil; | 
| 466 |     dfil.setDetailType(type: QContactUrl::Type, field: QContactUrl::FieldUrl); | 
| 467 |     cfr.setFilter(dfil); | 
| 468 |     QVERIFY(cfr.filter() == dfil); | 
| 469 |     QVERIFY(!cfr.cancel()); // not started | 
| 470 |  | 
| 471 |     QVERIFY(cfr.start()); | 
| 472 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 473 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 474 |     QVERIFY(cfr.waitForFinished()); | 
| 475 |     QVERIFY(cfr.isFinished()); | 
| 476 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 477 |     spy.clear(); | 
| 478 |  | 
| 479 |     contactIds = cm->contactIds(filter: dfil); | 
| 480 |     contacts = cfr.contacts(); | 
| 481 |     QCOMPARE(contactIds.size(), contacts.size()); | 
| 482 |     for (int i = 0; i < contactIds.size(); i++) { | 
| 483 |         QContact curr = cm->contact(contactId: contactIds.at(i)); | 
| 484 |         QVERIFY(contacts.at(i) == curr); | 
| 485 |     } | 
| 486 |  | 
| 487 |     // sort order | 
| 488 |     QContactSortOrder sortOrder; | 
| 489 |     sortOrder.setDetailType(type: QContactPhoneNumber::Type, field: QContactPhoneNumber::FieldNumber); | 
| 490 |     QList<QContactSortOrder> sorting; | 
| 491 |     sorting.append(t: sortOrder); | 
| 492 |     cfr.setFilter(fil); | 
| 493 |     cfr.setSorting(sorting); | 
| 494 |     QCOMPARE(cfr.sorting(), sorting); | 
| 495 |     QVERIFY(!cfr.cancel()); // not started | 
| 496 |     QVERIFY(cfr.start()); | 
| 497 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 498 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 499 |     QVERIFY(cfr.waitForFinished()); | 
| 500 |     QVERIFY(cfr.isFinished()); | 
| 501 |  | 
| 502 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 503 |     spy.clear(); | 
| 504 |  | 
| 505 |     contactIds = cm->contactIds(sortOrders: sorting); | 
| 506 |     contacts = cfr.contacts(); | 
| 507 |     QCOMPARE(contactIds.size(), contacts.size()); | 
| 508 |     for (int i = 0; i < contactIds.size(); i++) { | 
| 509 |         QContact curr = cm->contact(contactId: contactIds.at(i)); | 
| 510 |         QVERIFY(contacts.at(i) == curr); | 
| 511 |     } | 
| 512 |  | 
| 513 |     // restrictions | 
| 514 |     sorting.clear(); | 
| 515 |     cfr.setFilter(fil); | 
| 516 |     cfr.setSorting(sorting); | 
| 517 |     QContactFetchHint fetchHint; | 
| 518 |     QList<QContactDetail::DetailType> typeHints; | 
| 519 |     typeHints << QContactName::Type; | 
| 520 |     fetchHint.setDetailTypesHint(typeHints); | 
| 521 |     cfr.setFetchHint(fetchHint); | 
| 522 |     QCOMPARE(cfr.fetchHint().detailTypesHint(), typeHints); | 
| 523 |     QVERIFY(!cfr.cancel()); // not started | 
| 524 |     QVERIFY(cfr.start()); | 
| 525 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 526 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 527 |     QVERIFY(cfr.waitForFinished()); | 
| 528 |     QVERIFY(cfr.isFinished()); | 
| 529 |  | 
| 530 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 531 |     spy.clear(); | 
| 532 |  | 
| 533 |     contactIds = cm->contactIds(sortOrders: sorting); | 
| 534 |     contacts = cfr.contacts(); | 
| 535 |     QCOMPARE(contactIds.size(), contacts.size()); | 
| 536 |     for (int i = 0; i < contactIds.size(); i++) { | 
| 537 |         // create a contact from the restricted data only (id) | 
| 538 |         QContact currFull = cm->contact(contactId: contactIds.at(i)); | 
| 539 |         QContact currRestricted; | 
| 540 |         currRestricted.setId(currFull.id()); | 
| 541 |         QList<QContactName> names = currFull.details<QContactName>(); | 
| 542 |         foreach (const QContactName& name, names) { | 
| 543 |             QContactName fullName = name; | 
| 544 |             if (!fullName.isEmpty()) { | 
| 545 |                 currRestricted.saveDetail(detail: &fullName); | 
| 546 |             } | 
| 547 |         } | 
| 548 |  | 
| 549 |         // now find the contact in the retrieved list which our restricted contact mimics | 
| 550 |         QContact retrievedRestricted; | 
| 551 |         bool found = false; | 
| 552 |         foreach (const QContact& retrieved, contacts) { | 
| 553 |             if (retrieved.id() == currRestricted.id()) { | 
| 554 |                 retrievedRestricted = retrieved; | 
| 555 |                 found = true; | 
| 556 |             } | 
| 557 |         } | 
| 558 |  | 
| 559 |         QVERIFY(found); // must exist or fail. | 
| 560 |  | 
| 561 |         // ensure that the contact is the same (except synth fields) | 
| 562 |         QList<QContactDetail> retrievedDetails = retrievedRestricted.details(); | 
| 563 |         QList<QContactDetail> expectedDetails = currRestricted.details(); | 
| 564 |         foreach (const QContactDetail& det, expectedDetails) { | 
| 565 |             // ignore backend synthesised details | 
| 566 |             // again, this requires a "default contact details" function to work properly. | 
| 567 |             if (det.type() == QContactTimestamp::Type) { | 
| 568 |                 continue; | 
| 569 |             } | 
| 570 |  | 
| 571 |             // everything else in the expected contact should be in the retrieved one. | 
| 572 |             QVERIFY(retrievedDetails.contains(det)); | 
| 573 |         } | 
| 574 |     } | 
| 575 |  | 
| 576 |     // cancelling | 
| 577 |     sorting.clear(); | 
| 578 |     cfr.setFilter(fil); | 
| 579 |     cfr.setSorting(sorting); | 
| 580 |     cfr.setFetchHint(QContactFetchHint()); | 
| 581 |  | 
| 582 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 583 |     while (true) { | 
| 584 |         QVERIFY(!cfr.cancel()); // not started | 
| 585 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 586 |         QVERIFY(cfr.start()); | 
| 587 |         if (!cfr.cancel()) { | 
| 588 |             // due to thread scheduling, async cancel might be attempted | 
| 589 |             // after the request has already finished.. so loop and try again. | 
| 590 |             spy.clear(); | 
| 591 |             cfr.waitForFinished(); | 
| 592 |             sorting.clear(); | 
| 593 |             cfr.setFilter(fil); | 
| 594 |             cfr.setSorting(sorting); | 
| 595 |             cfr.setFetchHint(QContactFetchHint()); | 
| 596 |             cfr.setFetchHint(QContactFetchHint()); | 
| 597 |             bailoutCount -= 1; | 
| 598 |             if (!bailoutCount) { | 
| 599 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 600 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 601 |                 break; | 
| 602 |             } | 
| 603 |             continue; | 
| 604 |         } | 
| 605 |  | 
| 606 |         // if we get here, then we are cancelling the request. | 
| 607 |         QVERIFY(cfr.waitForFinished()); | 
| 608 |         QVERIFY(cfr.isCanceled()); | 
| 609 |  | 
| 610 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 611 |         spy.clear(); | 
| 612 |         break; | 
| 613 |     } | 
| 614 |  | 
| 615 |     // restart, and wait for progress after cancel. | 
| 616 |     while (true) { | 
| 617 |         QVERIFY(!cfr.cancel()); // not started | 
| 618 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 619 |         QVERIFY(cfr.start()); | 
| 620 |         if (!cfr.cancel()) { | 
| 621 |             // due to thread scheduling, async cancel might be attempted | 
| 622 |             // after the request has already finished.. so loop and try again. | 
| 623 |             cfr.waitForFinished(); | 
| 624 |             sorting.clear(); | 
| 625 |             cfr.setFilter(fil); | 
| 626 |             cfr.setSorting(sorting); | 
| 627 |             cfr.setFetchHint(QContactFetchHint()); | 
| 628 |             bailoutCount -= 1; | 
| 629 |             spy.clear(); | 
| 630 |             if (!bailoutCount) { | 
| 631 |                 //qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 632 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 633 |                 break; | 
| 634 |             } | 
| 635 |             continue; | 
| 636 |         } | 
| 637 |         cfr.waitForFinished(); | 
| 638 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 639 |         spy.clear(); | 
| 640 |         QVERIFY(!cfr.isActive()); | 
| 641 |         QVERIFY(cfr.state() == QContactAbstractRequest::CanceledState); | 
| 642 |         break; | 
| 643 |     } | 
| 644 | } | 
| 645 |  | 
| 646 | void tst_QContactAsync::contactFetchById() | 
| 647 | { | 
| 648 |     QFETCH(QString, uri); | 
| 649 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 650 |  | 
| 651 |     QContactFetchByIdRequest cfr; | 
| 652 |     QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); | 
| 653 |  | 
| 654 |     // initial state - not started, no manager. | 
| 655 |     QVERIFY(!cfr.isActive()); | 
| 656 |     QVERIFY(!cfr.isFinished()); | 
| 657 |     QVERIFY(!cfr.start()); | 
| 658 |     QVERIFY(!cfr.cancel()); | 
| 659 |     QVERIFY(!cfr.waitForFinished()); | 
| 660 |  | 
| 661 |     // get all contact ids | 
| 662 |     QList<QContactId> contactIds(cm->contactIds()); | 
| 663 |  | 
| 664 |     // "all contacts" retrieval | 
| 665 |     cfr.setManager(cm.data()); | 
| 666 |     cfr.setIds(contactIds); | 
| 667 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 668 |     QVERIFY(!cfr.isActive()); | 
| 669 |     QVERIFY(!cfr.isFinished()); | 
| 670 |     QVERIFY(!cfr.cancel()); | 
| 671 |     QVERIFY(!cfr.waitForFinished()); | 
| 672 |     qRegisterMetaType<QContactFetchByIdRequest*>(typeName: "QContactFetchByIdRequest*" ); | 
| 673 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 674 |     QVERIFY(!cfr.cancel()); // not started | 
| 675 |  | 
| 676 |     QVERIFY(cfr.start()); | 
| 677 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 678 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 679 |     QVERIFY(cfr.waitForFinished()); | 
| 680 |     QVERIFY(cfr.isFinished()); | 
| 681 |  | 
| 682 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 683 |     spy.clear(); | 
| 684 |  | 
| 685 |     QList<QContact> contacts = cfr.contacts(); | 
| 686 |     QCOMPARE(contactIds.size(), contacts.size()); | 
| 687 |     for (int i = 0; i < contactIds.size(); i++) { | 
| 688 |         QContact curr = cm->contact(contactId: contactIds.at(i)); | 
| 689 |         QVERIFY(contacts.at(i) == curr); | 
| 690 |     } | 
| 691 | } | 
| 692 |  | 
| 693 | void tst_QContactAsync::contactFetchByIdWithEmptyIds() | 
| 694 | { | 
| 695 |     QFETCH(QString, uri); | 
| 696 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 697 |  | 
| 698 |     QContactFetchByIdRequest cfr; | 
| 699 |     QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); | 
| 700 |  | 
| 701 |     // List of qmpty ids. | 
| 702 |     QList<QContactId> contactIds; | 
| 703 |     contactIds.append(t: QContactId()); | 
| 704 |     contactIds.append(t: QContactId()); | 
| 705 |     contactIds.append(t: QContactId()); | 
| 706 |     int expectedCount = 3; | 
| 707 |  | 
| 708 |     // "all contacts" retrieval | 
| 709 |     cfr.setManager(cm.data()); | 
| 710 |     cfr.setIds(contactIds); | 
| 711 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 712 |     QVERIFY(!cfr.isActive()); | 
| 713 |     QVERIFY(!cfr.isFinished()); | 
| 714 |     QVERIFY(!cfr.cancel()); | 
| 715 |     QVERIFY(!cfr.waitForFinished()); | 
| 716 |     qRegisterMetaType<QContactFetchByIdRequest*>(typeName: "QContactFetchByIdRequest*" ); | 
| 717 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 718 |     QVERIFY(!cfr.cancel()); // not started | 
| 719 |  | 
| 720 |     QVERIFY(cfr.start()); | 
| 721 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 722 |     QVERIFY(cfr.waitForFinished()); | 
| 723 |     QVERIFY(cfr.isFinished()); | 
| 724 |  | 
| 725 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 726 |     spy.clear(); | 
| 727 |  | 
| 728 |     QList<QContact> contacts = cfr.contacts(); | 
| 729 |     QCOMPARE(contacts.size(), expectedCount); | 
| 730 |     int contactIndex = 0; | 
| 731 |     foreach (const QContactId id, contactIds) { | 
| 732 |             QCOMPARE(contacts.at(contactIndex), QContact()); | 
| 733 |             QCOMPARE(cfr.errorMap().value(contactIndex),QContactManager::DoesNotExistError); | 
| 734 |         contactIndex++; | 
| 735 |     } | 
| 736 | } | 
| 737 |  | 
| 738 | void tst_QContactAsync::contactFetchByIdMixingEmptyIds() | 
| 739 | { | 
| 740 |     QFETCH(QString, uri); | 
| 741 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 742 |  | 
| 743 |     QContactFetchByIdRequest cfr; | 
| 744 |     QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); | 
| 745 |  | 
| 746 |     // List of ids contains few empty ones among good ones. | 
| 747 |     int expectedCount = cm->contactIds().size(); | 
| 748 |     QList<QContactId> contactIds; | 
| 749 |     foreach (const QContactId id, cm->contactIds()) { | 
| 750 |         contactIds.append(t: QContactId()); | 
| 751 |         expectedCount ++; | 
| 752 |         contactIds.append(t: id); | 
| 753 |     } | 
| 754 |     contactIds.append(t: QContactId()); | 
| 755 |     expectedCount++; | 
| 756 |  | 
| 757 |     // "Contacts" retrieval | 
| 758 |     cfr.setManager(cm.data()); | 
| 759 |     cfr.setIds(contactIds); | 
| 760 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 761 |     QVERIFY(!cfr.isActive()); | 
| 762 |     QVERIFY(!cfr.isFinished()); | 
| 763 |     QVERIFY(!cfr.cancel()); | 
| 764 |     QVERIFY(!cfr.waitForFinished()); | 
| 765 |     qRegisterMetaType<QContactFetchByIdRequest*>(typeName: "QContactFetchByIdRequest*" ); | 
| 766 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 767 |     QVERIFY(!cfr.cancel()); // not started | 
| 768 |  | 
| 769 |     QVERIFY(cfr.start()); | 
| 770 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 771 |     QVERIFY(cfr.waitForFinished()); | 
| 772 |     QVERIFY(cfr.isFinished()); | 
| 773 |  | 
| 774 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 775 |     spy.clear(); | 
| 776 |  | 
| 777 |     QList<QContact> contacts = cfr.contacts(); | 
| 778 |     QCOMPARE(contacts.size(), expectedCount); | 
| 779 |     int contactIndex = 0; | 
| 780 |     foreach (const QContactId id, contactIds) { | 
| 781 |         if (id == QContactId()) { | 
| 782 |             QCOMPARE(contacts.at(contactIndex), QContact()); | 
| 783 |             QCOMPARE(cfr.errorMap().value(contactIndex),QContactManager::DoesNotExistError); | 
| 784 |         } else { | 
| 785 |             QCOMPARE(contacts.at(contactIndex).id(), id); | 
| 786 |         } | 
| 787 |         contactIndex++; | 
| 788 |     } | 
| 789 | } | 
| 790 |  | 
| 791 | void tst_QContactAsync::contactFetchByIdWithNonExistingButValidIds() | 
| 792 | { | 
| 793 |     QFETCH(QString, uri); | 
| 794 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 795 |  | 
| 796 |     QContactFetchByIdRequest cfr; | 
| 797 |     QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); | 
| 798 |  | 
| 799 |     // List of ids contains few empty ones among good ones. | 
| 800 |     int expectedCount = 0; | 
| 801 |     QList<QContactId> contactIds; | 
| 802 |     foreach (const QContactId id, cm->contactIds()) { | 
| 803 |         QVERIFY(cm->removeContact(id)); | 
| 804 |         contactIds.append(t: id); | 
| 805 |         expectedCount ++; | 
| 806 |     } | 
| 807 |  | 
| 808 |     // "Contacts" retrieval | 
| 809 |     cfr.setManager(cm.data()); | 
| 810 |     cfr.setIds(contactIds); | 
| 811 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 812 |     QVERIFY(!cfr.isActive()); | 
| 813 |     QVERIFY(!cfr.isFinished()); | 
| 814 |     QVERIFY(!cfr.cancel()); | 
| 815 |     QVERIFY(!cfr.waitForFinished()); | 
| 816 |     qRegisterMetaType<QContactFetchByIdRequest*>(typeName: "QContactFetchByIdRequest*" ); | 
| 817 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 818 |     QVERIFY(!cfr.cancel()); // not started | 
| 819 |  | 
| 820 |     QVERIFY(cfr.start()); | 
| 821 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 822 |     QVERIFY(cfr.waitForFinished()); | 
| 823 |     QVERIFY(cfr.isFinished()); | 
| 824 |  | 
| 825 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 826 |     spy.clear(); | 
| 827 |  | 
| 828 |     QList<QContact> contacts = cfr.contacts(); | 
| 829 |     QCOMPARE(contacts.size(), expectedCount); | 
| 830 |     int contactIndex = 0; | 
| 831 |     foreach (const QContactId id, contactIds) { | 
| 832 |         QCOMPARE(contacts.at(contactIndex), QContact()); | 
| 833 |         QCOMPARE(cfr.errorMap().value(contactIndex),QContactManager::DoesNotExistError); | 
| 834 |         contactIndex++; | 
| 835 |     } | 
| 836 | } | 
| 837 |  | 
| 838 | void tst_QContactAsync::contactFetchByIdErrorHandling() | 
| 839 | { | 
| 840 |     // Testing error handling by fetching by non existing local ids. | 
| 841 |  | 
| 842 |     QFETCH(QString, uri); | 
| 843 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 844 |  | 
| 845 |     QContactFetchByIdRequest cfrCausingErrors; | 
| 846 |     QVERIFY(cfrCausingErrors.type() == QContactAbstractRequest::ContactFetchByIdRequest); | 
| 847 |  | 
| 848 |     // Check initial state - not started, no manager. | 
| 849 |     QVERIFY(!cfrCausingErrors.isActive()); | 
| 850 |     QVERIFY(!cfrCausingErrors.isFinished()); | 
| 851 |     QVERIFY(!cfrCausingErrors.start()); | 
| 852 |     QVERIFY(!cfrCausingErrors.cancel()); | 
| 853 |     QVERIFY(!cfrCausingErrors.waitForFinished()); | 
| 854 |  | 
| 855 |     // Prepare non-existing contact ids for request we like to result. | 
| 856 |     QList<QContactId> nonExistingContactIds; | 
| 857 |     nonExistingContactIds << QContactId() << QContactId() << QContactId(); | 
| 858 |  | 
| 859 |     // "Make contacts" retrieval with some extra checking. | 
| 860 |     cfrCausingErrors.setManager(cm.data()); | 
| 861 |     cfrCausingErrors.setIds(nonExistingContactIds); | 
| 862 |     QCOMPARE(cfrCausingErrors.manager(), cm.data()); | 
| 863 |     QVERIFY(!cfrCausingErrors.isActive()); | 
| 864 |     QVERIFY(!cfrCausingErrors.isFinished()); | 
| 865 |     QVERIFY(!cfrCausingErrors.cancel()); | 
| 866 |     QVERIFY(!cfrCausingErrors.waitForFinished()); | 
| 867 |  | 
| 868 |     qRegisterMetaType<QContactFetchByIdRequest*>(typeName: "QContactFetchByIdRequest*" ); | 
| 869 |     QThreadSignalSpy spy2(&cfrCausingErrors, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 870 |  | 
| 871 |     QVERIFY(!cfrCausingErrors.cancel()); // not started | 
| 872 |  | 
| 873 |     QVERIFY(cfrCausingErrors.start()); | 
| 874 |     QVERIFY((cfrCausingErrors.isActive() && cfrCausingErrors.state() == QContactAbstractRequest::ActiveState) || cfrCausingErrors.isFinished()); | 
| 875 |     QVERIFY(cfrCausingErrors.waitForFinished()); | 
| 876 |     QVERIFY(cfrCausingErrors.isFinished()); | 
| 877 |  | 
| 878 |     QVERIFY(spy2.count() >= 1); // active + finished progress signals | 
| 879 |     spy2.clear(); | 
| 880 |  | 
| 881 |     // Check errors, they should be stored in the errorMap using index of local id in the request as a key for the error. | 
| 882 |     // Note, the returned values are actually set in to the errorMap by common code in qcontactmanagerenginev2wrapper_p.cpp. | 
| 883 |  | 
| 884 |     QVERIFY(cfrCausingErrors.errorMap().value(0) == QContactManager::DoesNotExistError); | 
| 885 |     QVERIFY(cfrCausingErrors.errorMap().value(1) == QContactManager::DoesNotExistError); | 
| 886 |     QVERIFY(cfrCausingErrors.errorMap().value(2) == QContactManager::DoesNotExistError); | 
| 887 | } | 
| 888 |  | 
| 889 | void tst_QContactAsync::contactIdFetch() | 
| 890 | { | 
| 891 |     QFETCH(QString, uri); | 
| 892 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 893 |     QContactIdFetchRequest cfr; | 
| 894 |     QVERIFY(cfr.type() == QContactAbstractRequest::ContactIdFetchRequest); | 
| 895 |  | 
| 896 |     // initial state - not started, no manager. | 
| 897 |     QVERIFY(!cfr.isActive()); | 
| 898 |     QVERIFY(!cfr.isFinished()); | 
| 899 |     QVERIFY(!cfr.start()); | 
| 900 |     QVERIFY(!cfr.cancel()); | 
| 901 |     QVERIFY(!cfr.waitForFinished()); | 
| 902 |  | 
| 903 |     // "all contacts" retrieval | 
| 904 |     QContactFilter fil; | 
| 905 |     cfr.setManager(cm.data()); | 
| 906 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 907 |     QVERIFY(!cfr.isActive()); | 
| 908 |     QVERIFY(!cfr.isFinished()); | 
| 909 |     QVERIFY(!cfr.cancel()); | 
| 910 |     QVERIFY(!cfr.waitForFinished()); | 
| 911 |     qRegisterMetaType<QContactIdFetchRequest*>(typeName: "QContactIdFetchRequest*" ); | 
| 912 |  | 
| 913 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 914 |     cfr.setFilter(fil); | 
| 915 |     QCOMPARE(cfr.filter(), fil); | 
| 916 |     QVERIFY(!cfr.cancel()); // not started | 
| 917 |     QVERIFY(cfr.start()); | 
| 918 |  | 
| 919 |     QVERIFY((cfr.isActive() &&cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 920 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 921 |     QVERIFY(cfr.waitForFinished()); | 
| 922 |     QVERIFY(cfr.isFinished()); | 
| 923 |  | 
| 924 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 925 |     spy.clear(); | 
| 926 |  | 
| 927 |     QList<QContactId> contactIds = cm->contactIds(); | 
| 928 |     QList<QContactId> result = cfr.ids(); | 
| 929 |     QCOMPARE(contactIds, result); | 
| 930 |  | 
| 931 |     // asynchronous detail filtering | 
| 932 |     QContactDetailFilter dfil; | 
| 933 |     dfil.setDetailType(type: QContactUrl::Type, field: QContactUrl::FieldUrl); | 
| 934 |     cfr.setFilter(dfil); | 
| 935 |     QVERIFY(cfr.filter() == dfil); | 
| 936 |     QVERIFY(!cfr.cancel()); // not started | 
| 937 |  | 
| 938 |     QVERIFY(cfr.start()); | 
| 939 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 940 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 941 |     QVERIFY(cfr.waitForFinished()); | 
| 942 |     QVERIFY(cfr.isFinished()); | 
| 943 |  | 
| 944 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 945 |     spy.clear(); | 
| 946 |  | 
| 947 |     contactIds = cm->contactIds(filter: dfil); | 
| 948 |     result = cfr.ids(); | 
| 949 |     QCOMPARE(contactIds, result); | 
| 950 |  | 
| 951 |     // sort order | 
| 952 |     QContactSortOrder sortOrder; | 
| 953 |     sortOrder.setDetailType(type: QContactPhoneNumber::Type, field: QContactPhoneNumber::FieldNumber); | 
| 954 |     QList<QContactSortOrder> sorting; | 
| 955 |     sorting.append(t: sortOrder); | 
| 956 |     cfr.setFilter(fil); | 
| 957 |     cfr.setSorting(sorting); | 
| 958 |     QCOMPARE(cfr.sorting(), sorting); | 
| 959 |     QVERIFY(!cfr.cancel()); // not started | 
| 960 |     QVERIFY(cfr.start()); | 
| 961 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 962 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 963 |     QVERIFY(cfr.waitForFinished()); | 
| 964 |     QVERIFY(cfr.isFinished()); | 
| 965 |  | 
| 966 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 967 |     spy.clear(); | 
| 968 |  | 
| 969 |     contactIds = cm->contactIds(sortOrders: sorting); | 
| 970 |     result = cfr.ids(); | 
| 971 |     QCOMPARE(contactIds, result); | 
| 972 |  | 
| 973 |     // cancelling | 
| 974 |     sorting.clear(); | 
| 975 |     cfr.setFilter(fil); | 
| 976 |     cfr.setSorting(sorting); | 
| 977 |  | 
| 978 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 979 |     while (true) { | 
| 980 |         QVERIFY(!cfr.cancel()); // not started | 
| 981 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 982 |         QVERIFY(cfr.start()); | 
| 983 |         if (!cfr.cancel()) { | 
| 984 |             // due to thread scheduling, async cancel might be attempted | 
| 985 |             // after the request has already finished.. so loop and try again. | 
| 986 |             cfr.waitForFinished(); | 
| 987 |             sorting.clear(); | 
| 988 |             cfr.setFilter(fil); | 
| 989 |             cfr.setSorting(sorting); | 
| 990 |             bailoutCount -= 1; | 
| 991 |             spy.clear(); | 
| 992 |             if (!bailoutCount) { | 
| 993 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 994 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 995 |                 break; | 
| 996 |             } | 
| 997 |             continue; | 
| 998 |         } | 
| 999 |  | 
| 1000 |         // if we get here, then we are cancelling the request. | 
| 1001 |         QVERIFY(cfr.waitForFinished()); | 
| 1002 |         QVERIFY(cfr.isCanceled()); | 
| 1003 |  | 
| 1004 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 1005 |         spy.clear(); | 
| 1006 |  | 
| 1007 |         break; | 
| 1008 |     } | 
| 1009 |  | 
| 1010 |     // restart, and wait for progress after cancel. | 
| 1011 |     while (true) { | 
| 1012 |         QVERIFY(!cfr.cancel()); // not started | 
| 1013 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 1014 |         QVERIFY(cfr.start()); | 
| 1015 |         if (!cfr.cancel()) { | 
| 1016 |             // due to thread scheduling, async cancel might be attempted | 
| 1017 |             // after the request has already finished.. so loop and try again. | 
| 1018 |             cfr.waitForFinished(); | 
| 1019 |             sorting.clear(); | 
| 1020 |             cfr.setFilter(fil); | 
| 1021 |             cfr.setSorting(sorting); | 
| 1022 |             bailoutCount -= 1; | 
| 1023 |             if (!bailoutCount) { | 
| 1024 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 1025 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 1026 |                 break; | 
| 1027 |             } | 
| 1028 |             continue; | 
| 1029 |         } | 
| 1030 |         cfr.waitForFinished(); | 
| 1031 |         QVERIFY(cfr.isCanceled()); | 
| 1032 |  | 
| 1033 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 1034 |         spy.clear(); | 
| 1035 |         break; | 
| 1036 |     } | 
| 1037 |  | 
| 1038 |     // Error cases not tested here as can not generate common error case | 
| 1039 |     // for all backends. | 
| 1040 | } | 
| 1041 |  | 
| 1042 | void tst_QContactAsync::contactRemove() | 
| 1043 | { | 
| 1044 |     QFETCH(QString, uri); | 
| 1045 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1046 |     QContactRemoveRequest crr; | 
| 1047 |     QVERIFY(crr.type() == QContactAbstractRequest::ContactRemoveRequest); | 
| 1048 |  | 
| 1049 |     // initial state - not started, no manager. | 
| 1050 |     QVERIFY(!crr.isActive()); | 
| 1051 |     QVERIFY(!crr.isFinished()); | 
| 1052 |     QVERIFY(!crr.start()); | 
| 1053 |     QVERIFY(!crr.cancel()); | 
| 1054 |     QVERIFY(!crr.waitForFinished()); | 
| 1055 |  | 
| 1056 |     // specific contact set | 
| 1057 |     crr.setContactId(cm->contactIds()[0]); | 
| 1058 |     QVERIFY(crr.contactIds() == QList<QContactId>() << cm->contactIds()[0]); | 
| 1059 |  | 
| 1060 |     // specific contact removal via detail filter | 
| 1061 |     int originalCount = cm->contactIds().size(); | 
| 1062 |     QContactDetailFilter dfil; | 
| 1063 |     dfil.setDetailType(type: QContactUrl::Type); | 
| 1064 |     crr.setContactIds(cm->contactIds(filter: dfil)); | 
| 1065 |     crr.setManager(cm.data()); | 
| 1066 |     QCOMPARE(crr.manager(), cm.data()); | 
| 1067 |     QVERIFY(!crr.isActive()); | 
| 1068 |     QVERIFY(!crr.isFinished()); | 
| 1069 |     QVERIFY(!crr.cancel()); | 
| 1070 |     QVERIFY(!crr.waitForFinished()); | 
| 1071 |     qRegisterMetaType<QContactRemoveRequest*>(typeName: "QContactRemoveRequest*" ); | 
| 1072 |     QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1073 |     QVERIFY(!crr.cancel()); // not started | 
| 1074 |  | 
| 1075 |     QVERIFY(!cm->contactIds(dfil).isEmpty()); | 
| 1076 |  | 
| 1077 |     QVERIFY(crr.start()); | 
| 1078 |  | 
| 1079 |     QVERIFY((crr.isActive() &&crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); | 
| 1080 |     //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable | 
| 1081 |     QVERIFY(crr.waitForFinished()); | 
| 1082 |     QVERIFY(crr.isFinished()); | 
| 1083 |  | 
| 1084 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1085 |     spy.clear(); | 
| 1086 |  | 
| 1087 |     QCOMPARE(cm->contactIds().size(), originalCount - 1); | 
| 1088 |     QVERIFY(cm->contactIds(dfil).isEmpty()); | 
| 1089 |  | 
| 1090 |     // remove all contacts | 
| 1091 |     // delete everything. | 
| 1092 |     crr.setContactIds(cm->contactIds()); | 
| 1093 |  | 
| 1094 |     QVERIFY(!crr.cancel()); // not started | 
| 1095 |     QVERIFY(crr.start()); | 
| 1096 |  | 
| 1097 |     QVERIFY((crr.isActive() && crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); | 
| 1098 |     //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable | 
| 1099 |     QVERIFY(crr.waitForFinished()); | 
| 1100 |     QVERIFY(crr.isFinished()); | 
| 1101 |  | 
| 1102 |     QCOMPARE(cm->contactIds().size(), 0); // no contacts should be left. | 
| 1103 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1104 |     spy.clear(); | 
| 1105 |  | 
| 1106 |     // cancelling | 
| 1107 |     QContact temp; | 
| 1108 |     QContactName nameDetail; | 
| 1109 |     nameDetail.setFirstName("Should not be removed" ); | 
| 1110 |     temp.saveDetail(detail: &nameDetail); | 
| 1111 |     cm->saveContact(contact: &temp); | 
| 1112 |     crr.setContactIds(cm->contactIds(filter: dfil)); | 
| 1113 |  | 
| 1114 |     // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 1115 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 1116 |     while (true) { | 
| 1117 |         QVERIFY(!crr.cancel()); // not started | 
| 1118 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 1119 |         QVERIFY(spy.count() == 0); | 
| 1120 |         QVERIFY(crr.start()); | 
| 1121 |         if (!crr.cancel()) { | 
| 1122 |             // due to thread scheduling, async cancel might be attempted | 
| 1123 |             // after the request has already finished.. so loop and try again. | 
| 1124 |             crr.waitForFinished(); | 
| 1125 |             crr.setContactIds(cm->contactIds(filter: dfil)); | 
| 1126 |             temp.setId(QContactId()); | 
| 1127 |             if (!cm->saveContact(contact: &temp)) { | 
| 1128 |                 QSKIP("Unable to save temporary contact for remove request cancellation test!" ); | 
| 1129 |             } | 
| 1130 |             bailoutCount -= 1; | 
| 1131 |             if (!bailoutCount) { | 
| 1132 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 1133 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 1134 |                 break; | 
| 1135 |             } | 
| 1136 |             spy.clear(); | 
| 1137 |             continue; | 
| 1138 |         } | 
| 1139 |  | 
| 1140 |         // if we get here, then we are cancelling the request. | 
| 1141 |         QVERIFY(crr.waitForFinished()); | 
| 1142 |         QVERIFY(crr.isCanceled()); | 
| 1143 |         QCOMPARE(cm->contactIds().size(), 1); | 
| 1144 |         QCOMPARE(cm->contactIds(), crr.contactIds()); | 
| 1145 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 1146 |         spy.clear(); | 
| 1147 |         break; | 
| 1148 |     } | 
| 1149 |  | 
| 1150 |     // restart, and wait for progress after cancel. | 
| 1151 |     while (true) { | 
| 1152 |         QVERIFY(!crr.cancel()); // not started | 
| 1153 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 1154 |         QVERIFY(crr.start()); | 
| 1155 |         if (!crr.cancel()) { | 
| 1156 |             // due to thread scheduling, async cancel might be attempted | 
| 1157 |             // after the request has already finished.. so loop and try again. | 
| 1158 |             crr.waitForFinished(); | 
| 1159 |             crr.setContactIds(cm->contactIds(filter: dfil)); | 
| 1160 |             temp.setId(QContactId()); | 
| 1161 |             cm->saveContact(contact: &temp); | 
| 1162 |             bailoutCount -= 1; | 
| 1163 |             if (!bailoutCount) { | 
| 1164 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 1165 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 1166 |                 break; | 
| 1167 |             } | 
| 1168 |             spy.clear(); | 
| 1169 |             continue; | 
| 1170 |         } | 
| 1171 |         crr.waitForFinished(); | 
| 1172 |         QVERIFY(crr.isCanceled()); | 
| 1173 |         QCOMPARE(cm->contactIds().size(), 1); | 
| 1174 |         QCOMPARE(cm->contactIds(), crr.contactIds()); | 
| 1175 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 1176 |         spy.clear(); | 
| 1177 |         break; | 
| 1178 |     } | 
| 1179 |  | 
| 1180 | } | 
| 1181 |  | 
| 1182 | void tst_QContactAsync::contactRemoveErrorHandling() { | 
| 1183 |     QFETCH(QString, uri); | 
| 1184 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1185 |  | 
| 1186 |     // Make save request and make initial setup for it with some verifications. | 
| 1187 |     QContactSaveRequest csr; | 
| 1188 |     QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); | 
| 1189 |     csr.setManager(cm.data()); | 
| 1190 |     QCOMPARE(csr.manager(), cm.data()); | 
| 1191 |     QVERIFY(!csr.isActive()); | 
| 1192 |     QVERIFY(!csr.isFinished()); | 
| 1193 |     QVERIFY(!csr.cancel()); | 
| 1194 |     QVERIFY(!csr.waitForFinished()); | 
| 1195 |     qRegisterMetaType<QContactSaveRequest*>(typeName: "QContactSaveRequest*" ); | 
| 1196 |     QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1197 |  | 
| 1198 |     // Setup a two identical lists of contacts for testing save and remove. | 
| 1199 |     QContact testContact1, testContact2, testContact3, testContact4, testContact5, testContact6; | 
| 1200 |     QContactName nameDetail; | 
| 1201 |     nameDetail.setFirstName("Test Contact1" ); | 
| 1202 |     testContact1.saveDetail(detail: &nameDetail); | 
| 1203 |     nameDetail.setFirstName("Test Contact2" ); | 
| 1204 |     testContact2.saveDetail(detail: &nameDetail); | 
| 1205 |     nameDetail.setFirstName("Test Contact3" ); | 
| 1206 |     testContact3.saveDetail(detail: &nameDetail); | 
| 1207 |     nameDetail.setFirstName("Test Contact4" ); | 
| 1208 |     testContact4.saveDetail(detail: &nameDetail); | 
| 1209 |     nameDetail.setFirstName("Test Contact5" ); | 
| 1210 |     testContact5.saveDetail(detail: &nameDetail); | 
| 1211 |     nameDetail.setFirstName("Test Contact6" ); | 
| 1212 |     testContact6.saveDetail(detail: &nameDetail); | 
| 1213 |     QList<QContact> saveList, testContacts; | 
| 1214 |     saveList << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6; | 
| 1215 |     testContacts << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6; | 
| 1216 |  | 
| 1217 |     // Check save requests takes test contacts ok, start save and wait for finished. | 
| 1218 |     csr.setContacts(testContacts); | 
| 1219 |     QCOMPARE(csr.contacts(), saveList); | 
| 1220 |     QVERIFY(!csr.cancel()); // not started | 
| 1221 |     QVERIFY(csr.start()); | 
| 1222 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 1223 |     QVERIFY(csr.waitForFinished()); | 
| 1224 |     QVERIFY(csr.isFinished()); | 
| 1225 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1226 |     spy.clear(); | 
| 1227 |  | 
| 1228 |     // foreach (QContact testc, csr.contacts()) { | 
| 1229 |     //     qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CONTACT: " << testc.id().managerUri(); | 
| 1230 |     //     qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CONTACT: " << testc.id().id(); | 
| 1231 |     // } | 
| 1232 |     // qDebug() << "Returned errors:" << csr.errorMap(); | 
| 1233 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1234 |  | 
| 1235 |     // Setup remove initial remove request and verify it takes data ok. | 
| 1236 |     QContactRemoveRequest contactRemoveRequest; | 
| 1237 |     QVERIFY(contactRemoveRequest.type() == QContactAbstractRequest::ContactRemoveRequest); | 
| 1238 |     contactRemoveRequest.setManager(cm.data()); | 
| 1239 |     QCOMPARE(contactRemoveRequest.manager(), cm.data()); | 
| 1240 |     QVERIFY(!contactRemoveRequest.isActive()); | 
| 1241 |     QVERIFY(!contactRemoveRequest.isFinished()); | 
| 1242 |     QVERIFY(!contactRemoveRequest.cancel()); | 
| 1243 |     QVERIFY(!contactRemoveRequest.waitForFinished()); | 
| 1244 |     qRegisterMetaType<QContactSaveRequest*>(typeName: "QContactSaveRequest*" ); | 
| 1245 |     QThreadSignalSpy spy2(&contactRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1246 |  | 
| 1247 |     // Setup valid and invalid contact ids for remove request, start it and wait for finished. | 
| 1248 |     QContactId emptyId; | 
| 1249 |     QContactId failingId = makeId(managerName: "Failing" , id: 0); | 
| 1250 |     QList<QContactId> toRemove; | 
| 1251 |     toRemove << emptyId << cm.data()->contactIds(); | 
| 1252 |     toRemove.insert(i: 3, t: emptyId); | 
| 1253 |     toRemove.insert(i: 4, t: failingId); | 
| 1254 |     toRemove << emptyId << failingId << failingId; | 
| 1255 |     // qDebug() << "TO REMOVE: " << toRemove; | 
| 1256 |     contactRemoveRequest.setContactIds(toRemove); | 
| 1257 |     QCOMPARE(contactRemoveRequest.contactIds(), toRemove); | 
| 1258 |     QVERIFY(!contactRemoveRequest.cancel()); // not started | 
| 1259 |     QVERIFY(contactRemoveRequest.start()); | 
| 1260 |     QVERIFY((contactRemoveRequest.isActive() && contactRemoveRequest.state() == QContactAbstractRequest::ActiveState) || contactRemoveRequest.isFinished()); | 
| 1261 |     QVERIFY(contactRemoveRequest.waitForFinished()); | 
| 1262 |     QVERIFY(contactRemoveRequest.isFinished()); | 
| 1263 |     QVERIFY(spy2.count() >= 1); // active + finished progress signals | 
| 1264 |     spy2.clear(); | 
| 1265 |  | 
| 1266 |     // Check ok and errors, empty ids in the beginning, middle and in the end of the contact id list, | 
| 1267 |     // qDebug() << "Returned errors:" << contactRemoveRequest.errorMap(); | 
| 1268 |     QVERIFY(contactRemoveRequest.errorMap().value(0) == QContactManager::DoesNotExistError); | 
| 1269 |     QVERIFY(contactRemoveRequest.errorMap().value(1) == QContactManager::NoError); | 
| 1270 |     QVERIFY(contactRemoveRequest.errorMap().value(2) == QContactManager::NoError); | 
| 1271 |     QVERIFY(contactRemoveRequest.errorMap().value(3) == QContactManager::DoesNotExistError); | 
| 1272 |     QVERIFY(contactRemoveRequest.errorMap().value(4) == QContactManager::DoesNotExistError); | 
| 1273 |     QVERIFY(contactRemoveRequest.errorMap().value(5) == QContactManager::NoError); | 
| 1274 |     QVERIFY(contactRemoveRequest.errorMap().value(6) == QContactManager::NoError); | 
| 1275 |     QVERIFY(contactRemoveRequest.errorMap().value(7) == QContactManager::NoError); | 
| 1276 |     QVERIFY(contactRemoveRequest.errorMap().value(9) == QContactManager::NoError); | 
| 1277 |     QVERIFY(contactRemoveRequest.errorMap().value(10) == QContactManager::NoError); | 
| 1278 |     QVERIFY(contactRemoveRequest.errorMap().value(11) == QContactManager::NoError); | 
| 1279 |     QVERIFY(contactRemoveRequest.errorMap().value(12) == QContactManager::DoesNotExistError); | 
| 1280 |     QVERIFY(contactRemoveRequest.errorMap().value(13) == QContactManager::DoesNotExistError); | 
| 1281 |     QVERIFY(contactRemoveRequest.errorMap().value(14) == QContactManager::DoesNotExistError); | 
| 1282 |  | 
| 1283 |     // Check that all the contacts have been removed | 
| 1284 |     QList<QContactId> contactsLeft = cm.data()->contactIds(); | 
| 1285 |     QVERIFY(contactsLeft.isEmpty()); | 
| 1286 | } | 
| 1287 |  | 
| 1288 | void tst_QContactAsync::contactSave() | 
| 1289 | { | 
| 1290 |     QFETCH(QString, uri); | 
| 1291 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1292 |     QContactSaveRequest csr; | 
| 1293 |     QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); | 
| 1294 |  | 
| 1295 |     // initial state - not started, no manager. | 
| 1296 |     QVERIFY(!csr.isActive()); | 
| 1297 |     QVERIFY(!csr.isFinished()); | 
| 1298 |     QVERIFY(!csr.start()); | 
| 1299 |     QVERIFY(!csr.cancel()); | 
| 1300 |     QVERIFY(!csr.waitForFinished()); | 
| 1301 |  | 
| 1302 |     // save a new contact | 
| 1303 |     int originalCount = cm->contactIds().size(); | 
| 1304 |     QContact testContact; | 
| 1305 |     QContactName nameDetail; | 
| 1306 |     nameDetail.setFirstName("Test Contact" ); | 
| 1307 |     testContact.saveDetail(detail: &nameDetail); | 
| 1308 |     QList<QContact> saveList; | 
| 1309 |     saveList << testContact; | 
| 1310 |     csr.setManager(cm.data()); | 
| 1311 |     QCOMPARE(csr.manager(), cm.data()); | 
| 1312 |     QVERIFY(!csr.isActive()); | 
| 1313 |     QVERIFY(!csr.isFinished()); | 
| 1314 |     QVERIFY(!csr.cancel()); | 
| 1315 |     QVERIFY(!csr.waitForFinished()); | 
| 1316 |     qRegisterMetaType<QContactSaveRequest*>(typeName: "QContactSaveRequest*" ); | 
| 1317 |     QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1318 |     csr.setContact(testContact); | 
| 1319 |     QCOMPARE(csr.contacts(), saveList); | 
| 1320 |     QVERIFY(!csr.cancel()); // not started | 
| 1321 |     QVERIFY(csr.start()); | 
| 1322 |  | 
| 1323 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 1324 |     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable | 
| 1325 |     QVERIFY(csr.waitForFinished()); | 
| 1326 |     QVERIFY(csr.isFinished()); | 
| 1327 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1328 |     spy.clear(); | 
| 1329 |  | 
| 1330 |     QList<QContact> expected = csr.contacts(); | 
| 1331 |     QCOMPARE(expected.size(), 1); | 
| 1332 |     QList<QContact> result; | 
| 1333 |     result << cm->contact(contactId: expected.first().id()); | 
| 1334 |     //some backends add extra fields, so this doesn't work: | 
| 1335 |     //QCOMPARE(result, expected); | 
| 1336 |     // XXX: really, we should use isSuperset() from tst_QContactManager, but this will do for now: | 
| 1337 |     QVERIFY(result.first().detail<QContactName>() == nameDetail); | 
| 1338 |  | 
| 1339 |     // check if the contact was saved on default collection | 
| 1340 |     QCOMPARE(result.first().collectionId().toString(), cm->defaultCollectionId().toString()); | 
| 1341 |     QCOMPARE(cm->contactIds().size(), originalCount + 1); | 
| 1342 |  | 
| 1343 |     // update a previously saved contact | 
| 1344 |     QContactPhoneNumber phn; | 
| 1345 |     phn.setNumber("12345678" ); | 
| 1346 |     testContact = result.first(); | 
| 1347 |     testContact.saveDetail(detail: &phn); | 
| 1348 |     saveList.clear(); | 
| 1349 |     saveList << testContact; | 
| 1350 |     csr.setContacts(saveList); | 
| 1351 |     QCOMPARE(csr.contacts(), saveList); | 
| 1352 |     QVERIFY(!csr.cancel()); // not started | 
| 1353 |     QVERIFY(csr.start()); | 
| 1354 |  | 
| 1355 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 1356 |     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable | 
| 1357 |     QVERIFY(csr.waitForFinished()); | 
| 1358 |  | 
| 1359 |     QVERIFY(csr.isFinished()); | 
| 1360 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1361 |     spy.clear(); | 
| 1362 |  | 
| 1363 |     expected = csr.contacts(); | 
| 1364 |     result.clear(); | 
| 1365 |     result << cm->contact(contactId: expected.first().id()); | 
| 1366 |     //QVERIFY(compareContactLists(result, expected)); | 
| 1367 |  | 
| 1368 |     //here we can't compare the whole contact details, testContact would be updated by async call because we just use QThreadSignalSpy to receive signals. | 
| 1369 |     //QVERIFY(containsIgnoringTimestamps(result, testContact)); | 
| 1370 |     // XXX: really, we should use isSuperset() from tst_QContactManager, but this will do for now: | 
| 1371 |     QVERIFY(result.first().detail<QContactPhoneNumber>().number() == phn.number()); | 
| 1372 |  | 
| 1373 |     QCOMPARE(cm->contactIds().size(), originalCount + 1); | 
| 1374 |  | 
| 1375 |     // cancelling | 
| 1376 |     QContact temp = testContact; | 
| 1377 |     QContactUrl url; | 
| 1378 |     url.setUrl("should not get saved" ); | 
| 1379 |     temp.saveDetail(detail: &url); | 
| 1380 |     saveList.clear(); | 
| 1381 |     saveList << temp; | 
| 1382 |     csr.setContacts(saveList); | 
| 1383 |  | 
| 1384 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 1385 |     while (true) { | 
| 1386 |         QVERIFY(!csr.cancel()); // not started | 
| 1387 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 1388 |         QVERIFY(csr.start()); | 
| 1389 |         if (!csr.cancel()) { | 
| 1390 |             // due to thread scheduling, async cancel might be attempted | 
| 1391 |             // after the request has already finished.. so loop and try again. | 
| 1392 |             csr.waitForFinished(); | 
| 1393 |             saveList = csr.contacts(); | 
| 1394 |             if (cm->contactIds().size() > (originalCount + 1) && !cm->removeContact(contactId: saveList.at(i: 0).id())) { | 
| 1395 |                 QSKIP("Unable to remove saved contact to test cancellation of contact save request" ); | 
| 1396 |             } | 
| 1397 |             saveList.clear(); | 
| 1398 |             saveList << temp; | 
| 1399 |             csr.setContacts(saveList); | 
| 1400 |             bailoutCount -= 1; | 
| 1401 |             if (!bailoutCount) { | 
| 1402 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 1403 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 1404 |                 break; | 
| 1405 |             } | 
| 1406 |             spy.clear(); | 
| 1407 |             continue; | 
| 1408 |         } | 
| 1409 |  | 
| 1410 |         // if we get here, then we are cancelling the request. | 
| 1411 |         QVERIFY(csr.waitForFinished()); | 
| 1412 |         QVERIFY(csr.isCanceled()); | 
| 1413 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 1414 |         spy.clear(); | 
| 1415 |  | 
| 1416 |         // verify that the changes were not saved | 
| 1417 |         expected.clear(); | 
| 1418 |         QList<QContactId> allContacts = cm->contactIds(); | 
| 1419 |         for (int i = 0; i < allContacts.size(); i++) { | 
| 1420 |             expected.append(t: cm->contact(contactId: allContacts.at(i))); | 
| 1421 |         } | 
| 1422 |         QVERIFY(!expected.contains(temp)); | 
| 1423 |         QCOMPARE(cm->contactIds().size(), originalCount + 1); | 
| 1424 |         break; | 
| 1425 |     } | 
| 1426 |     // restart, and wait for progress after cancel. | 
| 1427 |  | 
| 1428 |     while (true) { | 
| 1429 |         QVERIFY(!csr.cancel()); // not started | 
| 1430 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 1431 |         QVERIFY(csr.start()); | 
| 1432 |         if (!csr.cancel()) { | 
| 1433 |             // due to thread scheduling, async cancel might be attempted | 
| 1434 |             // after the request has already finished.. so loop and try again. | 
| 1435 |             csr.waitForFinished(); | 
| 1436 |             saveList = csr.contacts(); | 
| 1437 |             if (cm->contactIds().size() > (originalCount + 1) && !cm->removeContact(contactId: saveList.at(i: 0).id())) { | 
| 1438 |                 QSKIP("Unable to remove saved contact to test cancellation of contact save request" ); | 
| 1439 |             } | 
| 1440 |             saveList.clear(); | 
| 1441 |             saveList << temp; | 
| 1442 |             csr.setContacts(saveList); | 
| 1443 |             bailoutCount -= 1; | 
| 1444 |             if (!bailoutCount) { | 
| 1445 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 1446 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 1447 |                 break; | 
| 1448 |             } | 
| 1449 |             spy.clear(); | 
| 1450 |             continue; | 
| 1451 |         } | 
| 1452 |         csr.waitForFinished(); // now wait until finished (if it hasn't already). | 
| 1453 |         QVERIFY(csr.isCanceled()); | 
| 1454 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 1455 |         spy.clear(); | 
| 1456 |  | 
| 1457 |         // verify that the changes were not saved | 
| 1458 |         expected.clear(); | 
| 1459 |         QList<QContactId> allContacts = cm->contactIds(); | 
| 1460 |         for (int i = 0; i < allContacts.size(); i++) { | 
| 1461 |             expected.append(t: cm->contact(contactId: allContacts.at(i))); | 
| 1462 |         } | 
| 1463 |         QVERIFY(!expected.contains(temp)); | 
| 1464 |         QCOMPARE(cm->contactIds().size(), originalCount + 1); | 
| 1465 |         break; | 
| 1466 |     } | 
| 1467 | } | 
| 1468 |  | 
| 1469 | void tst_QContactAsync::contactSaveErrorHandling() | 
| 1470 | { | 
| 1471 |     QFETCH(QString, uri); | 
| 1472 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1473 |     QContactSaveRequest csr; | 
| 1474 |     QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); | 
| 1475 |  | 
| 1476 |     // initial state - not started, no manager. | 
| 1477 |     QVERIFY(!csr.isActive()); | 
| 1478 |     QVERIFY(!csr.isFinished()); | 
| 1479 |     QVERIFY(!csr.start()); | 
| 1480 |     QVERIFY(!csr.cancel()); | 
| 1481 |     QVERIFY(!csr.waitForFinished()); | 
| 1482 |  | 
| 1483 |     // Save a group of contacts, including few TypeGroup contacts which may not be supported by all backends. | 
| 1484 |     QContact testContact1, testContact2, testContact3, testContact4, testContact5, testContact6, testContact7,testContact8; | 
| 1485 |     QContactName nameDetail; | 
| 1486 |     nameDetail.setFirstName("Test Contact1" ); | 
| 1487 |     testContact1.saveDetail(detail: &nameDetail); | 
| 1488 |     nameDetail.setFirstName("Test Contact2" ); | 
| 1489 |     testContact2.saveDetail(detail: &nameDetail); | 
| 1490 |     nameDetail.setFirstName("Test Contact3" ); | 
| 1491 |     testContact3.saveDetail(detail: &nameDetail); | 
| 1492 |     nameDetail.setFirstName("Test Contact4" ); | 
| 1493 |     testContact4.saveDetail(detail: &nameDetail); | 
| 1494 |     nameDetail.setFirstName("Test Contact5" ); | 
| 1495 |     testContact5.saveDetail(detail: &nameDetail); | 
| 1496 |     nameDetail.setFirstName("Test Contact6" ); | 
| 1497 |     testContact6.saveDetail(detail: &nameDetail); | 
| 1498 |     nameDetail.setFirstName("Test Contact7" ); | 
| 1499 |     testContact7.saveDetail(detail: &nameDetail); | 
| 1500 |     nameDetail.setFirstName("Test Contact8" ); | 
| 1501 |     testContact8.saveDetail(detail: &nameDetail); | 
| 1502 |  | 
| 1503 |     // Set group type to first, middle and last contact in the list. | 
| 1504 |     QContactType typeDetail; | 
| 1505 |     typeDetail.setType(QContactType::TypeGroup); | 
| 1506 |     testContact1.saveDetail(detail: &typeDetail); | 
| 1507 |     testContact3.saveDetail(detail: &typeDetail); | 
| 1508 |     testContact6.saveDetail(detail: &typeDetail); | 
| 1509 |  | 
| 1510 |     // Set an invalid phone number | 
| 1511 |     QContactPhoneNumber invalidPhone; | 
| 1512 |     invalidPhone.setNumber("      hjkjkjhkjkhjkhj       //////" ); | 
| 1513 |     testContact7.saveDetail(detail: &invalidPhone); | 
| 1514 |  | 
| 1515 |     // This phone number can be cleaned up, so no error should be generated | 
| 1516 |     QContactPhoneNumber sanitizablePhone; | 
| 1517 |     sanitizablePhone.setNumber("       +123458++++++++*#" ); | 
| 1518 |     testContact5.saveDetail(detail: &sanitizablePhone); | 
| 1519 |  | 
| 1520 |     // Set an invalid name | 
| 1521 |     QContactName invalidName; | 
| 1522 |     invalidName.setFirstName("     " ); | 
| 1523 |     testContact8.saveDetail(detail: &invalidName); | 
| 1524 |  | 
| 1525 |     QList<QContact> saveList; | 
| 1526 |     saveList << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6 << testContact7 << testContact8; | 
| 1527 |  | 
| 1528 |     csr.setManager(cm.data()); | 
| 1529 |     QCOMPARE(csr.manager(), cm.data()); | 
| 1530 |     QVERIFY(!csr.isActive()); | 
| 1531 |     QVERIFY(!csr.isFinished()); | 
| 1532 |     QVERIFY(!csr.cancel()); | 
| 1533 |     QVERIFY(!csr.waitForFinished()); | 
| 1534 |     qRegisterMetaType<QContactSaveRequest*>(typeName: "QContactSaveRequest*" ); | 
| 1535 |     QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1536 |  | 
| 1537 |     QList<QContact> testContacts; | 
| 1538 |     testContacts << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6 << testContact7 << testContact8; | 
| 1539 |     csr.setContacts(testContacts); | 
| 1540 |  | 
| 1541 |     QCOMPARE(csr.contacts(), saveList); | 
| 1542 |     QVERIFY(!csr.cancel()); // not started | 
| 1543 |     QVERIFY(csr.start()); | 
| 1544 |  | 
| 1545 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 1546 |     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable | 
| 1547 |     QVERIFY(csr.waitForFinished()); | 
| 1548 |     QVERIFY(csr.isFinished()); | 
| 1549 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1550 |     spy.clear(); | 
| 1551 |  | 
| 1552 |     // Note, the returned value is actually set/remapped in to the errorMap by common code in qcontactmanagerengine | 
| 1553 |     if (!cm->supportedContactTypes().contains(t: QContactType::TypeGroup)) { | 
| 1554 |         QCOMPARE(csr.errorMap().value(0), QContactManager::InvalidContactTypeError); | 
| 1555 |         QCOMPARE(csr.errorMap().value(2), QContactManager::InvalidContactTypeError); | 
| 1556 |         QCOMPARE(csr.errorMap().value(5), QContactManager::InvalidContactTypeError); | 
| 1557 |         QCOMPARE(csr.error(), QContactManager::InvalidContactTypeError); | 
| 1558 |  | 
| 1559 |         // With errors in the batch, we can't know that the other contacts have no error... | 
| 1560 |     } else { | 
| 1561 |         QCOMPARE(csr.errorMap().value(0), QContactManager::NoError); | 
| 1562 |         QCOMPARE(csr.errorMap().value(2), QContactManager::NoError); | 
| 1563 |         QCOMPARE(csr.errorMap().value(5), QContactManager::NoError); | 
| 1564 |         QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1565 |  | 
| 1566 |         QCOMPARE(csr.errorMap().value(1), QContactManager::NoError); | 
| 1567 |         QCOMPARE(csr.errorMap().value(3), QContactManager::NoError); | 
| 1568 |         QCOMPARE(csr.errorMap().value(4), QContactManager::NoError); | 
| 1569 |         QCOMPARE(csr.errorMap().value(7), QContactManager::NoError); | 
| 1570 |     } | 
| 1571 | } | 
| 1572 |  | 
| 1573 | void tst_QContactAsync::contactSaveRemovedContacts() | 
| 1574 | { | 
| 1575 |     QFETCH(QString, uri); | 
| 1576 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1577 |     QContactSaveRequest csr; | 
| 1578 |  | 
| 1579 |     // Make three test contacts. | 
| 1580 |     QContactName nameDetail; | 
| 1581 |     nameDetail.setFirstName("Test1" ); | 
| 1582 |     QContact testContact1; | 
| 1583 |     testContact1.saveDetail(detail: &nameDetail); | 
| 1584 |     nameDetail.setFirstName("Test2" ); | 
| 1585 |     QContact testContact2; | 
| 1586 |     testContact2.saveDetail(detail: &nameDetail); | 
| 1587 |     nameDetail.setFirstName("Test3" ); | 
| 1588 |     QContact testContact3; | 
| 1589 |     testContact3.saveDetail(detail: &nameDetail); | 
| 1590 |     QList<QContact> testContacts; | 
| 1591 |     testContacts << testContact1 << testContact2 << testContact3; | 
| 1592 |  | 
| 1593 |     // Save all three test contacts. | 
| 1594 |     csr.setManager(cm.data()); | 
| 1595 |     qRegisterMetaType<QContactSaveRequest*>(typeName: "QContactSaveRequest*" ); | 
| 1596 |     csr.setContacts(testContacts); | 
| 1597 |     QVERIFY(csr.start()); | 
| 1598 |     QVERIFY(csr.waitForFinished()); | 
| 1599 |     QCOMPARE(csr.errorMap().value(0), QContactManager::NoError); | 
| 1600 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1601 |  | 
| 1602 |     // And then remove all of them. | 
| 1603 |     QList<QContactId> contactIds = cm->contactIds(); | 
| 1604 |     QVERIFY(cm->removeContacts(contactIds)); | 
| 1605 |  | 
| 1606 |     // Try now to save again the same three test contacts with the ids of | 
| 1607 |     // just removed contacts to fail and check proper error code. | 
| 1608 |     QVERIFY(csr.start()); | 
| 1609 |     QVERIFY(csr.waitForFinished()); | 
| 1610 |     QCOMPARE(csr.errorMap().value(0), QContactManager::DoesNotExistError); | 
| 1611 |     QCOMPARE(csr.errorMap().value(1), QContactManager::DoesNotExistError); | 
| 1612 |     QCOMPARE(csr.errorMap().value(2), QContactManager::DoesNotExistError); | 
| 1613 |     QCOMPARE(csr.error(), QContactManager::DoesNotExistError); | 
| 1614 |  | 
| 1615 |     //test case for existing and non existing contacts after remove in contact | 
| 1616 |     //save request | 
| 1617 |     nameDetail.setFirstName("Test4" ); | 
| 1618 |     QContact testContact4; | 
| 1619 |     testContact4.saveDetail(detail: &nameDetail); | 
| 1620 |     testContacts = csr.contacts(); | 
| 1621 |     testContacts << testContact4; | 
| 1622 |     csr.setContacts(testContacts); | 
| 1623 |     QVERIFY(csr.start()); | 
| 1624 |     QVERIFY(csr.waitForFinished()); | 
| 1625 |     QCOMPARE(csr.errorMap().value(0), QContactManager::DoesNotExistError); | 
| 1626 |     QCOMPARE(csr.errorMap().value(1), QContactManager::DoesNotExistError); | 
| 1627 |     QCOMPARE(csr.errorMap().value(2), QContactManager::DoesNotExistError); | 
| 1628 |     QCOMPARE(csr.errorMap().value(3), QContactManager::NoError); | 
| 1629 | } | 
| 1630 |  | 
| 1631 | void tst_QContactAsync::contactSaveRemovedContactsWithCleanIds() | 
| 1632 | { | 
| 1633 |     QFETCH(QString, uri); | 
| 1634 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1635 |     QContactSaveRequest csr; | 
| 1636 |  | 
| 1637 |     // Make three test contacts. | 
| 1638 |     QContactName nameDetail; | 
| 1639 |     nameDetail.setFirstName("Testing1" ); | 
| 1640 |     QContact testContact1; | 
| 1641 |     testContact1.saveDetail(detail: &nameDetail); | 
| 1642 |     nameDetail.setFirstName("Testing2" ); | 
| 1643 |     QContact testContact2; | 
| 1644 |     testContact2.saveDetail(detail: &nameDetail); | 
| 1645 |     nameDetail.setFirstName("Testing3" ); | 
| 1646 |     QContact testContact3; | 
| 1647 |     testContact3.saveDetail(detail: &nameDetail); | 
| 1648 |     QList<QContact> testContacts; | 
| 1649 |     testContacts << testContact1 << testContact2 << testContact3; | 
| 1650 |  | 
| 1651 |     // Save all three test contacts. | 
| 1652 |     csr.setManager(cm.data()); | 
| 1653 |     csr.setContacts(testContacts); | 
| 1654 |     QVERIFY(csr.start()); | 
| 1655 |     QVERIFY(csr.waitForFinished()); | 
| 1656 |     QCOMPARE(csr.errorMap().value(0), QContactManager::NoError); | 
| 1657 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1658 |  | 
| 1659 |     // And then remove all of them. | 
| 1660 |     QList<QContactId> contactIds = cm->contactIds(); | 
| 1661 |     QVERIFY(cm->removeContacts(contactIds)); | 
| 1662 |  | 
| 1663 |     // Use the same contacts but clean their ids before saving them. | 
| 1664 |     QList<QContact> contactsWithCleanIds = csr.contacts(); | 
| 1665 |     contactsWithCleanIds[0].setId(QContactId()); | 
| 1666 |     contactsWithCleanIds[1].setId(QContactId()); | 
| 1667 |     contactsWithCleanIds[2].setId(QContactId()); | 
| 1668 |     csr.setContacts(contactsWithCleanIds); | 
| 1669 |  | 
| 1670 |     // Save and check no errors occured. | 
| 1671 |     QVERIFY(csr.start()); | 
| 1672 |     QVERIFY(csr.waitForFinished()); | 
| 1673 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1674 | } | 
| 1675 |  | 
| 1676 | void tst_QContactAsync::contactPartialSave() | 
| 1677 | { | 
| 1678 |     QFETCH(QString, uri); | 
| 1679 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1680 |  | 
| 1681 |     QList<QContact> contacts(cm->contacts()); | 
| 1682 |     QList<QContact> originalContacts(contacts); | 
| 1683 |     QCOMPARE(contacts.count(), 3); | 
| 1684 |  | 
| 1685 |     qDebug() << Q_FUNC_INFO << "contacts:"  << contacts; | 
| 1686 |  | 
| 1687 |     QContactId aId = contacts[0].id(); | 
| 1688 |     QContactId bId = contacts[1].id(); | 
| 1689 |  | 
| 1690 |     // Test 1: saving a contact with a changed detail masked out does nothing | 
| 1691 |     QContactPhoneNumber phn(contacts[0].detail<QContactPhoneNumber>()); | 
| 1692 |     phn.setNumber("new number" ); | 
| 1693 |     contacts[0].saveDetail(detail: &phn); | 
| 1694 |  | 
| 1695 |     QContactSaveRequest csr; | 
| 1696 |     csr.setManager(cm.data()); | 
| 1697 |     csr.setContacts(contacts); | 
| 1698 |     QList<QContactDetail::DetailType> typeMasks; | 
| 1699 |     typeMasks << QContactDetail::TypeEmailAddress; | 
| 1700 |     csr.setTypeMask(typeMasks); | 
| 1701 |     qRegisterMetaType<QContactSaveRequest*>(typeName: "QContactSaveRequest*" ); | 
| 1702 |     QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1703 |     QVERIFY(csr.start()); | 
| 1704 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 1705 |     QVERIFY(csr.waitForFinished()); | 
| 1706 |     QVERIFY(csr.isFinished()); | 
| 1707 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1708 |     spy.clear(); | 
| 1709 |  | 
| 1710 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1711 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1712 |     contacts[0] = cm->contact(contactId: aId); | 
| 1713 |     QCOMPARE(contacts[0].detail<QContactPhoneNumber>().number(), | 
| 1714 |             originalContacts[0].detail<QContactPhoneNumber>().number()); | 
| 1715 |  | 
| 1716 |     // Test 2: saving a contact with a changed detail in the mask changes it | 
| 1717 |     QContactEmailAddress email; | 
| 1718 |     email.setEmailAddress("me@example.com" ); | 
| 1719 |     contacts[1].saveDetail(detail: &email); | 
| 1720 |     csr.setContacts(contacts); | 
| 1721 |     csr.setTypeMask(typeMasks); | 
| 1722 |     QVERIFY(csr.start()); | 
| 1723 |     QVERIFY(csr.waitForFinished()); | 
| 1724 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1725 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1726 |     contacts[1] = cm->contact(contactId: bId); | 
| 1727 |     QCOMPARE(contacts[1].detail<QContactEmailAddress>().emailAddress(), QString("me@example.com" )); | 
| 1728 |  | 
| 1729 |     // 3) Remove an email address and a phone number | 
| 1730 |     QCOMPARE(contacts[1].details<QContactPhoneNumber>().count(), 1); | 
| 1731 |     QCOMPARE(contacts[1].details<QContactEmailAddress>().count(), 1); | 
| 1732 |     // Next line added because otherwise the removeDetail fails because the email details key | 
| 1733 |     // changes in: contacts[1] = cm->contact(bId); and it does not match email variables key. | 
| 1734 |     email = contacts[1].detail<QContactEmailAddress>(); | 
| 1735 |     QVERIFY(contacts[1].removeDetail(&email)); | 
| 1736 |     phn = contacts[1].detail<QContactPhoneNumber>(); | 
| 1737 |     QVERIFY(contacts[1].removeDetail(&phn)); | 
| 1738 |     QVERIFY(contacts[1].details<QContactEmailAddress>().count() == 0); | 
| 1739 |     QVERIFY(contacts[1].details<QContactPhoneNumber>().count() == 0); | 
| 1740 |     csr.setContacts(contacts); | 
| 1741 |     csr.setTypeMask(typeMasks); | 
| 1742 |     QVERIFY(csr.start()); | 
| 1743 |     QVERIFY(csr.waitForFinished()); | 
| 1744 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1745 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1746 |     contacts[1] = cm->contact(contactId: bId); | 
| 1747 |     QCOMPARE(contacts[1].details<QContactEmailAddress>().count(), 0); | 
| 1748 |     QCOMPARE(contacts[1].details<QContactPhoneNumber>().count(), 1); | 
| 1749 |  | 
| 1750 |     // 4 - New contact, no details in the mask | 
| 1751 |     QContact newContact; | 
| 1752 |     newContact.saveDetail(detail: &email); | 
| 1753 |     newContact.saveDetail(detail: &phn); | 
| 1754 |     contacts.append(t: newContact); | 
| 1755 |     csr.setContacts(contacts); | 
| 1756 |     typeMasks.clear(); | 
| 1757 |     typeMasks << QContactDetail::TypeOnlineAccount; | 
| 1758 |     csr.setTypeMask(typeMasks); | 
| 1759 |     QVERIFY(csr.start()); | 
| 1760 |     QVERIFY(csr.waitForFinished()); | 
| 1761 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1762 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1763 |     contacts = csr.contacts(); | 
| 1764 |     QCOMPARE(contacts.size()-1, 3);  // Just check that we are dealing with the contact at index 3 | 
| 1765 |     QContactId dId = contacts[3].id(); | 
| 1766 |     contacts[3] = cm->contact(contactId: dId); | 
| 1767 |     QVERIFY(contacts[3].details<QContactEmailAddress>().count() == 0); // not saved | 
| 1768 |     QVERIFY(contacts[3].details<QContactPhoneNumber>().count() == 0); // not saved | 
| 1769 |  | 
| 1770 |     // 5 - New contact, some details in the mask | 
| 1771 |     QVERIFY(newContact.id().isNull()); | 
| 1772 |     QVERIFY(newContact.details<QContactEmailAddress>().count() == 1); | 
| 1773 |     QVERIFY(newContact.details<QContactPhoneNumber>().count() == 1); | 
| 1774 |     contacts.append(t: newContact); | 
| 1775 |     csr.setContacts(contacts); | 
| 1776 |     typeMasks.clear(); | 
| 1777 |     typeMasks << QContactDetail::TypePhoneNumber; | 
| 1778 |     csr.setTypeMask(typeMasks); | 
| 1779 |     QVERIFY(csr.start()); | 
| 1780 |     QVERIFY(csr.waitForFinished()); | 
| 1781 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1782 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1783 |     contacts = csr.contacts(); | 
| 1784 |     QCOMPARE(contacts.size()-1, 4);  // Just check that we are dealing with the contact at index 4 | 
| 1785 |     QContactId eId = contacts[4].id(); | 
| 1786 |     contacts[4] = cm->contact(contactId: eId); | 
| 1787 |     QCOMPARE(contacts[4].details<QContactEmailAddress>().count(), 0); // not saved | 
| 1788 |     QCOMPARE(contacts[4].details<QContactPhoneNumber>().count(), 1); // saved | 
| 1789 |  | 
| 1790 |     // 6) Have a bad manager uri in the middle | 
| 1791 |     QContactId badId = makeId(managerName: "BadManager" , id: 0); | 
| 1792 |     contacts[3].setId(badId); | 
| 1793 |     csr.setContacts(contacts); | 
| 1794 |     QVERIFY(csr.start()); | 
| 1795 |     QVERIFY(csr.waitForFinished()); | 
| 1796 |     QVERIFY(csr.error() == QContactManager::DoesNotExistError); // error in the middle in this partial save | 
| 1797 |     QMap<int, QContactManager::Error> errorMap(csr.errorMap()); | 
| 1798 |     QCOMPARE(errorMap.count(), 1);//one error in error map, related to the fetch phase of this partial save | 
| 1799 |     QCOMPARE(errorMap[3], QContactManager::DoesNotExistError); | 
| 1800 |  | 
| 1801 |     // 7) Have a non existing contact in the middle | 
| 1802 |     badId = makeId(managerName: cm->managerName(), id: 0); | 
| 1803 |     contacts[3].setId(badId); | 
| 1804 |     csr.setContacts(contacts); | 
| 1805 |     QVERIFY(csr.start()); | 
| 1806 |     QVERIFY(csr.waitForFinished()); | 
| 1807 |     QVERIFY(csr.error() == QContactManager::DoesNotExistError); // error in the middle in this partial save | 
| 1808 |     errorMap = csr.errorMap(); | 
| 1809 |     QCOMPARE(errorMap.count(), 1);//one error in error map, related to the fetch phase of this partial save | 
| 1810 |     QCOMPARE(errorMap[3], QContactManager::DoesNotExistError); | 
| 1811 |  | 
| 1812 |     // 8) A list entirely of new contacts, with no details in the mask | 
| 1813 |     QList<QContact> contacts2; | 
| 1814 |     QVERIFY(newContact.id().isNull()); | 
| 1815 |     QVERIFY(newContact.details<QContactEmailAddress>().count() == 1); | 
| 1816 |     QVERIFY(newContact.details<QContactPhoneNumber>().count() == 1); | 
| 1817 |     contacts2.append(t: newContact); | 
| 1818 |     csr.setContacts(contacts2); | 
| 1819 |     typeMasks.clear(); | 
| 1820 |     typeMasks << QContactDetail::TypeOrganization; | 
| 1821 |     csr.setTypeMask(typeMasks); | 
| 1822 |     QVERIFY(csr.start()); | 
| 1823 |     QVERIFY(csr.waitForFinished()); | 
| 1824 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1825 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1826 |     contacts2 = csr.contacts(); | 
| 1827 |     QCOMPARE(contacts2.size(), 1); | 
| 1828 |     contacts2[0] = cm->contact(contactId: contacts2[0].id()); | 
| 1829 |     QCOMPARE(contacts2[0].details<QContactEmailAddress>().count(), 0); // not saved | 
| 1830 |     QCOMPARE(contacts2[0].details<QContactPhoneNumber>().count(), 0); // saved | 
| 1831 |  | 
| 1832 |     // 9) A list entirely of new contacts, with some details in the mask | 
| 1833 |     contacts2.clear(); | 
| 1834 |     QVERIFY(newContact.id().isNull()); | 
| 1835 |     QVERIFY(newContact.details<QContactEmailAddress>().count() == 1); | 
| 1836 |     QVERIFY(newContact.details<QContactPhoneNumber>().count() == 1); | 
| 1837 |     contacts2.append(t: newContact); | 
| 1838 |     csr.setContacts(contacts2); | 
| 1839 |     typeMasks.clear(); | 
| 1840 |     typeMasks << QContactDetail::TypePhoneNumber; | 
| 1841 |     csr.setTypeMask(typeMasks); | 
| 1842 |     QVERIFY(csr.start()); | 
| 1843 |     QVERIFY(csr.waitForFinished()); | 
| 1844 |     QCOMPARE(csr.error(), QContactManager::NoError); | 
| 1845 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 1846 |     contacts2 = csr.contacts(); | 
| 1847 |     QCOMPARE(contacts2.size(), 1); | 
| 1848 |     contacts2[0] = cm->contact(contactId: contacts2[0].id()); | 
| 1849 |     QCOMPARE(contacts2[0].details<QContactEmailAddress>().count(), 0); // not saved | 
| 1850 |     QCOMPARE(contacts2[0].details<QContactPhoneNumber>().count(), 1); // saved | 
| 1851 |  | 
| 1852 |     // 10) A list entirely of new contacts | 
| 1853 |     contacts2.clear(); | 
| 1854 |     QVERIFY(newContact.id().isNull()); | 
| 1855 |     QVERIFY(newContact.details<QContactEmailAddress>().count() == 1); | 
| 1856 |     QVERIFY(newContact.details<QContactPhoneNumber>().count() == 1); | 
| 1857 |     contacts2.append(t: newContact); | 
| 1858 |     contacts2.append(t: newContact); | 
| 1859 |     contacts2[0].setId(badId); | 
| 1860 |     csr.setContacts(contacts2); | 
| 1861 |     QVERIFY(csr.start()); | 
| 1862 |     QVERIFY(csr.waitForFinished()); | 
| 1863 |     QVERIFY(csr.error() == QContactManager::DoesNotExistError); // error in the middle in this partial save | 
| 1864 |     errorMap = csr.errorMap(); | 
| 1865 |     QCOMPARE(errorMap.count(), 1);//one error in error map, related to the fetch phase of this partial save | 
| 1866 |     QCOMPARE(errorMap[0], QContactManager::DoesNotExistError); | 
| 1867 | } | 
| 1868 |  | 
| 1869 | void tst_QContactAsync::contactPartialSaveAsync() | 
| 1870 | { | 
| 1871 |     QFETCH(QString, uri); | 
| 1872 |     QContactManager* cm = QContactManager::fromUri(uri); | 
| 1873 |  | 
| 1874 |     QList<QContact> contacts; | 
| 1875 |  | 
| 1876 |     int numContacts = 10; | 
| 1877 |     // add contacts | 
| 1878 |     for (int i = 0; i < numContacts; i++) { | 
| 1879 |         QContact c; | 
| 1880 |         QContactName name; | 
| 1881 |         name.setFirstName("John" ); | 
| 1882 |         name.setMiddleName(QString::number(i)); | 
| 1883 |         name.setLastName("Doe" ); | 
| 1884 |         QContactPhoneNumber phone; | 
| 1885 |         QString number = "555-100" +QString::number(i); | 
| 1886 |         phone.setNumber(number); | 
| 1887 |         c.saveDetail(detail: &name); | 
| 1888 |         c.saveDetail(detail: &phone); | 
| 1889 |         contacts.append(t: c); | 
| 1890 |     } | 
| 1891 |  | 
| 1892 |     QContactSaveRequest *saveRequest = new QContactSaveRequest(); | 
| 1893 |     saveRequest->setManager(cm); | 
| 1894 |     saveRequest->setContacts(contacts); | 
| 1895 |     saveRequest->start(); | 
| 1896 |     saveRequest->waitForFinished(msecs: 20000); | 
| 1897 |     QVERIFY(saveRequest->isFinished()); | 
| 1898 |     QCOMPARE(saveRequest->contacts().count(), numContacts); | 
| 1899 |     delete saveRequest; | 
| 1900 |     qRegisterMetaType<QContactAbstractRequest::State>(typeName: "QContactAbstractRequest::State" ); | 
| 1901 |  | 
| 1902 |     saveRequest = new QContactSaveRequest(); | 
| 1903 |     saveRequest->setManager(cm); | 
| 1904 |     saveRequest->setContacts(contacts); | 
| 1905 |     QList<QContactDetail::DetailType> typeMasks; | 
| 1906 |     typeMasks << QContactDetail::TypeTag; | 
| 1907 |     saveRequest->setTypeMask(typeMasks); | 
| 1908 |     saveRequest->start(); | 
| 1909 |     QTest::qWait(ms: 1000); | 
| 1910 |     QVERIFY(saveRequest->waitForFinished()); | 
| 1911 |     QVERIFY(saveRequest->isFinished()); | 
| 1912 | } | 
| 1913 |  | 
| 1914 | void tst_QContactAsync::relationshipFetch() | 
| 1915 | { | 
| 1916 |     QFETCH(QString, uri); | 
| 1917 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 1918 |  | 
| 1919 |     QContactRelationshipFetchRequest rfr; | 
| 1920 |     QVERIFY(rfr.type() == QContactAbstractRequest::RelationshipFetchRequest); | 
| 1921 |  | 
| 1922 |     // initial state - not started, no manager. | 
| 1923 |     QVERIFY(!rfr.isActive()); | 
| 1924 |     QVERIFY(!rfr.isFinished()); | 
| 1925 |     QVERIFY(!rfr.start()); | 
| 1926 |     QVERIFY(!rfr.cancel()); | 
| 1927 |     QVERIFY(!rfr.waitForFinished()); | 
| 1928 |  | 
| 1929 |     // "all relationships" retrieval | 
| 1930 |     rfr.setManager(cm.data()); | 
| 1931 |     QCOMPARE(rfr.manager(), cm.data()); | 
| 1932 |     QVERIFY(!rfr.isActive()); | 
| 1933 |     QVERIFY(!rfr.isFinished()); | 
| 1934 |     QVERIFY(!rfr.cancel()); | 
| 1935 |     QVERIFY(!rfr.waitForFinished()); | 
| 1936 |     qRegisterMetaType<QContactRelationshipFetchRequest*>(typeName: "QContactRelationshipFetchRequest*" ); | 
| 1937 |     QThreadSignalSpy spy(&rfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 1938 |     QVERIFY(!rfr.cancel()); // not started | 
| 1939 |     QVERIFY(rfr.start()); | 
| 1940 |  | 
| 1941 |     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); | 
| 1942 |     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable | 
| 1943 |     QVERIFY(rfr.waitForFinished()); | 
| 1944 |  | 
| 1945 |     QVERIFY(rfr.isFinished()); | 
| 1946 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1947 |     spy.clear(); | 
| 1948 |  | 
| 1949 |     QList<QContactRelationship> rels = cm->relationships(); | 
| 1950 |     QList<QContactRelationship> result = rfr.relationships(); | 
| 1951 |     QCOMPARE(rels, result); | 
| 1952 |  | 
| 1953 |     // specific relationship type retrieval | 
| 1954 |     rfr.setRelationshipType(QContactRelationship::HasManager()); | 
| 1955 |     QVERIFY(!rfr.cancel()); // not started | 
| 1956 |     QVERIFY(rfr.start()); | 
| 1957 |  | 
| 1958 |     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); | 
| 1959 |     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable | 
| 1960 |     QVERIFY(rfr.waitForFinished()); | 
| 1961 |     QVERIFY(rfr.isFinished()); | 
| 1962 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1963 |     spy.clear(); | 
| 1964 |  | 
| 1965 |     rels = cm->relationships(relationshipType: QContactRelationship::HasManager()); | 
| 1966 |     result = rfr.relationships(); | 
| 1967 |     QCOMPARE(rels, result); | 
| 1968 |  | 
| 1969 |     // specific source contact retrieval | 
| 1970 |     rfr.setRelationshipType(QString()); | 
| 1971 |     QList<QContactId> contacts = cm->contactIds(); | 
| 1972 |     QContact aContact; | 
| 1973 |     foreach (const QContactId& currId, contacts) { | 
| 1974 |         QContact curr = cm->contact(contactId: currId); | 
| 1975 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Aaron" )) { | 
| 1976 |             aContact = curr; | 
| 1977 |             break; | 
| 1978 |         } | 
| 1979 |     } | 
| 1980 |     rfr.setFirst(aContact.id()); | 
| 1981 |     QVERIFY(!rfr.cancel()); // not started | 
| 1982 |     QVERIFY(rfr.start()); | 
| 1983 |  | 
| 1984 |     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); | 
| 1985 |     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable | 
| 1986 |     QVERIFY(rfr.waitForFinished()); | 
| 1987 |     QVERIFY(rfr.isFinished()); | 
| 1988 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 1989 |     spy.clear(); | 
| 1990 |  | 
| 1991 |     rels = cm->relationships(participantId: aContact.id(), role: QContactRelationship::First); | 
| 1992 |     result = rfr.relationships(); | 
| 1993 |     QCOMPARE(rels, result); | 
| 1994 |  | 
| 1995 |     // specific participant retrieval #1 - destination participant | 
| 1996 |     rfr.setFirst(QContactId()); | 
| 1997 |     contacts = cm->contactIds(); | 
| 1998 |     QContact bContact; | 
| 1999 |     foreach (const QContactId& currId, contacts) { | 
| 2000 |         QContact curr = cm->contact(contactId: currId); | 
| 2001 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Bob" )) { | 
| 2002 |             bContact = curr; | 
| 2003 |             break; | 
| 2004 |         } | 
| 2005 |     } | 
| 2006 |     rfr.setSecond(bContact.id()); | 
| 2007 |  | 
| 2008 |     QVERIFY(!rfr.cancel()); // not started | 
| 2009 |     QVERIFY(rfr.start()); | 
| 2010 |  | 
| 2011 |     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); | 
| 2012 |     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable | 
| 2013 |     QVERIFY(rfr.waitForFinished()); | 
| 2014 |     QVERIFY(rfr.isFinished()); | 
| 2015 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2016 |     spy.clear(); | 
| 2017 |  | 
| 2018 |     // retrieve rels where second = id of B, and ensure that we get the same results | 
| 2019 |     rels = cm->relationships(participantId: bContact.id(), role: QContactRelationship::Second); | 
| 2020 |     result = rfr.relationships(); | 
| 2021 |     QCOMPARE(rels, result); | 
| 2022 |  | 
| 2023 |     // specific participant retrieval #2 - source participant | 
| 2024 |     rfr.setFirst(QContactId()); | 
| 2025 |     contacts = cm->contactIds(); | 
| 2026 |     QContact cContact; | 
| 2027 |     foreach (const QContactId& currId, contacts) { | 
| 2028 |         QContact curr = cm->contact(contactId: currId); | 
| 2029 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Borris" )) { | 
| 2030 |             cContact = curr; | 
| 2031 |             break; | 
| 2032 |         } | 
| 2033 |     } | 
| 2034 |     rfr.setSecond(cContact.id()); | 
| 2035 |  | 
| 2036 |     QVERIFY(!rfr.cancel()); // not started | 
| 2037 |     QVERIFY(rfr.start()); | 
| 2038 |  | 
| 2039 |     QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); | 
| 2040 |     //QVERIFY(rfr.isFinished() || !rfr.start());  // already started. // thread scheduling means this is untestable | 
| 2041 |     QVERIFY(rfr.waitForFinished()); | 
| 2042 |     QVERIFY(rfr.isFinished()); | 
| 2043 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2044 |     spy.clear(); | 
| 2045 |  | 
| 2046 |     // retrieve rels where first = id of C and compare the results | 
| 2047 |     rfr.setFirst(cContact.id()); | 
| 2048 |     rfr.setSecond(QContactId()); | 
| 2049 |     QVERIFY(rfr.start()); | 
| 2050 |     QVERIFY(rfr.waitForFinished()); | 
| 2051 |     result = rfr.relationships(); | 
| 2052 |     rels = cm->relationships(participantId: cContact.id(), role: QContactRelationship::First); | 
| 2053 |     QCOMPARE(rels, result); | 
| 2054 |  | 
| 2055 |     // cancelling | 
| 2056 |     rfr.setRelationshipType(QString()); | 
| 2057 |  | 
| 2058 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 2059 |     while (true) { | 
| 2060 |         QVERIFY(!rfr.cancel()); // not started | 
| 2061 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 2062 |         QVERIFY(rfr.start()); | 
| 2063 |         if (!rfr.cancel()) { | 
| 2064 |             // due to thread scheduling, async cancel might be attempted | 
| 2065 |             // after the request has already finished.. so loop and try again. | 
| 2066 |             rfr.waitForFinished(); | 
| 2067 |             rfr.setRelationshipType(QString()); | 
| 2068 |             bailoutCount -= 1; | 
| 2069 |             if (!bailoutCount) { | 
| 2070 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2071 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2072 |                 break; | 
| 2073 |             } | 
| 2074 |             spy.clear(); | 
| 2075 |             continue; | 
| 2076 |         } | 
| 2077 |  | 
| 2078 |         // if we get here, then we are cancelling the request. | 
| 2079 |         QVERIFY(rfr.waitForFinished()); | 
| 2080 |         QVERIFY(rfr.isCanceled()); | 
| 2081 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2082 |         spy.clear(); | 
| 2083 |         break; | 
| 2084 |     } | 
| 2085 |  | 
| 2086 |     // restart, and wait for progress after cancel. | 
| 2087 |     while (true) { | 
| 2088 |         QVERIFY(!rfr.cancel()); // not started | 
| 2089 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 2090 |         QVERIFY(rfr.start()); | 
| 2091 |         if (!rfr.cancel()) { | 
| 2092 |             // due to thread scheduling, async cancel might be attempted | 
| 2093 |             // after the request has already finished.. so loop and try again. | 
| 2094 |             rfr.waitForFinished(); | 
| 2095 |             rfr.setRelationshipType(QString()); | 
| 2096 |             bailoutCount -= 1; | 
| 2097 |             if (!bailoutCount) { | 
| 2098 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2099 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2100 |                 break; | 
| 2101 |             } | 
| 2102 |             spy.clear(); | 
| 2103 |             continue; | 
| 2104 |         } | 
| 2105 |         rfr.waitForFinished(); | 
| 2106 |         QVERIFY(rfr.isCanceled()); | 
| 2107 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2108 |         spy.clear(); | 
| 2109 |         break; | 
| 2110 |     } | 
| 2111 | } | 
| 2112 |  | 
| 2113 | void tst_QContactAsync::relationshipRemove() | 
| 2114 | { | 
| 2115 |     QFETCH(QString, uri); | 
| 2116 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 2117 |  | 
| 2118 |     QContactRelationshipRemoveRequest rrr; | 
| 2119 |     QVERIFY(rrr.type() == QContactAbstractRequest::RelationshipRemoveRequest); | 
| 2120 |  | 
| 2121 |     // initial state - not started, no manager. | 
| 2122 |     QVERIFY(!rrr.isActive()); | 
| 2123 |     QVERIFY(!rrr.isFinished()); | 
| 2124 |     QVERIFY(!rrr.start()); | 
| 2125 |     QVERIFY(!rrr.cancel()); | 
| 2126 |     QVERIFY(!rrr.waitForFinished()); | 
| 2127 |  | 
| 2128 |     QList<QContactId> contacts = cm->contactIds(); | 
| 2129 |     QContact aContact, bContact, cContact; | 
| 2130 |     foreach (const QContactId& currId, contacts) { | 
| 2131 |         QContact curr = cm->contact(contactId: currId); | 
| 2132 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Aaron" )) { | 
| 2133 |             aContact = curr; | 
| 2134 |             continue; | 
| 2135 |         } | 
| 2136 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Bob" )) { | 
| 2137 |             bContact = curr; | 
| 2138 |             continue; | 
| 2139 |         } | 
| 2140 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Borris" )) { | 
| 2141 |             cContact = curr; | 
| 2142 |             continue; | 
| 2143 |         } | 
| 2144 |     } | 
| 2145 |  | 
| 2146 |     // specific source, destination and type removal | 
| 2147 |     QList<QContactRelationship> relationships; | 
| 2148 |     QContactRelationship r; | 
| 2149 |     r.setFirst(aContact.id()); | 
| 2150 |     r.setSecond(cContact.id()); | 
| 2151 |     r.setRelationshipType(QContactRelationship::HasAssistant()); | 
| 2152 |     relationships.push_back(t: r); | 
| 2153 |  | 
| 2154 |     rrr.setRelationships(relationships); | 
| 2155 |     rrr.setManager(cm.data()); | 
| 2156 |     qRegisterMetaType<QContactRelationshipRemoveRequest*>(typeName: "QContactRelationshipRemoveRequest*" ); | 
| 2157 |     QThreadSignalSpy spy(&rrr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 2158 |     QCOMPARE(rrr.manager(), cm.data()); | 
| 2159 |     QVERIFY(!rrr.isActive()); | 
| 2160 |     QVERIFY(!rrr.isFinished()); | 
| 2161 |     QVERIFY(!rrr.cancel()); | 
| 2162 |     QVERIFY(!rrr.waitForFinished()); | 
| 2163 |  | 
| 2164 |     QVERIFY(!rrr.cancel()); // not started | 
| 2165 |     QVERIFY(rrr.start()); | 
| 2166 |  | 
| 2167 |     QVERIFY((rrr.isActive() && rrr.state() == QContactAbstractRequest::ActiveState) || rrr.isFinished()); | 
| 2168 |     //QVERIFY(rrr.isFinished() || !rrr.start());  // already started. // thread scheduling means this is untestable | 
| 2169 |     QVERIFY(rrr.waitForFinished()); | 
| 2170 |     QVERIFY(rrr.isFinished()); | 
| 2171 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2172 |     spy.clear(); | 
| 2173 |     QCOMPARE(cm->relationships(QContactRelationship::HasAssistant(), cContact.id(), QContactRelationship::Second).size(), 1); | 
| 2174 |  | 
| 2175 |     // remove (asynchronously) a nonexistent relationship - should fail. | 
| 2176 |     r.setFirst(cContact.id()); | 
| 2177 |     r.setSecond(aContact.id()); | 
| 2178 |     r.setRelationshipType(QContactRelationship::HasManager()); | 
| 2179 |     relationships.clear(); | 
| 2180 |     relationships.push_back(t: r); | 
| 2181 |     rrr.setRelationship(r); | 
| 2182 |     QVERIFY(rrr.relationships() == relationships); | 
| 2183 |     rrr.setManager(cm.data()); | 
| 2184 |     QVERIFY(!rrr.cancel()); // not started | 
| 2185 |     QVERIFY(rrr.start()); | 
| 2186 |  | 
| 2187 |     QVERIFY((rrr.isActive() && rrr.state() == QContactAbstractRequest::ActiveState) || rrr.isFinished()); | 
| 2188 |     //QVERIFY(rrr.isFinished() || !rrr.start());  // already started. // thread scheduling means this is untestable | 
| 2189 |     QVERIFY(rrr.waitForFinished()); | 
| 2190 |     QVERIFY(rrr.isFinished()); | 
| 2191 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2192 |     spy.clear(); | 
| 2193 |  | 
| 2194 |     QCOMPARE(cm->relationships(QContactRelationship::HasManager(), cContact.id(), QContactRelationship::First).size(), 0); | 
| 2195 | //    QCOMPARE(rrr.error(), QContactManager::DoesNotExistError); | 
| 2196 |  | 
| 2197 |     // cancelling | 
| 2198 |     r.setFirst(cContact.id()); | 
| 2199 |     r.setSecond(QContactId()); | 
| 2200 |     relationships.clear(); | 
| 2201 |     relationships.push_back(t: r); | 
| 2202 |  | 
| 2203 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 2204 |     while (true) { | 
| 2205 |         QVERIFY(!rrr.cancel()); // not started | 
| 2206 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 2207 |         QVERIFY(rrr.start()); | 
| 2208 |         if (!rrr.cancel()) { | 
| 2209 |             // due to thread scheduling, async cancel might be attempted | 
| 2210 |             // after the request has already finished.. so loop and try again. | 
| 2211 |             rrr.waitForFinished(); | 
| 2212 |             rrr.setRelationships(relationships); | 
| 2213 |             bailoutCount -= 1; | 
| 2214 |             if (!bailoutCount) { | 
| 2215 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2216 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2217 |                 break; | 
| 2218 |             } | 
| 2219 |             spy.clear(); | 
| 2220 |             continue; | 
| 2221 |         } | 
| 2222 |  | 
| 2223 |         // if we get here, then we are cancelling the request. | 
| 2224 |         QVERIFY(rrr.waitForFinished()); | 
| 2225 |         QVERIFY(rrr.isCanceled()); | 
| 2226 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2227 |         spy.clear(); | 
| 2228 |  | 
| 2229 |         QVERIFY(cm->relationships(cContact.id()).size() != 0); // didn't remove them. | 
| 2230 |         break; | 
| 2231 |     } | 
| 2232 |  | 
| 2233 |     // restart, and wait for progress after cancel. | 
| 2234 |     while (true) { | 
| 2235 |         QVERIFY(!rrr.cancel()); // not started | 
| 2236 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 2237 |         QVERIFY(rrr.start()); | 
| 2238 |         if (!rrr.cancel()) { | 
| 2239 |             // due to thread scheduling, async cancel might be attempted | 
| 2240 |             // after the request has already finished.. so loop and try again. | 
| 2241 |             rrr.waitForFinished(); | 
| 2242 |             rrr.setRelationships(relationships); | 
| 2243 |             bailoutCount -= 1; | 
| 2244 |             if (!bailoutCount) { | 
| 2245 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2246 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2247 |                 break; | 
| 2248 |             } | 
| 2249 |             spy.clear(); | 
| 2250 |             continue; | 
| 2251 |         } | 
| 2252 |         rrr.waitForFinished(); | 
| 2253 |         QVERIFY(rrr.isCanceled()); | 
| 2254 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2255 |         spy.clear(); | 
| 2256 |  | 
| 2257 |         QVERIFY(cm->relationships(cContact.id()).size() != 0); // didn't remove them. | 
| 2258 |         break; | 
| 2259 |     } | 
| 2260 | } | 
| 2261 |  | 
| 2262 | void tst_QContactAsync::relationshipSave() | 
| 2263 | { | 
| 2264 |     QFETCH(QString, uri); | 
| 2265 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 2266 |  | 
| 2267 |     QContactRelationshipSaveRequest rsr; | 
| 2268 |     QVERIFY(rsr.type() == QContactAbstractRequest::RelationshipSaveRequest); | 
| 2269 |  | 
| 2270 |     // initial state - not started, no manager. | 
| 2271 |     QVERIFY(!rsr.isActive()); | 
| 2272 |     QVERIFY(!rsr.isFinished()); | 
| 2273 |     QVERIFY(!rsr.start()); | 
| 2274 |     QVERIFY(!rsr.cancel()); | 
| 2275 |     QVERIFY(!rsr.waitForFinished()); | 
| 2276 |  | 
| 2277 |     QList<QContactId> contacts = cm->contactIds(); | 
| 2278 |     QContact cContact, aContact, bContact; | 
| 2279 |     foreach (const QContactId& currId, contacts) { | 
| 2280 |         QContact curr = cm->contact(contactId: currId); | 
| 2281 |         if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Borris" )) { | 
| 2282 |             cContact = curr; | 
| 2283 |         } else if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Bob" )) { | 
| 2284 |             bContact = curr; | 
| 2285 |         } else if (curr.detail(type: QContactName::Type).value(field: QContactName::FieldFirstName) == QString("Aaron" )) { | 
| 2286 |             aContact = curr; | 
| 2287 |         } | 
| 2288 |     } | 
| 2289 |  | 
| 2290 |     // save a new relationship | 
| 2291 |     int originalCount = cm->relationships(participantId: aContact.id()).size(); | 
| 2292 |     QContactRelationship testRel; | 
| 2293 |     testRel.setFirst(aContact.id()); | 
| 2294 |     testRel.setRelationshipType(QContactRelationship::HasSpouse()); | 
| 2295 |     testRel.setSecond(bContact.id()); | 
| 2296 |     QList<QContactRelationship> saveList; | 
| 2297 |     saveList << testRel; | 
| 2298 |     rsr.setManager(cm.data()); | 
| 2299 |     QCOMPARE(rsr.manager(), cm.data()); | 
| 2300 |     QVERIFY(!rsr.isActive()); | 
| 2301 |     QVERIFY(!rsr.isFinished()); | 
| 2302 |     QVERIFY(!rsr.cancel()); | 
| 2303 |     QVERIFY(!rsr.waitForFinished()); | 
| 2304 |     qRegisterMetaType<QContactRelationshipSaveRequest*>(typeName: "QContactRelationshipSaveRequest*" ); | 
| 2305 |     QThreadSignalSpy spy(&rsr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 2306 |     rsr.setRelationship(testRel); | 
| 2307 |     QCOMPARE(rsr.relationships(), saveList); | 
| 2308 |     QVERIFY(!rsr.cancel()); // not started | 
| 2309 |     QVERIFY(rsr.start()); | 
| 2310 |  | 
| 2311 |     QVERIFY((rsr.isActive() && rsr.state() == QContactAbstractRequest::ActiveState) || rsr.isFinished()); | 
| 2312 |     //QVERIFY(rsr.isFinished() || !rsr.start());  // already started. // thread scheduling means this is untestable | 
| 2313 |     QVERIFY(rsr.waitForFinished()); | 
| 2314 |     QVERIFY(rsr.isFinished()); | 
| 2315 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2316 |     spy.clear(); | 
| 2317 |  | 
| 2318 |     QList<QContactRelationship> expected = cm->relationships(relationshipType: QContactRelationship::HasSpouse(), participantId: aContact.id(), role: QContactRelationship::First); | 
| 2319 |     QList<QContactRelationship> result = rsr.relationships(); | 
| 2320 |     QCOMPARE(expected, result); | 
| 2321 |     QVERIFY(result.contains(testRel)); | 
| 2322 |     QCOMPARE(cm->relationships(aContact.id()).size(), originalCount + 1); // should be one extra | 
| 2323 |  | 
| 2324 |     // save a new relationship | 
| 2325 |     testRel.setSecond(cContact.id()); | 
| 2326 |     saveList.clear(); | 
| 2327 |     saveList << testRel; | 
| 2328 |     rsr.setRelationships(saveList); | 
| 2329 |     QCOMPARE(rsr.relationships(), saveList); | 
| 2330 |     QVERIFY(!rsr.cancel()); // not started | 
| 2331 |     QVERIFY(rsr.start()); | 
| 2332 |  | 
| 2333 |     QVERIFY((rsr.isActive() && rsr.state() == QContactAbstractRequest::ActiveState) || rsr.isFinished()); | 
| 2334 |     //QVERIFY(rsr.isFinished() || !rsr.start());  // already started. // thread scheduling means this is untestable | 
| 2335 |     QVERIFY(rsr.waitForFinished()); | 
| 2336 |     QVERIFY(rsr.isFinished()); | 
| 2337 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2338 |     spy.clear(); | 
| 2339 |  | 
| 2340 |     expected.clear(); | 
| 2341 |     expected = cm->relationships(relationshipType: QContactRelationship::HasSpouse(), participantId: aContact.id(), role: QContactRelationship::First); | 
| 2342 |     result = rsr.relationships(); | 
| 2343 |     QCOMPARE(result, QList<QContactRelationship>() << testRel); | 
| 2344 |     QVERIFY(expected.contains(testRel)); | 
| 2345 |     QCOMPARE(cm->relationships(aContact.id()).size(), originalCount + 2); // should now be two extra | 
| 2346 |  | 
| 2347 |     // cancelling | 
| 2348 |     testRel.setSecond(aContact.id()); // shouldn't get saved (circular anyway) | 
| 2349 |     saveList.clear(); | 
| 2350 |     saveList << testRel; | 
| 2351 |     rsr.setRelationships(saveList); | 
| 2352 |  | 
| 2353 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 2354 |     while (true) { | 
| 2355 |         QVERIFY(!rsr.cancel()); // not started | 
| 2356 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 2357 |         QVERIFY(rsr.start()); | 
| 2358 |         if (!rsr.cancel()) { | 
| 2359 |             // due to thread scheduling, async cancel might be attempted | 
| 2360 |             // after the request has already finished.. so loop and try again. | 
| 2361 |             rsr.waitForFinished(); | 
| 2362 |             saveList.clear(); | 
| 2363 |             saveList << testRel; | 
| 2364 |             rsr.setRelationships(saveList); | 
| 2365 |             cm->removeRelationship(relationship: testRel); // probably shouldn't have been saved anyway (circular) | 
| 2366 |             bailoutCount -= 1; | 
| 2367 |             if (!bailoutCount) { | 
| 2368 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2369 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2370 |                 break; | 
| 2371 |             } | 
| 2372 |             spy.clear(); | 
| 2373 |             continue; | 
| 2374 |         } | 
| 2375 |  | 
| 2376 |         // if we get here, then we are cancelling the request. | 
| 2377 |         QVERIFY(rsr.waitForFinished()); | 
| 2378 |         QVERIFY(rsr.isCanceled()); | 
| 2379 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2380 |         spy.clear(); | 
| 2381 |  | 
| 2382 |         // verify that the changes were not saved | 
| 2383 |         QList<QContactRelationship> aRels = cm->relationships(participantId: aContact.id(), role: QContactRelationship::First); | 
| 2384 |         QVERIFY(!aRels.contains(testRel)); | 
| 2385 |         QCOMPARE(cm->relationships(aContact.id()).size(), originalCount + 2); // should still only be two extra | 
| 2386 |  | 
| 2387 |         break; | 
| 2388 |     } | 
| 2389 |  | 
| 2390 |     // restart, and wait for progress after cancel. | 
| 2391 |     while (true) { | 
| 2392 |         QVERIFY(!rsr.cancel()); // not started | 
| 2393 |         FILL_QUEUE_WITH_FETCH_REQUESTS(); | 
| 2394 |         QVERIFY(rsr.start()); | 
| 2395 |         if (!rsr.cancel()) { | 
| 2396 |             // due to thread scheduling, async cancel might be attempted | 
| 2397 |             // after the request has already finished.. so loop and try again. | 
| 2398 |             rsr.waitForFinished(); | 
| 2399 |             saveList.clear(); | 
| 2400 |             saveList << testRel; | 
| 2401 |             rsr.setRelationships(saveList); | 
| 2402 |             cm->removeRelationship(relationship: testRel); // probably shouldn't have been saved anyway (circular) | 
| 2403 |             bailoutCount -= 1; | 
| 2404 |             if (!bailoutCount) { | 
| 2405 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2406 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2407 |                 break; | 
| 2408 |             } | 
| 2409 |             spy.clear(); | 
| 2410 |             continue; | 
| 2411 |         } | 
| 2412 |         rsr.waitForFinished(); | 
| 2413 |         QVERIFY(rsr.isCanceled()); | 
| 2414 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2415 |         spy.clear(); | 
| 2416 |  | 
| 2417 |         // verify that the changes were not saved | 
| 2418 |         QList<QContactRelationship> aRels = cm->relationships(participantId: aContact.id(), role: QContactRelationship::First); | 
| 2419 |         QVERIFY(!aRels.contains(testRel)); | 
| 2420 |         QCOMPARE(cm->relationships(aContact.id()).size(), originalCount + 2); // should still only be two extra | 
| 2421 |  | 
| 2422 |         break; | 
| 2423 |     } | 
| 2424 | } | 
| 2425 |  | 
| 2426 | void tst_QContactAsync::collectionFetch() | 
| 2427 | { | 
| 2428 |     QFETCH(QString, uri); | 
| 2429 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 2430 |  | 
| 2431 |     QContactCollectionFetchRequest cfr; | 
| 2432 |     QVERIFY(cfr.type() == QContactAbstractRequest::CollectionFetchRequest); | 
| 2433 |  | 
| 2434 |     // initial state - not started, no manager. | 
| 2435 |     QVERIFY(!cfr.isActive()); | 
| 2436 |     QVERIFY(!cfr.isFinished()); | 
| 2437 |     QVERIFY(!cfr.start()); | 
| 2438 |     QVERIFY(!cfr.cancel()); | 
| 2439 |     QVERIFY(!cfr.waitForFinished()); | 
| 2440 |  | 
| 2441 |     // retrieve all collections. | 
| 2442 |     cfr.setManager(cm.data()); | 
| 2443 |     QCOMPARE(cfr.manager(), cm.data()); | 
| 2444 |     QVERIFY(!cfr.isActive()); | 
| 2445 |     QVERIFY(!cfr.isFinished()); | 
| 2446 |     QVERIFY(!cfr.cancel()); | 
| 2447 |     QVERIFY(!cfr.waitForFinished()); | 
| 2448 |     qRegisterMetaType<QContactCollectionFetchRequest*>(typeName: "QContactCollectionFetchRequest*" ); | 
| 2449 |     QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 2450 |     QVERIFY(!cfr.cancel()); // not started | 
| 2451 |  | 
| 2452 |     QVERIFY(cfr.start()); | 
| 2453 |     //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable | 
| 2454 |     QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); | 
| 2455 |     QVERIFY(cfr.waitForFinished()); | 
| 2456 |     QVERIFY(cfr.isFinished()); | 
| 2457 |  | 
| 2458 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2459 |     spy.clear(); | 
| 2460 |  | 
| 2461 |     QList<QContactCollection> syncCols = cm->collections(); | 
| 2462 |     QList<QContactCollection> cols = cfr.collections(); | 
| 2463 |     QCOMPARE(cols.size(), syncCols.size()); | 
| 2464 |     for (int i = 0; i < cols.size(); i++) { | 
| 2465 |         QContactCollection curr = cols.at(i); | 
| 2466 |         QVERIFY(syncCols.contains(curr)); | 
| 2467 |     } | 
| 2468 |  | 
| 2469 |     // cancelling | 
| 2470 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 2471 |     while (true) { | 
| 2472 |         QVERIFY(!cfr.cancel()); // not started | 
| 2473 |         FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); | 
| 2474 |         QVERIFY(cfr.start()); | 
| 2475 |         if (!cfr.cancel()) { | 
| 2476 |             // due to thread scheduling, async cancel might be attempted | 
| 2477 |             // after the request has already finished.. so loop and try again. | 
| 2478 |             spy.clear(); | 
| 2479 |             cfr.waitForFinished(); | 
| 2480 |             bailoutCount -= 1; | 
| 2481 |             if (!bailoutCount) { | 
| 2482 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2483 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2484 |                 break; | 
| 2485 |             } | 
| 2486 |             continue; | 
| 2487 |         } | 
| 2488 |  | 
| 2489 |         // if we get here, then we are cancelling the request. | 
| 2490 |         QVERIFY(cfr.waitForFinished()); | 
| 2491 |         QVERIFY(cfr.isCanceled()); | 
| 2492 |  | 
| 2493 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2494 |         spy.clear(); | 
| 2495 |         break; | 
| 2496 |     } | 
| 2497 |  | 
| 2498 |     // restart, and wait for progress after cancel. | 
| 2499 |     while (true) { | 
| 2500 |         QVERIFY(!cfr.cancel()); // not started | 
| 2501 |         FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); | 
| 2502 |         QVERIFY(cfr.start()); | 
| 2503 |         if (!cfr.cancel()) { | 
| 2504 |             // due to thread scheduling, async cancel might be attempted | 
| 2505 |             // after the request has already finished.. so loop and try again. | 
| 2506 |             cfr.waitForFinished(); | 
| 2507 |             bailoutCount -= 1; | 
| 2508 |             spy.clear(); | 
| 2509 |             if (!bailoutCount) { | 
| 2510 |                 //qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2511 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2512 |                 break; | 
| 2513 |             } | 
| 2514 |             continue; | 
| 2515 |         } | 
| 2516 |         cfr.waitForFinished(); | 
| 2517 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2518 |         spy.clear(); | 
| 2519 |         QVERIFY(!cfr.isActive()); | 
| 2520 |         QVERIFY(cfr.state() == QContactAbstractRequest::CanceledState); | 
| 2521 |         break; | 
| 2522 |     } | 
| 2523 | } | 
| 2524 |  | 
| 2525 | void tst_QContactAsync::collectionRemove() | 
| 2526 | { | 
| 2527 |     QFETCH(QString, uri); | 
| 2528 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 2529 |     QContactCollectionRemoveRequest crr; | 
| 2530 |     QVERIFY(crr.type() == QContactAbstractRequest::CollectionRemoveRequest); | 
| 2531 |  | 
| 2532 |     // initial state - not started, no manager. | 
| 2533 |     QVERIFY(!crr.isActive()); | 
| 2534 |     QVERIFY(!crr.isFinished()); | 
| 2535 |     QVERIFY(!crr.start()); | 
| 2536 |     QVERIFY(!crr.cancel()); | 
| 2537 |     QVERIFY(!crr.waitForFinished()); | 
| 2538 |  | 
| 2539 |     // specific collection set | 
| 2540 |     QContactCollectionId removeId = cm->collections().last().id(); | 
| 2541 |     if (cm->defaultCollectionId() == removeId) | 
| 2542 |         removeId = cm->collections().first().id(); | 
| 2543 |     crr.setCollectionId(removeId); | 
| 2544 |     QVERIFY(crr.collectionIds() == QList<QContactCollectionId>() << removeId); | 
| 2545 |     int originalCount = cm->collections().size(); | 
| 2546 |     crr.setManager(cm.data()); | 
| 2547 |     QCOMPARE(crr.manager(), cm.data()); | 
| 2548 |     QVERIFY(!crr.isActive()); | 
| 2549 |     QVERIFY(!crr.isFinished()); | 
| 2550 |     QVERIFY(!crr.cancel()); | 
| 2551 |     QVERIFY(!crr.waitForFinished()); | 
| 2552 |     qRegisterMetaType<QContactCollectionRemoveRequest*>(typeName: "QContactCollectionRemoveRequest*" ); | 
| 2553 |     QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 2554 |     QVERIFY(!crr.cancel()); // not started | 
| 2555 |     QVERIFY(crr.start()); | 
| 2556 |     QVERIFY((crr.isActive() &&crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); | 
| 2557 |     //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable | 
| 2558 |     QVERIFY(crr.waitForFinished()); | 
| 2559 |     QVERIFY(crr.isFinished()); | 
| 2560 |  | 
| 2561 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2562 |     spy.clear(); | 
| 2563 |  | 
| 2564 |     QCOMPARE(cm->collections().size(), originalCount - 1); // should have removed that particular collection. | 
| 2565 |     QVERIFY(crr.error() == QContactManager::NoError); | 
| 2566 |     QVERIFY(crr.errorMap().isEmpty()); | 
| 2567 |  | 
| 2568 |     // remove all collections | 
| 2569 |     QList<QContactCollectionId> allCollectionIds; | 
| 2570 |     QList<QContactCollection> allCollections = cm->collections(); | 
| 2571 |     for (int i = 0; i < allCollections.size(); ++i) | 
| 2572 |         allCollectionIds << allCollections.at(i).id(); | 
| 2573 |     crr.setCollectionIds(allCollectionIds); | 
| 2574 |  | 
| 2575 |     QVERIFY(!crr.cancel()); // not started | 
| 2576 |     QVERIFY(crr.start()); | 
| 2577 |  | 
| 2578 |     QVERIFY((crr.isActive() && crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); | 
| 2579 |     //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable | 
| 2580 |     QVERIFY(crr.waitForFinished()); | 
| 2581 |     QVERIFY(crr.isFinished()); | 
| 2582 |  | 
| 2583 |     QVERIFY(cm->collections().size() >= 1); // at least one collection must be left, since default collection cannot be removed. | 
| 2584 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2585 |     spy.clear(); | 
| 2586 |  | 
| 2587 |     // remove empty list | 
| 2588 |     QList<QContactCollectionId> collectionIdList; | 
| 2589 |     QContactCollectionRemoveRequest crr1; | 
| 2590 |     crr1.setManager(cm.data()); | 
| 2591 |     crr1.setCollectionIds(collectionIdList); | 
| 2592 |     crr1.start(); | 
| 2593 |     crr1.waitForFinished(); | 
| 2594 |     QVERIFY(crr1.isFinished()); | 
| 2595 |     QVERIFY(crr1.error() == QContactManager::NoError); | 
| 2596 |  | 
| 2597 |     // cancelling | 
| 2598 |     QContactCollection temp; | 
| 2599 |     temp.setMetaData(key: QContactCollection::KeyDescription, value: "Should not be removed!" ); | 
| 2600 |     cm->saveCollection(collection: &temp); | 
| 2601 |     crr.setCollectionId(temp.id()); | 
| 2602 |  | 
| 2603 |     int collectionCount = cm->collections().size(); | 
| 2604 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 2605 |     while (true) { | 
| 2606 |         QVERIFY(!crr.cancel()); // not started | 
| 2607 |         FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); | 
| 2608 |         QVERIFY(spy.count() == 0); | 
| 2609 |         QVERIFY(crr.start()); | 
| 2610 |         if (!crr.cancel()) { | 
| 2611 |             // due to thread scheduling, async cancel might be attempted | 
| 2612 |             // after the request has already finished.. so loop and try again. | 
| 2613 |             crr.waitForFinished(); | 
| 2614 |             temp.setId(QContactCollectionId()); | 
| 2615 |             if (!cm->saveCollection(collection: &temp)) { | 
| 2616 |                 QSKIP("Unable to save temporary item for remove request cancellation test!" ); | 
| 2617 |             } | 
| 2618 |             crr.setCollectionId(temp.id()); | 
| 2619 |             bailoutCount -= 1; | 
| 2620 |             if (!bailoutCount) { | 
| 2621 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2622 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2623 |                 break; | 
| 2624 |             } | 
| 2625 |             spy.clear(); | 
| 2626 |             continue; | 
| 2627 |         } | 
| 2628 |  | 
| 2629 |         // if we get here, then we are cancelling the request. | 
| 2630 |         QVERIFY(crr.waitForFinished()); | 
| 2631 |         QVERIFY(crr.isCanceled()); | 
| 2632 |         QCOMPARE(cm->collections().size(), collectionCount); // temp collection should not have been removed | 
| 2633 |         QList<QContactCollectionId> removeCollectionIds; | 
| 2634 |         QList<QContactCollection> removeCollections = cm->collections(); | 
| 2635 |         for (int i = 0; i < removeCollections.size(); ++i) | 
| 2636 |             removeCollectionIds << removeCollections.at(i).id(); | 
| 2637 |         QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds())); | 
| 2638 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2639 |         spy.clear(); | 
| 2640 |         break; | 
| 2641 |     } | 
| 2642 |  | 
| 2643 |     // restart, and wait for progress after cancel. | 
| 2644 |     while (true) { | 
| 2645 |         QVERIFY(!crr.cancel()); // not started | 
| 2646 |         FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); | 
| 2647 |         QVERIFY(crr.start()); | 
| 2648 |         if (!crr.cancel()) { | 
| 2649 |             // due to thread scheduling, async cancel might be attempted | 
| 2650 |             // after the request has already finished.. so loop and try again. | 
| 2651 |             crr.waitForFinished(); | 
| 2652 |             temp.setId(QContactCollectionId()); | 
| 2653 |             if (!cm->saveCollection(collection: &temp)) { | 
| 2654 |                 QSKIP("Unable to save temporary item for remove request cancellation test!" ); | 
| 2655 |             } | 
| 2656 |             crr.setCollectionId(temp.id()); | 
| 2657 |             bailoutCount -= 1; | 
| 2658 |             if (!bailoutCount) { | 
| 2659 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2660 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2661 |                 break; | 
| 2662 |             } | 
| 2663 |             spy.clear(); | 
| 2664 |             continue; | 
| 2665 |         } | 
| 2666 |         crr.waitForFinished(); | 
| 2667 |         QVERIFY(crr.isCanceled()); | 
| 2668 |         QCOMPARE(cm->collections().size(), collectionCount); // temp collection should not have been removed | 
| 2669 |         QList<QContactCollectionId> removeCollectionIds; | 
| 2670 |         QList<QContactCollection> removeCollections = cm->collections(); | 
| 2671 |         for (int i = 0; i < removeCollections.size(); ++i) | 
| 2672 |             removeCollectionIds << removeCollections.at(i).id(); | 
| 2673 |         QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds())); | 
| 2674 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2675 |         spy.clear(); | 
| 2676 |         break; | 
| 2677 |     } | 
| 2678 |  | 
| 2679 |     // now clean up our temp collection. | 
| 2680 |     cm->removeCollection(collectionId: temp.id()); | 
| 2681 |  | 
| 2682 | } | 
| 2683 |  | 
| 2684 | void tst_QContactAsync::collectionSave() | 
| 2685 | { | 
| 2686 |     QFETCH(QString, uri); | 
| 2687 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 2688 |     QContactCollectionSaveRequest csr; | 
| 2689 |     QVERIFY(csr.type() == QContactAbstractRequest::CollectionSaveRequest); | 
| 2690 |  | 
| 2691 |     // initial state - not started, no manager. | 
| 2692 |     QVERIFY(!csr.isActive()); | 
| 2693 |     QVERIFY(!csr.isFinished()); | 
| 2694 |     QVERIFY(!csr.start()); | 
| 2695 |     QVERIFY(!csr.cancel()); | 
| 2696 |     QVERIFY(!csr.waitForFinished()); | 
| 2697 |  | 
| 2698 |     // save a new item | 
| 2699 |     int originalCount = cm->collections().size(); | 
| 2700 |     QContactCollection testCollection; | 
| 2701 |     testCollection.setMetaData(key: QContactCollection::KeyDescription, value: "test description" ); | 
| 2702 |     testCollection.setMetaData(key: QContactCollection::KeyName, value: "New collection" ); | 
| 2703 |     QList<QContactCollection> saveList; | 
| 2704 |     saveList << testCollection; | 
| 2705 |     csr.setManager(cm.data()); | 
| 2706 |     QCOMPARE(csr.manager(), cm.data()); | 
| 2707 |     QVERIFY(!csr.isActive()); | 
| 2708 |     QVERIFY(!csr.isFinished()); | 
| 2709 |     QVERIFY(!csr.cancel()); | 
| 2710 |     QVERIFY(!csr.waitForFinished()); | 
| 2711 |     qRegisterMetaType<QContactCollectionSaveRequest*>(typeName: "QContactCollectionSaveRequest*" ); | 
| 2712 |     QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); | 
| 2713 |     csr.setCollection(testCollection); | 
| 2714 |     QCOMPARE(csr.collections(), saveList); | 
| 2715 |     QVERIFY(!csr.cancel()); // not started | 
| 2716 |     QVERIFY(csr.start()); | 
| 2717 |  | 
| 2718 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 2719 |     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable | 
| 2720 |     QVERIFY(csr.waitForFinished()); | 
| 2721 |     QVERIFY(csr.isFinished()); | 
| 2722 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2723 |     spy.clear(); | 
| 2724 |  | 
| 2725 |     QList<QContactCollection> expected = csr.collections(); | 
| 2726 |     QCOMPARE(expected.size(), 1); | 
| 2727 |     QList<QContactCollection> result; | 
| 2728 |     result << cm->collection(collectionId: csr.collections().at(i: 0).id()); | 
| 2729 |  | 
| 2730 |     // find the saved one, compare. | 
| 2731 |     foreach (const QContactCollection &col, result) | 
| 2732 |         QVERIFY(col.id() == expected.at(0).id()); | 
| 2733 |  | 
| 2734 |     // update a previously saved collection | 
| 2735 |     QVERIFY(!result.isEmpty()); // make sure that we were able to retrieve the required collection. | 
| 2736 |     testCollection = result.first(); | 
| 2737 |     testCollection.setMetaData(key: QContactCollection::KeyName, value: "test name" ); | 
| 2738 |     saveList.clear(); | 
| 2739 |     saveList << testCollection; | 
| 2740 |     csr.setCollections(saveList); | 
| 2741 |     QCOMPARE(csr.collections(), saveList); | 
| 2742 |     QVERIFY(!csr.cancel()); // not started | 
| 2743 |     QVERIFY(csr.start()); | 
| 2744 |  | 
| 2745 |     QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); | 
| 2746 |     //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable | 
| 2747 |     QVERIFY(csr.waitForFinished()); | 
| 2748 |  | 
| 2749 |     QVERIFY(csr.isFinished()); | 
| 2750 |     QVERIFY(spy.count() >= 1); // active + finished progress signals | 
| 2751 |     spy.clear(); | 
| 2752 |  | 
| 2753 |     expected = csr.collections(); | 
| 2754 |     result.clear(); | 
| 2755 |     result = cm->collections(); | 
| 2756 |     // find the saved one, compare. | 
| 2757 |     foreach (const QContactCollection& col, result) { | 
| 2758 |         if (col.id() == expected.at(i: 0).id()) { | 
| 2759 |             QVERIFY(col == expected.at(0)); // XXX TODO: if we change the semantic so that save merely updates the id...? | 
| 2760 |         } | 
| 2761 |     } | 
| 2762 |     QCOMPARE(cm->collections().size(), originalCount + 1); // ie shouldn't have added an extra one (would be +2) | 
| 2763 |     QVERIFY(csr.error() == QContactManager::NoError); | 
| 2764 |     QVERIFY(csr.errorMap().isEmpty()); | 
| 2765 |  | 
| 2766 |     // save empty list | 
| 2767 |     QList<QContactCollection> collectionList; | 
| 2768 |     QContactCollectionSaveRequest csr1; | 
| 2769 |     csr1.setManager(cm.data()); | 
| 2770 |     csr1.setCollections(collectionList); | 
| 2771 |     csr1.start(); | 
| 2772 |     csr1.waitForFinished(); | 
| 2773 |     QVERIFY(csr1.isFinished()); | 
| 2774 |     QVERIFY(csr1.error() == QContactManager::NoError); | 
| 2775 |  | 
| 2776 |     // cancelling | 
| 2777 |     QContactCollection temp; | 
| 2778 |     temp.setMetaData(testCollection.metaData()); | 
| 2779 |     temp.setExtendedMetaData(key: "test" , value: "shouldn't be saved" ); | 
| 2780 |     saveList.clear(); | 
| 2781 |     saveList << temp; | 
| 2782 |     csr.setCollections(saveList); | 
| 2783 |  | 
| 2784 |     int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out. | 
| 2785 |     while (true) { | 
| 2786 |         QVERIFY(!csr.cancel()); // not started | 
| 2787 |         FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); | 
| 2788 |         QVERIFY(csr.start()); | 
| 2789 |         if (!csr.cancel()) { | 
| 2790 |             // due to thread scheduling, async cancel might be attempted | 
| 2791 |             // after the request has already finished.. so loop and try again. | 
| 2792 |             csr.waitForFinished(); | 
| 2793 |             saveList = csr.collections(); | 
| 2794 |             if (cm->collections().size() > (originalCount + 1) && !cm->removeCollection(collectionId: saveList.at(i: 0).id())) { | 
| 2795 |                 QSKIP("Unable to remove saved collection to test cancellation of collection save request" ); | 
| 2796 |             } | 
| 2797 |             saveList.clear(); | 
| 2798 |             saveList << temp; | 
| 2799 |             csr.setCollections(saveList); | 
| 2800 |             bailoutCount -= 1; | 
| 2801 |             if (!bailoutCount) { | 
| 2802 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2803 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2804 |                 break; | 
| 2805 |             } | 
| 2806 |             spy.clear(); | 
| 2807 |             continue; | 
| 2808 |         } | 
| 2809 |  | 
| 2810 |         // if we get here, then we are cancelling the request. | 
| 2811 |         QVERIFY(csr.waitForFinished()); | 
| 2812 |         QVERIFY(csr.isCanceled()); | 
| 2813 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2814 |         spy.clear(); | 
| 2815 |  | 
| 2816 |         // verify that the changes were not saved | 
| 2817 |         expected.clear(); | 
| 2818 |         QList<QContactCollection> allCollections = cm->collections(); | 
| 2819 |         QVERIFY(!allCollections.contains(temp)); // should NOT contain it since it was cancelled. | 
| 2820 |         QCOMPARE(allCollections.size(), originalCount + 1); | 
| 2821 |         break; | 
| 2822 |     } | 
| 2823 |     // restart, and wait for progress after cancel. | 
| 2824 |  | 
| 2825 |     while (true) { | 
| 2826 |         QVERIFY(!csr.cancel()); // not started | 
| 2827 |         FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); | 
| 2828 |         QVERIFY(csr.start()); | 
| 2829 |         if (!csr.cancel()) { | 
| 2830 |             // due to thread scheduling, async cancel might be attempted | 
| 2831 |             // after the request has already finished.. so loop and try again. | 
| 2832 |             csr.waitForFinished(); | 
| 2833 |             saveList = csr.collections(); | 
| 2834 |             if (cm->collections().size() > (originalCount + 1) && !cm->removeCollection(collectionId: saveList.at(i: 0).id())) { | 
| 2835 |                 QSKIP("Unable to remove saved item to test cancellation of item save request" ); | 
| 2836 |             } | 
| 2837 |             saveList.clear(); | 
| 2838 |             saveList << temp; | 
| 2839 |             csr.setCollections(saveList); | 
| 2840 |             bailoutCount -= 1; | 
| 2841 |             if (!bailoutCount) { | 
| 2842 | //                qWarning("Unable to test cancelling due to thread scheduling!"); | 
| 2843 |                 bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; | 
| 2844 |                 break; | 
| 2845 |             } | 
| 2846 |             spy.clear(); | 
| 2847 |             continue; | 
| 2848 |         } | 
| 2849 |         csr.waitForFinished(); // now wait until finished (if it hasn't already). | 
| 2850 |         QVERIFY(csr.isCanceled()); | 
| 2851 |         QVERIFY(spy.count() >= 1); // active + cancelled progress signals | 
| 2852 |         spy.clear(); | 
| 2853 |  | 
| 2854 |         // verify that the changes were not saved | 
| 2855 |         expected.clear(); | 
| 2856 |         QList<QContactCollection> allCollections = cm->collections(); | 
| 2857 |         QVERIFY(!allCollections.contains(temp)); | 
| 2858 |         QCOMPARE(cm->collections().size(), originalCount + 1); | 
| 2859 |         break; | 
| 2860 |     } | 
| 2861 | } | 
| 2862 |  | 
| 2863 | void tst_QContactAsync::maliciousManager() | 
| 2864 | { | 
| 2865 |     // use the invalid manager: passes all requests through to base class | 
| 2866 |     QContactManager cm("invalid" ); | 
| 2867 |     QContactFilter fil; // matches everything | 
| 2868 |     QContactFetchRequest cfr; | 
| 2869 |     cfr.setFilter(fil); | 
| 2870 |     cfr.setManager(&cm); | 
| 2871 |     QVERIFY(!cfr.start()); | 
| 2872 |     QVERIFY(!cfr.cancel()); | 
| 2873 |     QVERIFY(!cfr.waitForFinished()); | 
| 2874 |     QVERIFY(!cfr.start()); | 
| 2875 |  | 
| 2876 |     // ensure that the base class implementation of requestDestroyed() is called | 
| 2877 |     QContactFetchRequest *destroyedRequest = new QContactFetchRequest; | 
| 2878 |     destroyedRequest->setManager(&cm); | 
| 2879 |     destroyedRequest->setFilter(fil); | 
| 2880 |     QVERIFY(!destroyedRequest->start()); | 
| 2881 |     delete destroyedRequest; | 
| 2882 |  | 
| 2883 |     // now use a malicious manager that deliberately calls | 
| 2884 |     // things in a different thread | 
| 2885 |     if (!QContactManager::availableManagers().contains(str: "maliciousplugin" )) | 
| 2886 |         QSKIP("Malicious plugin is not installed, skipping test." ); | 
| 2887 |     QContactManager mcm("maliciousplugin" ); | 
| 2888 |     QCOMPARE(mcm.managerName(), QString("maliciousplugin" )); | 
| 2889 |     QList<QContact> emptyCList; | 
| 2890 |     QList<QContactId> emptyIList; | 
| 2891 |     QStringList emptyDNList; | 
| 2892 |     cfr.setFilter(fil); | 
| 2893 |     cfr.setManager(&mcm); | 
| 2894 |     QVERIFY(cfr.start()); | 
| 2895 |  | 
| 2896 |     QContactIdFetchRequest cifr; | 
| 2897 |     cifr.setFilter(fil); | 
| 2898 |     cifr.setManager(&mcm); | 
| 2899 |     QVERIFY(cifr.start()); | 
| 2900 |  | 
| 2901 |     QContactRemoveRequest crr; | 
| 2902 |     crr.setContactIds(mcm.contactIds(filter: fil)); | 
| 2903 |     crr.setManager(&mcm); | 
| 2904 |     QVERIFY(crr.start()); | 
| 2905 |  | 
| 2906 |     QContactSaveRequest csr; | 
| 2907 |     csr.setContacts(emptyCList); | 
| 2908 |     csr.setManager(&mcm); | 
| 2909 |     QVERIFY(csr.start()); | 
| 2910 |  | 
| 2911 | } | 
| 2912 |  | 
| 2913 | void tst_QContactAsync::testQuickDestruction() | 
| 2914 | { | 
| 2915 |     QFETCH(QString, uri); | 
| 2916 |  | 
| 2917 |     // in this test, we create a manager, fire off a request, and delete the manager, all in quick succession | 
| 2918 |     // this is to test for segfaults etc. | 
| 2919 |     for (int i = 0; i < 10; i++) { | 
| 2920 |         QContactFetchRequest cfr; | 
| 2921 |         QContactManager *cm = prepareModel(uri); | 
| 2922 |         cfr.setManager(cm); | 
| 2923 |         cfr.start(); | 
| 2924 |         delete cm; | 
| 2925 |     } | 
| 2926 |     // in this test, we create a manager, fire off a request, delete the request, then delete the manager, all in quick succession | 
| 2927 |     // this is to test for segfaults, etc. | 
| 2928 |     for (int i = 0; i < 10; i++) { | 
| 2929 |         QContactFetchRequest *cfr = new QContactFetchRequest; | 
| 2930 |         QContactManager *cm = prepareModel(uri); | 
| 2931 |         cfr->setManager(cm); | 
| 2932 |         cfr->start(); | 
| 2933 |         delete cfr; | 
| 2934 |         delete cm; | 
| 2935 |     } | 
| 2936 |     // in this test, we create a manager, fire off a request, delete the manager, then delete the request, all in quick succession | 
| 2937 |     // this is to test for segfaults, etc. | 
| 2938 |     for (int i = 0; i < 10; i++) { | 
| 2939 |         QContactFetchRequest *cfr = new QContactFetchRequest; | 
| 2940 |         QContactManager *cm = prepareModel(uri); | 
| 2941 |         cfr->setManager(cm); | 
| 2942 |         cfr->start(); | 
| 2943 |         delete cm; | 
| 2944 |         delete cfr; | 
| 2945 |     } | 
| 2946 |     // in this test, we create a manager, fire off a request, and delete the request, all in quick succession | 
| 2947 |     // this is to test for segfaults, etc. | 
| 2948 |     QContactManager *cm = prepareModel(uri); | 
| 2949 |     for (int i = 0; i < 10; i++) { | 
| 2950 |         QContactFetchRequest *cfr = new QContactFetchRequest; | 
| 2951 |         cfr->setManager(cm); | 
| 2952 |         cfr->start(); | 
| 2953 |         delete cfr; | 
| 2954 |     } | 
| 2955 |     delete cm; | 
| 2956 | } | 
| 2957 |  | 
| 2958 | void tst_QContactAsync::threadDelivery() | 
| 2959 | { | 
| 2960 |     QFETCH(QString, uri); | 
| 2961 |     QScopedPointer<QContactManager> cm(prepareModel(uri)); | 
| 2962 |     m_mainThreadId = cm->thread()->currentThreadId(); | 
| 2963 |     m_resultsAvailableSlotThreadId = m_mainThreadId; | 
| 2964 |  | 
| 2965 |     // now perform a fetch request and check that the progress is delivered to the correct thread. | 
| 2966 |     QContactFetchRequest *req = new QContactFetchRequest; | 
| 2967 |     req->setManager(cm.data()); | 
| 2968 |     connect(sender: req, SIGNAL(resultsAvailable()), receiver: this, SLOT(resultsAvailableReceived())); | 
| 2969 |     req->start(); | 
| 2970 |  | 
| 2971 |     int totalWaitTime = 0; | 
| 2972 |     QTest::qWait(ms: 1); // force it to process events at least once. | 
| 2973 |     while (req->state() != QContactAbstractRequest::FinishedState) { | 
| 2974 |         // ensure that the progress signal was delivered to the main thread. | 
| 2975 |         QCOMPARE(m_mainThreadId, m_resultsAvailableSlotThreadId); | 
| 2976 |  | 
| 2977 |         QTest::qWait(ms: 5); // spin until done | 
| 2978 |         totalWaitTime += 5; | 
| 2979 |  | 
| 2980 |         // break after 30 seconds. | 
| 2981 |         if (totalWaitTime > 30000) { | 
| 2982 |             delete req; | 
| 2983 |             QSKIP("Asynchronous request not complete after 30 seconds!" ); | 
| 2984 |         } | 
| 2985 |     } | 
| 2986 |  | 
| 2987 |     // ensure that the progress signal was delivered to the main thread. | 
| 2988 |     QCOMPARE(m_mainThreadId, m_resultsAvailableSlotThreadId); | 
| 2989 |     delete req; | 
| 2990 | } | 
| 2991 |  | 
| 2992 | void tst_QContactAsync::resultsAvailableReceived() | 
| 2993 | { | 
| 2994 |     QContactFetchRequest *req = qobject_cast<QContactFetchRequest *>(object: QObject::sender()); | 
| 2995 |     Q_ASSERT(req); | 
| 2996 |     m_resultsAvailableSlotThreadId = req->thread()->currentThreadId(); | 
| 2997 | } | 
| 2998 |  | 
| 2999 | void tst_QContactAsync::addManagers(QStringList stringlist) | 
| 3000 | { | 
| 3001 |     QTest::addColumn<QString>(name: "uri" ); | 
| 3002 |  | 
| 3003 |     // retrieve the list of available managers | 
| 3004 |     QStringList managers = QContactManager::availableManagers(); | 
| 3005 |  | 
| 3006 |     // remove ones that we know will not pass | 
| 3007 |     if (!stringlist.contains(str: "invalid" )) | 
| 3008 |         managers.removeAll(t: "invalid" ); | 
| 3009 |     if (!stringlist.contains(str: "maliciousplugin" )) | 
| 3010 |         managers.removeAll(t: "maliciousplugin" ); | 
| 3011 |     if (!stringlist.contains(str: "testdummy" )) | 
| 3012 |         managers.removeAll(t: "testdummy" ); | 
| 3013 |     if (!stringlist.contains(str: "social" )) | 
| 3014 |         managers.removeAll(t: "social" ); | 
| 3015 |     if (!stringlist.contains(str: "simcard" )) | 
| 3016 |         managers.removeAll(t: "simcard" ); | 
| 3017 |     if (!stringlist.contains(str: "com.nokia.messaging.contacts.engines.mail.contactslookup" )) | 
| 3018 |         managers.removeAll(t: "com.nokia.messaging.contacts.engines.mail.contactslookup" ); | 
| 3019 |  | 
| 3020 |     foreach(QString mgr, managers) { | 
| 3021 |         QMap<QString, QString> params; | 
| 3022 |         QTest::newRow(dataTag: QString("mgr='%1'" ).arg(a: mgr).toLatin1().constData()) << QContactManager::buildUri(managerName: mgr, params); | 
| 3023 |         if (mgr == "memory" ) { | 
| 3024 |             params.insert(key: "id" , value: "tst_QContactManager" ); | 
| 3025 |             QTest::newRow(dataTag: QString("mgr='%1', params" ).arg(a: mgr).toLatin1().constData()) << QContactManager::buildUri(managerName: mgr, params); | 
| 3026 |         } | 
| 3027 |     } | 
| 3028 | } | 
| 3029 |  | 
| 3030 | QContactManager* tst_QContactAsync::prepareModel(const QString& managerUri) | 
| 3031 | { | 
| 3032 |     QContactManager* cm = QContactManager::fromUri(uri: managerUri); | 
| 3033 |  | 
| 3034 |     // XXX TODO: ensure that this is the case: | 
| 3035 |     // there should be no contacts in the database. | 
| 3036 |     QList<QContactId> toRemove = cm->contactIds(); | 
| 3037 |     foreach (const QContactId& removeId, toRemove) | 
| 3038 |         cm->removeContact(contactId: removeId); | 
| 3039 |  | 
| 3040 |     QContact a, b, c; | 
| 3041 |     QContactName aNameDetail; | 
| 3042 |     aNameDetail.setFirstName("Aaron" ); | 
| 3043 |     aNameDetail.setLastName("Aaronson" ); | 
| 3044 |     a.saveDetail(detail: &aNameDetail); | 
| 3045 |     QContactName bNameDetail; | 
| 3046 |     bNameDetail.setFirstName("Bob" ); | 
| 3047 |     bNameDetail.setLastName("Aaronsen" ); | 
| 3048 |     b.saveDetail(detail: &bNameDetail); | 
| 3049 |     QContactName cNameDetail; | 
| 3050 |     cNameDetail.setFirstName("Borris" ); | 
| 3051 |     cNameDetail.setLastName("Aaronsun" ); | 
| 3052 |     c.saveDetail(detail: &cNameDetail); | 
| 3053 |  | 
| 3054 |     QContactPhoneNumber phn; | 
| 3055 |     phn.setNumber("0123" ); | 
| 3056 |     c.saveDetail(detail: &phn); | 
| 3057 |     phn.setNumber("3456" ); | 
| 3058 |     b.saveDetail(detail: &phn); | 
| 3059 |     phn.setNumber("6789" ); | 
| 3060 |     a.saveDetail(detail: &phn); | 
| 3061 |  | 
| 3062 |     QContactUrl url; | 
| 3063 |     url.setUrl("http://test.nokia.com" ); | 
| 3064 |     a.saveDetail(detail: &url); | 
| 3065 |  | 
| 3066 |     cm->saveContact(contact: &a); | 
| 3067 |     cm->saveContact(contact: &b); | 
| 3068 |     cm->saveContact(contact: &c); | 
| 3069 |  | 
| 3070 |     if (cm->contacts().size() != 3) | 
| 3071 |         qWarning() << Q_FUNC_INFO << "Failed to prepare model!" ; | 
| 3072 |  | 
| 3073 |     QContactRelationship arb; | 
| 3074 |     arb.setFirst(a.id()); | 
| 3075 |     arb.setSecond(b.id()); | 
| 3076 |     arb.setRelationshipType(QContactRelationship::HasManager()); | 
| 3077 |     cm->saveRelationship(relationship: &arb); | 
| 3078 |  | 
| 3079 |     QContactRelationship brc; | 
| 3080 |     brc.setFirst(b.id()); | 
| 3081 |     brc.setSecond(c.id()); | 
| 3082 |     brc.setRelationshipType(QContactRelationship::HasAssistant()); | 
| 3083 |     cm->saveRelationship(relationship: &brc); | 
| 3084 |  | 
| 3085 |     QContactRelationship cra; | 
| 3086 |     cra.setFirst(c.id()); | 
| 3087 |     cra.setSecond(a.id()); | 
| 3088 |     cra.setRelationshipType(QContactRelationship::HasSpouse()); | 
| 3089 |     cm->saveRelationship(relationship: &cra); | 
| 3090 |  | 
| 3091 |     QContactRelationship arc; | 
| 3092 |     arc.setFirst(a.id()); | 
| 3093 |     arc.setSecond(c.id()); | 
| 3094 |     arc.setRelationshipType(QContactRelationship::HasAssistant()); | 
| 3095 |     cm->saveRelationship(relationship: &arc); | 
| 3096 |  | 
| 3097 |     QContactRelationship crb; | 
| 3098 |     crb.setFirst(c.id()); | 
| 3099 |     crb.setSecond(b.id()); | 
| 3100 |     crb.setRelationshipType(QContactRelationship::IsSameAs()); | 
| 3101 |     cm->saveRelationship(relationship: &crb); | 
| 3102 |  | 
| 3103 |     QContactCollection testCollection; | 
| 3104 |     testCollection.setMetaData(key: QContactCollection::KeyName, value: "Test Collection" ); | 
| 3105 |     testCollection.setMetaData(key: QContactCollection::KeyDescription, value: "test collection" ); | 
| 3106 |     cm->saveCollection(collection: &testCollection); | 
| 3107 |  | 
| 3108 |     return cm; | 
| 3109 |  | 
| 3110 |     // TODO: cleanup once test is complete | 
| 3111 | } | 
| 3112 |  | 
| 3113 | QTEST_MAIN(tst_QContactAsync) | 
| 3114 | #include "tst_qcontactasync.moc" | 
| 3115 |  |