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
43QTCONTACTS_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)
66class QThreadSignalSpy: public QObject
67{
68public:
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
132private:
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
167static 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
173class tst_QContactAsync : public QObject
174{
175 Q_OBJECT
176
177public:
178 tst_QContactAsync();
179 virtual ~tst_QContactAsync();
180
181public slots:
182 void initTestCase();
183 void cleanupTestCase();
184
185private:
186 void addManagers(QStringList includes = QStringList()); // add standard managers to the data
187
188private 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"))); }
246protected slots:
247 void resultsAvailableReceived();
248
249private:
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
263tst_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
272tst_QContactAsync::~tst_QContactAsync()
273{
274}
275
276void tst_QContactAsync::initTestCase()
277{
278 managerDataHolder.reset(other: new QContactManagerDataHolder());
279}
280
281void tst_QContactAsync::cleanupTestCase()
282{
283 managerDataHolder.reset(other: 0);
284}
285
286bool 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
303bool 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
340bool 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
353bool 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
386bool 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
398void 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
418void 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
646void 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
693void 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
738void 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
791void 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
838void 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
889void 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
1042void 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
1182void 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
1288void 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
1469void 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
1573void 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
1631void 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
1676void 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
1869void 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
1914void 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
2113void 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
2262void 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
2426void 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
2525void 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
2684void 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
2863void 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
2913void 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
2958void 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
2992void 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
2999void 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
3030QContactManager* 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
3113QTEST_MAIN(tst_QContactAsync)
3114#include "tst_qcontactasync.moc"
3115

source code of qtpim/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp