diff -r e686773b3f54 -r 0ba2181d7c28 qtcontactsmobility/tests/auto/qcontactasync/unittest/tst_qcontactasync.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qtcontactsmobility/tests/auto/qcontactasync/unittest/tst_qcontactasync.cpp Fri Mar 19 09:27:18 2010 +0200 @@ -0,0 +1,2167 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include "qtcontacts.h" +#include "qcontactmanagerdataholder.h" //QContactManagerDataHolder + +QTM_USE_NAMESPACE + +class tst_QContactAsync : public QObject +{ + Q_OBJECT + +public: + tst_QContactAsync(); + virtual ~tst_QContactAsync(); + +public slots: + void init(); + void cleanup(); + +private: + void addManagers(); // add standard managers to the data + +private slots: + void testDestructor(); + void testDestructor_data() { addManagers(); } + + void contactFetch(); + void contactFetch_data() { addManagers(); } + void contactIdFetch(); + void contactIdFetch_data() { addManagers(); } + void contactRemove(); + void contactRemove_data() { addManagers(); } + void contactSave(); + void contactSave_data() { addManagers(); } + + void definitionFetch(); + void definitionFetch_data() { addManagers(); } + void definitionRemove(); + void definitionRemove_data() { addManagers(); } + void definitionSave(); + void definitionSave_data() { addManagers(); } + + void relationshipFetch(); + void relationshipFetch_data() { addManagers(); } + void relationshipRemove(); + void relationshipRemove_data() { addManagers(); } + void relationshipSave(); + void relationshipSave_data() { addManagers(); } + + void maliciousManager(); // uses it's own custom data (manager) + + void threadDelivery(); + void progressReceived(QContactFetchRequest* request, bool appendOnly); + void threadDelivery_data() { addManagers(); } + +private: + bool containsIgnoringTimestamps(const QList& list, const QContact& c); + bool compareIgnoringTimestamps(const QContact& ca, const QContact& cb); + bool prepareModel(const QString &uri, QContactManager *&cm); + bool prepareModel(const QString &uri, QContactManager *&cm, QList &contacts, QList &relationships); + + Qt::HANDLE m_mainThreadId; + Qt::HANDLE m_progressSlotThreadId; + QContactManagerDataHolder managerDataHolder; +}; + +typedef QList QContactLocalIdList; +Q_DECLARE_METATYPE(QContactLocalIdList); + +tst_QContactAsync::tst_QContactAsync() +{ + // ensure we can load all of the plugins we need to. + QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins"; + QApplication::addLibraryPath(path); + + qRegisterMetaType("QList"); +} + +tst_QContactAsync::~tst_QContactAsync() +{ + QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins"; + QApplication::removeLibraryPath(path); +} + +void tst_QContactAsync::init() +{ +} + +void tst_QContactAsync::cleanup() +{ +} + +bool tst_QContactAsync::containsIgnoringTimestamps(const QList& list, const QContact& c) +{ + QList cl = list; + QContact a(c); + for (int i = 0; i < cl.size(); i++) { + QContact b(cl.at(i)); + if (compareIgnoringTimestamps(a, b)) + return true; + } + + return false; +} + +bool tst_QContactAsync::compareIgnoringTimestamps(const QContact& ca, const QContact& cb) +{ + // Compares two contacts, ignoring any timestamp details + QContact a(ca); + QContact b(cb); + QList aDetails = a.details(); + QList bDetails = b.details(); + + // They can be in any order, so loop + // First remove any matches, and any timestamps + foreach (QContactDetail d, aDetails) { + foreach (QContactDetail d2, bDetails) { + if (d == d2) { + a.removeDetail(&d); + b.removeDetail(&d2); + break; + } + + if (d.definitionName() == QContactTimestamp::DefinitionName) { + a.removeDetail(&d); + } + + if (d2.definitionName() == QContactTimestamp::DefinitionName) { + b.removeDetail(&d2); + } + } + } + + if (a == b) + return true; + return false; +} + +void tst_QContactAsync::testDestructor() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + QContactFetchRequest* req = new QContactFetchRequest; + req->setManager(cm); + + QContactManager* cm2(0); + QVERIFY(prepareModel(uri, cm2)); + QContactFetchRequest* req2 = new QContactFetchRequest; + req2->setManager(cm2); + + // first, delete manager then request + delete cm; + delete req; + + // second, delete request then manager + delete req2; + delete cm2; +} + +void tst_QContactAsync::contactFetch() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + QContactFetchRequest cfr; + QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchRequest); + + // initial state - not started, no manager. + QVERIFY(!cfr.isActive()); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + QVERIFY(!cfr.waitForProgress()); + + // "all contacts" retrieval + QContactFilter fil; + cfr.setManager(cm); + QCOMPARE(cfr.manager(), cm); + QVERIFY(!cfr.isActive()); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + QVERIFY(!cfr.waitForProgress()); + qRegisterMetaType("QContactFetchRequest*"); + QSignalSpy spy(&cfr, SIGNAL(progress(QContactFetchRequest*, bool))); + cfr.setFilter(fil); + QCOMPARE(cfr.filter(), fil); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + QList contactIds = cm->contacts(); + QList contacts = cfr.contacts(); + QCOMPARE(contactIds.size(), contacts.size()); + for (int i = 0; i < contactIds.size(); i++) { + QContact curr = cm->contact(contactIds.at(i)); + QVERIFY(contacts.at(i) == curr); + } + + // asynchronous detail filtering + QContactDetailFilter dfil; + dfil.setDetailDefinitionName(QContactUrl::DefinitionName, QContactUrl::FieldUrl); + cfr.setFilter(dfil); + QVERIFY(cfr.filter() == dfil); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + contactIds = cm->contacts(dfil); + contacts = cfr.contacts(); + QCOMPARE(contactIds.size(), contacts.size()); + for (int i = 0; i < contactIds.size(); i++) { + QContact curr = cm->contact(contactIds.at(i)); + QVERIFY(contacts.at(i) == curr); + } + + // sort order + QContactSortOrder sortOrder; + sortOrder.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber); + QList sorting; + sorting.append(sortOrder); + cfr.setFilter(fil); + cfr.setSorting(sorting); + QCOMPARE(cfr.sorting(), sorting); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + contactIds = cm->contacts(sorting); + contacts = cfr.contacts(); + QCOMPARE(contactIds.size(), contacts.size()); + for (int i = 0; i < contactIds.size(); i++) { + QContact curr = cm->contact(contactIds.at(i)); + QVERIFY(contacts.at(i) == curr); + } + + // restrictions + sorting.clear(); + cfr.setFilter(fil); + cfr.setSorting(sorting); + cfr.setDefinitionRestrictions(QStringList(QContactName::DefinitionName)); + QCOMPARE(cfr.definitionRestrictions(), QStringList(QContactName::DefinitionName)); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + contactIds = cm->contacts(sorting); + contacts = cfr.contacts(); + QCOMPARE(contactIds.size(), contacts.size()); + for (int i = 0; i < contactIds.size(); i++) { + // create a contact from the restricted data only (id + display label) + QContact currFull = cm->contact(contactIds.at(i)); + QContact currRestricted; + currRestricted.setId(currFull.id()); + QList names = currFull.details(); + foreach (const QContactName& name, names) { + QContactName fullName = name; + if (!fullName.isEmpty()) { + currRestricted.saveDetail(&fullName); + } + } + + // now find the contact in the retrieved list which our restricted contact mimics + QContact retrievedRestricted; + bool found = false; + foreach (const QContact& retrieved, contacts) { + if (retrieved.id() == currRestricted.id()) { + retrievedRestricted = retrieved; + found = true; + } + } + + QVERIFY(found); // must exist or fail. + + // ensure that the contact is the same (except synth fields) + QList fdets = retrievedRestricted.details(); + QList rdets = currRestricted.details(); + foreach (const QContactDetail& det, fdets) { + // ignore backend synthesised details + // again, this requires a "default contact details" function to work properly. + if (det.definitionName() == QContactDisplayLabel::DefinitionName + || det.definitionName() == QContactTimestamp::DefinitionName) { + continue; + } + + // everything else must exist in both. + if(!rdets.contains(det)) { + qWarning("A detail exists in retrieved contact which doesn't exist in restricted contact! This could be due to backend synthesization, or represent a bug! (Definition name: %s)", det.definitionName().toAscii().constData()); + } + } + } + + // cancelling + sorting.clear(); + cfr.setFilter(fil); + cfr.setSorting(sorting); + cfr.setDefinitionRestrictions(QStringList()); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(cfr.cancel()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(cfr.isActive()); // still cancelling + QVERIFY(!cfr.isFinished()); // not finished cancelling + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelled); + + // restart, and wait for progress after cancel. + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(cfr.cancel()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(cfr.isActive()); // still cancelling + QVERIFY(!cfr.isFinished()); // not finished cancelling + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelled); + + delete cm; +} + +void tst_QContactAsync::contactIdFetch() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + QContactLocalIdFetchRequest cfr; + QVERIFY(cfr.type() == QContactAbstractRequest::ContactLocalIdFetchRequest); + + // initial state - not started, no manager. + QVERIFY(!cfr.isActive()); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + QVERIFY(!cfr.waitForProgress()); + + // "all contacts" retrieval + QContactFilter fil; + cfr.setManager(cm); + QCOMPARE(cfr.manager(), cm); + QVERIFY(!cfr.isActive()); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + QVERIFY(!cfr.waitForProgress()); + qRegisterMetaType("QContactLocalIdFetchRequest*"); + QSignalSpy spy(&cfr, SIGNAL(progress(QContactLocalIdFetchRequest*, bool))); + cfr.setFilter(fil); + QCOMPARE(cfr.filter(), fil); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + QList contactIds = cm->contacts(); + QList result = cfr.ids(); + QCOMPARE(contactIds, result); + + // asynchronous detail filtering + QContactDetailFilter dfil; + dfil.setDetailDefinitionName(QContactUrl::DefinitionName, QContactUrl::FieldUrl); + cfr.setFilter(dfil); + QVERIFY(cfr.filter() == dfil); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + contactIds = cm->contacts(dfil); + result = cfr.ids(); + QCOMPARE(contactIds, result); + + // sort order + QContactSortOrder sortOrder; + sortOrder.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber); + QList sorting; + sorting.append(sortOrder); + cfr.setFilter(fil); + cfr.setSorting(sorting); + QCOMPARE(cfr.sorting(), sorting); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + + contactIds = cm->contacts(sorting); + result = cfr.ids(); + QCOMPARE(contactIds, result); + + // cancelling + sorting.clear(); + cfr.setFilter(fil); + cfr.setSorting(sorting); + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(cfr.cancel()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(cfr.isActive()); // still cancelling + QVERIFY(!cfr.isFinished()); // not finished cancelling + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelled); + + // restart, and wait for progress after cancel. + QVERIFY(!cfr.cancel()); // not started + QVERIFY(cfr.start()); + QVERIFY(cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Active); + QVERIFY(!cfr.isFinished()); + QVERIFY(cfr.cancel()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(cfr.isActive()); // still cancelling + QVERIFY(!cfr.isFinished()); // not finished cancelling + QVERIFY(!cfr.start()); // already started. + QVERIFY(cfr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(cfr.isFinished()); + QVERIFY(!cfr.isActive()); + QVERIFY(cfr.status() == QContactAbstractRequest::Cancelled); + + delete cm; +} + +void tst_QContactAsync::contactRemove() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + QContactRemoveRequest crr; + QVERIFY(crr.type() == QContactAbstractRequest::ContactRemoveRequest); + + // initial state - not started, no manager. + QVERIFY(!crr.isActive()); + QVERIFY(!crr.isFinished()); + QVERIFY(!crr.start()); + QVERIFY(!crr.cancel()); + QVERIFY(!crr.waitForFinished()); + QVERIFY(!crr.waitForProgress()); + + // specific contact removal + int originalCount = cm->contacts().size(); + QContactDetailFilter dfil; + dfil.setDetailDefinitionName(QContactUrl::DefinitionName, QContactUrl::FieldUrl); + crr.setFilter(dfil); + crr.setManager(cm); + QCOMPARE(crr.manager(), cm); + QVERIFY(!crr.isActive()); + QVERIFY(!crr.isFinished()); + QVERIFY(!crr.cancel()); + QVERIFY(!crr.waitForFinished()); + QVERIFY(!crr.waitForProgress()); + qRegisterMetaType("QContactRemoveRequest*"); + QSignalSpy spy(&crr, SIGNAL(progress(QContactRemoveRequest*))); + QVERIFY(crr.filter() == dfil); + QVERIFY(!crr.cancel()); // not started + QVERIFY(crr.start()); + QVERIFY(crr.isActive()); + QVERIFY(crr.status() == QContactAbstractRequest::Active); + QVERIFY(!crr.isFinished()); + QVERIFY(!crr.start()); // already started. + QVERIFY(crr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(crr.isFinished()); + QVERIFY(!crr.isActive()); + + QCOMPARE(cm->contacts().size(), originalCount - 1); + QVERIFY(cm->contacts(dfil).isEmpty()); + + // remove all contacts + dfil.setDetailDefinitionName(QContactDisplayLabel::DefinitionName); // delete everything. + crr.setFilter(dfil); + QVERIFY(crr.filter() == dfil); + QVERIFY(!crr.cancel()); // not started + QVERIFY(crr.start()); + QVERIFY(crr.isActive()); + QVERIFY(crr.status() == QContactAbstractRequest::Active); + QVERIFY(!crr.isFinished()); + QVERIFY(!crr.start()); // already started. + QVERIFY(crr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(crr.isFinished()); + QVERIFY(!crr.isActive()); + + QCOMPARE(cm->contacts().size(), 0); // no contacts should be left. + + // cancelling + QContact temp; + QContactName nameDetail; + nameDetail.setFirst("Should not be removed"); + temp.saveDetail(&nameDetail); + cm->saveContact(&temp); + crr.setFilter(dfil); + QVERIFY(!crr.cancel()); // not started + QVERIFY(crr.start()); + QVERIFY(crr.isActive()); + QVERIFY(crr.status() == QContactAbstractRequest::Active); + QVERIFY(!crr.isFinished()); + QVERIFY(crr.cancel()); + QVERIFY(crr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(crr.isActive()); // still cancelling + QVERIFY(!crr.isFinished()); // not finished cancelling + QVERIFY(!crr.start()); // already started. + QVERIFY(crr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(crr.isFinished()); + QVERIFY(!crr.isActive()); + QVERIFY(crr.status() == QContactAbstractRequest::Cancelled); + + QCOMPARE(cm->contacts().size(), 1); + QCOMPARE(cm->contact(cm->contacts().first()), temp); + + // restart, and wait for progress after cancel. + QVERIFY(!crr.cancel()); // not started + QVERIFY(crr.start()); + QVERIFY(crr.isActive()); + QVERIFY(crr.status() == QContactAbstractRequest::Active); + QVERIFY(!crr.isFinished()); + QVERIFY(crr.cancel()); + QVERIFY(crr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(crr.isActive()); // still cancelling + QVERIFY(!crr.isFinished()); // not finished cancelling + QVERIFY(!crr.start()); // already started. + QVERIFY(crr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(crr.isFinished()); + QVERIFY(!crr.isActive()); + QVERIFY(crr.status() == QContactAbstractRequest::Cancelled); + + QCOMPARE(cm->contacts().size(), 1); + QCOMPARE(cm->contact(cm->contacts().first()), temp); + cm->removeContact(temp.localId()); // clean up + + delete cm; +} + +void tst_QContactAsync::contactSave() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + QContactSaveRequest csr; + QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); + + // initial state - not started, no manager. + QVERIFY(!csr.isActive()); + QVERIFY(!csr.isFinished()); + QVERIFY(!csr.start()); + QVERIFY(!csr.cancel()); + QVERIFY(!csr.waitForFinished()); + QVERIFY(!csr.waitForProgress()); + + // save a new contact + int originalCount = cm->contacts().size(); + QContact testContact; + QContactName nameDetail; + nameDetail.setFirst("Test Contact"); + testContact.saveDetail(&nameDetail); + QList saveList; + saveList << testContact; + csr.setManager(cm); + QCOMPARE(csr.manager(), cm); + QVERIFY(!csr.isActive()); + QVERIFY(!csr.isFinished()); + QVERIFY(!csr.cancel()); + QVERIFY(!csr.waitForFinished()); + QVERIFY(!csr.waitForProgress()); + qRegisterMetaType("QContactSaveRequest*"); + QSignalSpy spy(&csr, SIGNAL(progress(QContactSaveRequest*))); + csr.setContacts(saveList); + QCOMPARE(csr.contacts(), saveList); + QVERIFY(!csr.cancel()); // not started + QVERIFY(csr.start()); + QVERIFY(csr.isActive()); + QVERIFY(csr.status() == QContactAbstractRequest::Active); + QVERIFY(!csr.isFinished()); + QVERIFY(!csr.start()); // already started. + QVERIFY(csr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(csr.isFinished()); + QVERIFY(!csr.isActive()); + + QList expected; + expected << cm->contact(cm->contacts().last()); + QList result = csr.contacts(); + QCOMPARE(expected, result); + QCOMPARE(cm->contacts().size(), originalCount + 1); + + // update a previously saved contact + QContactPhoneNumber phn; + phn.setNumber("12345678"); + testContact = expected.first(); + testContact.saveDetail(&phn); + saveList.clear(); + saveList << testContact; + csr.setContacts(saveList); + QCOMPARE(csr.contacts(), saveList); + QVERIFY(!csr.cancel()); // not started + QVERIFY(csr.start()); + QVERIFY(csr.isActive()); + QVERIFY(csr.status() == QContactAbstractRequest::Active); + QVERIFY(!csr.isFinished()); + QVERIFY(!csr.start()); // already started. + QVERIFY(csr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(csr.isFinished()); + QVERIFY(!csr.isActive()); + + expected.clear(); + expected << cm->contact(cm->contacts().last()); + result = csr.contacts(); + QCOMPARE(expected, result); + + //here we can't compare the whole contact details, testContact would be updated by async call because we just use QSignalSpy to receive signals. + //QVERIFY(containsIgnoringTimestamps(expected, testContact)); + QVERIFY(expected.at(0).detail().number() == phn.number()); + + QCOMPARE(cm->contacts().size(), originalCount + 1); + + // cancelling + QContact temp = testContact; + QContactUrl url; + url.setUrl("should not get saved"); + temp.saveDetail(&url); + saveList.clear(); + saveList << temp; + csr.setContacts(saveList); + QVERIFY(!csr.cancel()); // not started + QVERIFY(csr.start()); + QVERIFY(csr.isActive()); + QVERIFY(csr.status() == QContactAbstractRequest::Active); + QVERIFY(!csr.isFinished()); + QVERIFY(csr.cancel()); + QVERIFY(csr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(csr.isActive()); // still cancelling + QVERIFY(!csr.isFinished()); // not finished cancelling + QVERIFY(!csr.start()); // already started. + QVERIFY(csr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(csr.isFinished()); + QVERIFY(!csr.isActive()); + QVERIFY(csr.status() == QContactAbstractRequest::Cancelled); + + // verify that the changes were not saved + expected.clear(); + QList allContacts = cm->contacts(); + for (int i = 0; i < allContacts.size(); i++) { + expected.append(cm->contact(allContacts.at(i))); + } + QVERIFY(!expected.contains(temp)); + QCOMPARE(cm->contacts().size(), originalCount + 1); + + // restart, and wait for progress after cancel. + QVERIFY(!csr.cancel()); // not started + QVERIFY(csr.start()); + QVERIFY(csr.isActive()); + QVERIFY(csr.status() == QContactAbstractRequest::Active); + QVERIFY(!csr.isFinished()); + QVERIFY(csr.cancel()); + QVERIFY(csr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(csr.isActive()); // still cancelling + QVERIFY(!csr.isFinished()); // not finished cancelling + QVERIFY(!csr.start()); // already started. + QVERIFY(csr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(csr.isFinished()); + QVERIFY(!csr.isActive()); + QVERIFY(csr.status() == QContactAbstractRequest::Cancelled); + + // verify that the changes were not saved + expected.clear(); + allContacts = cm->contacts(); + for (int i = 0; i < allContacts.size(); i++) { + expected.append(cm->contact(allContacts.at(i))); + } + QVERIFY(!expected.contains(temp)); + QCOMPARE(cm->contacts().size(), originalCount + 1); + + delete cm; +} + +void tst_QContactAsync::definitionFetch() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + QContactDetailDefinitionFetchRequest dfr; + QVERIFY(dfr.type() == QContactAbstractRequest::DetailDefinitionFetchRequest); + dfr.setContactType(QContactType::TypeContact); + QVERIFY(dfr.contactType() == QString(QLatin1String(QContactType::TypeContact))); + + // initial state - not started, no manager. + QVERIFY(!dfr.isActive()); + QVERIFY(!dfr.isFinished()); + QVERIFY(!dfr.start()); + QVERIFY(!dfr.cancel()); + QVERIFY(!dfr.waitForFinished()); + QVERIFY(!dfr.waitForProgress()); + + // "all definitions" retrieval + dfr.setManager(cm); + QCOMPARE(dfr.manager(), cm); + QVERIFY(!dfr.isActive()); + QVERIFY(!dfr.isFinished()); + QVERIFY(!dfr.cancel()); + QVERIFY(!dfr.waitForFinished()); + QVERIFY(!dfr.waitForProgress()); + qRegisterMetaType("QContactDetailDefinitionFetchRequest*"); + QSignalSpy spy(&dfr, SIGNAL(progress(QContactDetailDefinitionFetchRequest*, bool))); + dfr.setNames(QStringList()); + QVERIFY(!dfr.cancel()); // not started + QVERIFY(dfr.start()); + QVERIFY(dfr.isActive()); + QVERIFY(dfr.status() == QContactAbstractRequest::Active); + QVERIFY(!dfr.isFinished()); + QVERIFY(!dfr.start()); // already started. + QVERIFY(dfr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(dfr.isFinished()); + QVERIFY(!dfr.isActive()); + + QMap defs = cm->detailDefinitions(); + QMap result = dfr.definitions(); + QCOMPARE(defs, result); + + // specific definition retrieval + QStringList specific; + specific << QContactUrl::DefinitionName; + dfr.setNames(specific); + QVERIFY(!dfr.cancel()); // not started + QVERIFY(dfr.start()); + QVERIFY(dfr.isActive()); + QVERIFY(dfr.status() == QContactAbstractRequest::Active); + QVERIFY(!dfr.isFinished()); + QVERIFY(!dfr.start()); // already started. + QVERIFY(dfr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(dfr.isFinished()); + QVERIFY(!dfr.isActive()); + + defs.clear(); + defs.insert(QContactUrl::DefinitionName, cm->detailDefinition(QContactUrl::DefinitionName)); + result = dfr.definitions(); + QCOMPARE(defs, result); + + // cancelling + dfr.setNames(QStringList()); + QVERIFY(!dfr.cancel()); // not started + QVERIFY(dfr.start()); + QVERIFY(dfr.isActive()); + QVERIFY(dfr.status() == QContactAbstractRequest::Active); + QVERIFY(!dfr.isFinished()); + QVERIFY(dfr.cancel()); + QVERIFY(dfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(dfr.isActive()); // still cancelling + QVERIFY(!dfr.isFinished()); // not finished cancelling + QVERIFY(!dfr.start()); // already started. + QVERIFY(dfr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(dfr.isFinished()); + QVERIFY(!dfr.isActive()); + QVERIFY(dfr.status() == QContactAbstractRequest::Cancelled); + + // restart, and wait for progress after cancel. + QVERIFY(!dfr.cancel()); // not started + QVERIFY(dfr.start()); + QVERIFY(dfr.isActive()); + QVERIFY(dfr.status() == QContactAbstractRequest::Active); + QVERIFY(!dfr.isFinished()); + QVERIFY(dfr.cancel()); + QVERIFY(dfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(dfr.isActive()); // still cancelling + QVERIFY(!dfr.isFinished()); // not finished cancelling + QVERIFY(!dfr.start()); // already started. + QVERIFY(dfr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(dfr.isFinished()); + QVERIFY(!dfr.isActive()); + QVERIFY(dfr.status() == QContactAbstractRequest::Cancelled); + + delete cm; +} + +void tst_QContactAsync::definitionRemove() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + if (!cm->hasFeature(QContactManager::MutableDefinitions, QContactType::TypeContact)) { + QSKIP("This contact manager doest not support mutable definitions, can't remove a definition!", SkipSingle); + } + QContactDetailDefinitionRemoveRequest drr; + QVERIFY(drr.type() == QContactAbstractRequest::DetailDefinitionRemoveRequest); + drr.setContactType(QContactType::TypeContact); + QVERIFY(drr.contactType() == QString(QLatin1String(QContactType::TypeContact))); + + // initial state - not started, no manager. + QVERIFY(!drr.isActive()); + QVERIFY(!drr.isFinished()); + QVERIFY(!drr.start()); + QVERIFY(!drr.cancel()); + QVERIFY(!drr.waitForFinished()); + QVERIFY(!drr.waitForProgress()); + + // specific group removal + int originalCount = cm->detailDefinitions().keys().size(); + QStringList removeIds; + removeIds << cm->detailDefinitions().keys().first(); + drr.setNames(removeIds); + drr.setManager(cm); + QCOMPARE(drr.manager(), cm); + QVERIFY(!drr.isActive()); + QVERIFY(!drr.isFinished()); + QVERIFY(!drr.cancel()); + QVERIFY(!drr.waitForFinished()); + QVERIFY(!drr.waitForProgress()); + qRegisterMetaType("QContactDetailDefinitionRemoveRequest*"); + QSignalSpy spy(&drr, SIGNAL(progress(QContactDetailDefinitionRemoveRequest*))); + QVERIFY(drr.names() == removeIds); + QVERIFY(!drr.cancel()); // not started + QVERIFY(drr.start()); + QVERIFY(drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Active); + QVERIFY(!drr.isFinished()); + QVERIFY(!drr.start()); // already started. + QVERIFY(drr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(drr.isFinished()); + QVERIFY(!drr.isActive()); + + QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 1); + cm->detailDefinition(removeIds.first()); // check that it has already been removed. + QCOMPARE(cm->error(), QContactManager::DoesNotExistError); + + // remove (asynchronously) a nonexistent group - should fail. + drr.setNames(removeIds); + QVERIFY(!drr.cancel()); // not started + QVERIFY(drr.start()); + QVERIFY(drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Active); + QVERIFY(!drr.isFinished()); + QVERIFY(!drr.start()); // already started. + QVERIFY(drr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(drr.isFinished()); + QVERIFY(!drr.isActive()); + + QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 1); // hasn't changed + QCOMPARE(drr.error(), QContactManager::DoesNotExistError); + + // remove with list containing one valid and one invalid id. + removeIds << cm->detailDefinitions().keys().first(); + drr.setNames(removeIds); + QVERIFY(!drr.cancel()); // not started + QVERIFY(drr.start()); + QVERIFY(drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Active); + QVERIFY(!drr.isFinished()); + QVERIFY(!drr.start()); // already started. + QVERIFY(drr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(drr.isFinished()); + QVERIFY(!drr.isActive()); + + QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // only one more has been removed + QCOMPARE(drr.errors().first(), QContactManager::DoesNotExistError); + QCOMPARE(drr.errors().at(1), QContactManager::NoError); + + // remove with empty list - nothing should happen. + removeIds.clear(); + drr.setNames(removeIds); + QVERIFY(!drr.cancel()); // not started + QVERIFY(drr.start()); + QVERIFY(drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Active); + QVERIFY(!drr.isFinished()); + QVERIFY(!drr.start()); // already started. + QVERIFY(drr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(drr.isFinished()); + QVERIFY(!drr.isActive()); + + QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed + QCOMPARE(drr.error(), QContactManager::NoError); // no error but no effect. + + // cancelling + removeIds.clear(); + removeIds << cm->detailDefinitions().keys().first(); + drr.setNames(removeIds); + QVERIFY(!drr.cancel()); // not started + QVERIFY(drr.start()); + QVERIFY(drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Active); + QVERIFY(!drr.isFinished()); + QVERIFY(drr.cancel()); + QVERIFY(drr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(drr.isActive()); // still cancelling + QVERIFY(!drr.isFinished()); // not finished cancelling + QVERIFY(!drr.start()); // already started. + QVERIFY(drr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(drr.isFinished()); + QVERIFY(!drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Cancelled); + + QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed + + // restart, and wait for progress after cancel. + QVERIFY(!drr.cancel()); // not started + QVERIFY(drr.start()); + QVERIFY(drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Active); + QVERIFY(!drr.isFinished()); + QVERIFY(drr.cancel()); + QVERIFY(drr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(drr.isActive()); // still cancelling + QVERIFY(!drr.isFinished()); // not finished cancelling + QVERIFY(!drr.start()); // already started. + QVERIFY(drr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(drr.isFinished()); + QVERIFY(!drr.isActive()); + QVERIFY(drr.status() == QContactAbstractRequest::Cancelled); + + QCOMPARE(cm->detailDefinitions().keys().size(), originalCount - 2); // hasn't changed + + delete cm; +} + +void tst_QContactAsync::definitionSave() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QVERIFY(prepareModel(uri, cm)); + + if (!cm->hasFeature(QContactManager::MutableDefinitions, QContactType::TypeContact)) { + QSKIP("This contact manager doest not support mutable definitions, can't save a definition!", SkipSingle); + } + + QContactDetailDefinitionSaveRequest dsr; + QVERIFY(dsr.type() == QContactAbstractRequest::DetailDefinitionSaveRequest); + dsr.setContactType(QContactType::TypeContact); + QVERIFY(dsr.contactType() == QString(QLatin1String(QContactType::TypeContact))); + + // initial state - not started, no manager. + QVERIFY(!dsr.isActive()); + QVERIFY(!dsr.isFinished()); + QVERIFY(!dsr.start()); + QVERIFY(!dsr.cancel()); + QVERIFY(!dsr.waitForFinished()); + QVERIFY(!dsr.waitForProgress()); + + // save a new detail definition + int originalCount = cm->detailDefinitions().keys().size(); + QContactDetailDefinition testDef; + testDef.setName("TestDefinitionId"); + QMap fields; + QContactDetailDefinitionField f; + f.setDataType(QVariant::String); + fields.insert("TestDefinitionField", f); + testDef.setFields(fields); + QList saveList; + saveList << testDef; + dsr.setManager(cm); + QCOMPARE(dsr.manager(), cm); + QVERIFY(!dsr.isActive()); + QVERIFY(!dsr.isFinished()); + QVERIFY(!dsr.cancel()); + QVERIFY(!dsr.waitForFinished()); + QVERIFY(!dsr.waitForProgress()); + qRegisterMetaType("QContactDetailDefinitionSaveRequest*"); + QSignalSpy spy(&dsr, SIGNAL(progress(QContactDetailDefinitionSaveRequest*))); + dsr.setDefinitions(saveList); + QCOMPARE(dsr.definitions(), saveList); + QVERIFY(!dsr.cancel()); // not started + QVERIFY(dsr.start()); + QVERIFY(dsr.isActive()); + QVERIFY(dsr.status() == QContactAbstractRequest::Active); + QVERIFY(!dsr.isFinished()); + QVERIFY(!dsr.start()); // already started. + QVERIFY(dsr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(dsr.isFinished()); + QVERIFY(!dsr.isActive()); + + QList expected; + expected << cm->detailDefinition("TestDefinitionId"); + QList result = dsr.definitions(); + QCOMPARE(expected, result); + QVERIFY(expected.contains(testDef)); + QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1); + + // update a previously saved group + fields.insert("TestDefinitionFieldTwo", f); + testDef.setFields(fields); + saveList.clear(); + saveList << testDef; + dsr.setDefinitions(saveList); + QCOMPARE(dsr.definitions(), saveList); + QVERIFY(!dsr.cancel()); // not started + QVERIFY(dsr.start()); + QVERIFY(dsr.isActive()); + QVERIFY(dsr.status() == QContactAbstractRequest::Active); + QVERIFY(!dsr.isFinished()); + QVERIFY(!dsr.start()); // already started. + QVERIFY(dsr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(dsr.isFinished()); + QVERIFY(!dsr.isActive()); + + expected.clear(); + expected << cm->detailDefinition("TestDefinitionId"); + result = dsr.definitions(); + QCOMPARE(expected, result); + QVERIFY(expected.contains(testDef)); + QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1); + + // cancelling + fields.insert("TestDefinitionFieldThree - shouldn't get saved", f); + testDef.setFields(fields); + saveList.clear(); + saveList << testDef; + dsr.setDefinitions(saveList); + QCOMPARE(dsr.definitions(), saveList); + QVERIFY(!dsr.cancel()); // not started + QVERIFY(dsr.start()); + QVERIFY(dsr.isActive()); + QVERIFY(dsr.status() == QContactAbstractRequest::Active); + QVERIFY(!dsr.isFinished()); + QVERIFY(dsr.cancel()); + QVERIFY(dsr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(dsr.isActive()); // still cancelling + QVERIFY(!dsr.isFinished()); // not finished cancelling + QVERIFY(!dsr.start()); // already started. + QVERIFY(dsr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(dsr.isFinished()); + QVERIFY(!dsr.isActive()); + QVERIFY(dsr.status() == QContactAbstractRequest::Cancelled); + + // verify that the changes were not saved + QList allDefs = cm->detailDefinitions().values(); + QVERIFY(!allDefs.contains(testDef)); + QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1); + + // restart, and wait for progress after cancel. + QVERIFY(!dsr.cancel()); // not started + QVERIFY(dsr.start()); + QVERIFY(dsr.isActive()); + QVERIFY(dsr.status() == QContactAbstractRequest::Active); + QVERIFY(!dsr.isFinished()); + QVERIFY(dsr.cancel()); + QVERIFY(dsr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(dsr.isActive()); // still cancelling + QVERIFY(!dsr.isFinished()); // not finished cancelling + QVERIFY(!dsr.start()); // already started. + QVERIFY(dsr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(dsr.isFinished()); + QVERIFY(!dsr.isActive()); + QVERIFY(dsr.status() == QContactAbstractRequest::Cancelled); + + // verify that the changes were not saved + allDefs = cm->detailDefinitions().values(); + QVERIFY(!allDefs.contains(testDef)); + QCOMPARE(cm->detailDefinitions().values().size(), originalCount + 1); + + delete cm; +} + +void tst_QContactAsync::relationshipFetch() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QList contacts; + QList relationships; + QVERIFY(prepareModel(uri, cm, contacts, relationships)); + QContactRelationshipFetchRequest rfr; + QVERIFY(rfr.type() == QContactAbstractRequest::RelationshipFetchRequest); + + // initial state - not started, no manager. + QVERIFY(!rfr.isActive()); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); + QVERIFY(!rfr.cancel()); + QVERIFY(!rfr.waitForFinished()); + QVERIFY(!rfr.waitForProgress()); + + if (!cm->hasFeature(QContactManager::Relationships)) + { + // ensure manager returs errors + rfr.setManager(cm); + QCOMPARE(rfr.manager(), cm); + QVERIFY(!rfr.isActive()); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.cancel()); + QVERIFY(!rfr.waitForFinished()); + QVERIFY(!rfr.waitForProgress()); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NotSupportedError); + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + return; + } + + // use variables to make code more readable + QContactId aId = contacts.at(0).id(); + QContactId bId = contacts.at(1).id(); + QContactId cId = contacts.at(2).id(); + QContactId dId = contacts.at(3).id(); + QContactId eId = contacts.at(4).id(); + QContactId fId = contacts.at(5).id(); + QContactRelationship adRel = relationships.at(0); + QContactRelationship aeRel = relationships.at(1); + QContactRelationship beRel = relationships.at(2); + QContactRelationship ceRel = relationships.at(3); + QContactRelationship cfRel = relationships.at(4); + QString relType = adRel.relationshipType(); + + // "all relationships" retrieval + rfr.setManager(cm); + QCOMPARE(rfr.manager(), cm); + QVERIFY(!rfr.isActive()); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.cancel()); + QVERIFY(!rfr.waitForFinished()); + QVERIFY(!rfr.waitForProgress()); + qRegisterMetaType("QContactRelationshipFetchRequest*"); + QSignalSpy spy(&rfr, SIGNAL(progress(QContactRelationshipFetchRequest*, bool))); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + QList rels = cm->relationships(); + QList result = rfr.relationships(); + QCOMPARE(rels, result); + + // specific relationship type retrieval + rfr.setRelationshipType(relType); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + rels = cm->relationships(relType); + result = rfr.relationships(); + QCOMPARE(rels, result); + + // specific source contact retrieval + rfr.setFirst(aId); + rfr.setRelationshipType(QString()); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + rels = cm->relationships(aId, QContactRelationshipFilter::First); + result = rfr.relationships(); + QCOMPARE(rels, result); + + // specific participant retrieval #1 - destination participant + rfr.setFirst(QContactId()); + rfr.setParticipant(eId, QContactRelationshipFilter::Second); + QVERIFY(rfr.participantRole() == QContactRelationshipFilter::Second); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + rels = cm->relationships(eId, QContactRelationshipFilter::Second); + result = rfr.relationships(); + QCOMPARE(rels, result); + + // specific participant retrieval #2 - source participant + rfr.setFirst(QContactId()); + rfr.setParticipant(cId, QContactRelationshipFilter::First); + QVERIFY(rfr.participantRole() == QContactRelationshipFilter::First); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + rels = cm->relationships(cId, QContactRelationshipFilter::First); + result = rfr.relationships(); + QCOMPARE(rels, result); + + if (relationships.count() > 4) + { + // specific participant retrieval #3 - either participant + rfr.setFirst(QContactId()); + rfr.setParticipant(aId, QContactRelationshipFilter::Either); + QVERIFY(rfr.participantRole() == QContactRelationshipFilter::Either); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + rels = cm->relationships(aId); // either role. + result = rfr.relationships(); + QCOMPARE(rels, result); + } + + // cancelling + rfr.setRelationshipType(QString()); + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(rfr.cancel()); + QVERIFY(rfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(rfr.isActive()); // still cancelling + QVERIFY(!rfr.isFinished()); // not finished cancelling + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForFinished()); + QVERIFY(rfr.error() == QContactManager::NoError); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Cancelled); + + // restart, and wait for progress after cancel. + QVERIFY(!rfr.cancel()); // not started + QVERIFY(rfr.start()); + QVERIFY(rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Active); + QVERIFY(!rfr.isFinished()); + QVERIFY(rfr.cancel()); + QVERIFY(rfr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(rfr.isActive()); // still cancelling + QVERIFY(!rfr.isFinished()); // not finished cancelling + QVERIFY(!rfr.start()); // already started. + QVERIFY(rfr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(rfr.isFinished()); + QVERIFY(!rfr.isActive()); + QVERIFY(rfr.status() == QContactAbstractRequest::Cancelled); + + delete cm; +} + +void tst_QContactAsync::relationshipRemove() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QList contacts; + QList relationships; + QVERIFY(prepareModel(uri, cm, contacts, relationships)); + QContactRelationshipRemoveRequest rrr; + QVERIFY(rrr.type() == QContactAbstractRequest::RelationshipRemoveRequest); + + if (!cm->hasFeature(QContactManager::Relationships)) + { + // ensure manager returns error + rrr.setFirst(contacts.at(0).id()); + rrr.setManager(cm); + QCOMPARE(rrr.manager(), cm); + QVERIFY(!rrr.isActive()); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.cancel()); + QVERIFY(!rrr.waitForFinished()); + QVERIFY(!rrr.waitForProgress()); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QVERIFY(rrr.error() == QContactManager::NotSupportedError); + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + return; + } + + // use variables to make code more readable + QContactId aId = contacts.at(0).id(); + QContactId bId = contacts.at(1).id(); + QContactId cId = contacts.at(2).id(); + QContactId dId = contacts.at(3).id(); + QContactId eId = contacts.at(4).id(); + QContactId fId = contacts.at(5).id(); + QContactRelationship adRel = relationships.at(0); + QContactRelationship aeRel = relationships.at(1); + QContactRelationship beRel = relationships.at(2); + QContactRelationship ceRel = relationships.at(3); + QContactRelationship cfRel = relationships.at(4); + QString relType = adRel.relationshipType(); + + // initial state - not started, no manager. + QVERIFY(!rrr.isActive()); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); + QVERIFY(!rrr.cancel()); + QVERIFY(!rrr.waitForFinished()); + QVERIFY(!rrr.waitForProgress()); + + // specific source, destination and type removal + int relationshipCount = cm->relationships().count(); + rrr.setFirst(adRel.first()); + rrr.setSecond(adRel.second()); + rrr.setRelationshipType(adRel.relationshipType()); + rrr.setManager(cm); + qRegisterMetaType("QContactRelationshipRemoveRequest*"); + QSignalSpy spy(&rrr, SIGNAL(progress(QContactRelationshipRemoveRequest*))); + QCOMPARE(rrr.manager(), cm); + QVERIFY(!rrr.isActive()); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.cancel()); + QVERIFY(!rrr.waitForFinished()); + QVERIFY(!rrr.waitForProgress()); + QVERIFY(rrr.relationshipType() == adRel.relationshipType()); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QVERIFY(rrr.error() == QContactManager::NoError); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + QCOMPARE(cm->relationships().count(), relationshipCount-1); + + // remove (asynchronously) a nonexistent relationship - should fail. + relationshipCount = cm->relationships().count(); + rrr.setFirst(bId); + rrr.setSecond(aId); + rrr.setRelationshipType(relType); + rrr.setManager(cm); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QCOMPARE(rrr.error(), QContactManager::DoesNotExistError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + QCOMPARE(cm->relationships().count(), relationshipCount); + + // specific relationship type plus source removal + rrr.setFirst(cId); + rrr.setSecond(QContactId()); + rrr.setRelationshipType(relType); + rrr.setManager(cm); + QCOMPARE(rrr.manager(), cm); + QVERIFY(!rrr.isActive()); + QVERIFY(!rrr.cancel()); + QVERIFY(!rrr.waitForFinished()); + QVERIFY(!rrr.waitForProgress()); + QVERIFY(rrr.relationshipType() == relType); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QVERIFY(rrr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + QCOMPARE(cm->relationships(relType, cId, QContactRelationshipFilter::First).size(), 0); + QCOMPARE(cm->error(), QContactManager::DoesNotExistError); + + // specific source removal + rrr.setFirst(aId); + rrr.setSecond(QContactId()); + rrr.setRelationshipType(QString()); + rrr.setManager(cm); + QCOMPARE(rrr.manager(), cm); + QVERIFY(!rrr.isActive()); + QVERIFY(!rrr.cancel()); + QVERIFY(!rrr.waitForFinished()); + QVERIFY(!rrr.waitForProgress()); + QVERIFY(rrr.relationshipType() == QString()); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QVERIFY(rrr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + QCOMPARE(cm->relationships(aId, QContactRelationshipFilter::First).size(), 0); + QCOMPARE(cm->error(), QContactManager::DoesNotExistError); + + // cancelling + rrr.setFirst(bId); + rrr.setSecond(QContactId()); + rrr.setRelationshipType(QString()); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(rrr.cancel()); + QVERIFY(rrr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(rrr.isActive()); // still cancelling + QVERIFY(!rrr.isFinished()); // not finished cancelling + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QVERIFY(rrr.error() == QContactManager::NoError); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Cancelled); + QVERIFY(cm->relationships(bId).size() != 0); // didn't remove them. + + // restart, and wait for progress after cancel. + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(rrr.cancel()); + QVERIFY(rrr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(rrr.isActive()); // still cancelling + QVERIFY(!rrr.isFinished()); // not finished cancelling + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Cancelled); + QVERIFY(cm->relationships(bId).size() != 0); // didn't remove them. + + // specific relationship type removal + rrr.setFirst(QContactId()); + rrr.setSecond(QContactId()); + rrr.setRelationshipType(relType); + rrr.setManager(cm); + QCOMPARE(rrr.manager(), cm); + QVERIFY(!rrr.isActive()); + QVERIFY(!rrr.cancel()); + QVERIFY(!rrr.waitForFinished()); + QVERIFY(!rrr.waitForProgress()); + QVERIFY(rrr.relationshipType() == relType); + QVERIFY(!rrr.cancel()); // not started + QVERIFY(rrr.start()); + QVERIFY(rrr.isActive()); + QVERIFY(rrr.status() == QContactAbstractRequest::Active); + QVERIFY(!rrr.isFinished()); + QVERIFY(!rrr.start()); // already started. + QVERIFY(rrr.waitForFinished()); + QVERIFY(rrr.error() == QContactManager::NoError); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rrr.isFinished()); + QVERIFY(!rrr.isActive()); + + QCOMPARE(cm->relationships(relType).size(), 0); + cm->relationships(relType); // check that it has already been removed. + QCOMPARE(cm->error(), QContactManager::DoesNotExistError); + + delete cm; +} + +void tst_QContactAsync::relationshipSave() +{ + QFETCH(QString, uri); + QContactManager* cm(0); + QList contacts; + QList relationships; + QVERIFY(prepareModel(uri, cm, contacts, relationships)); + QContactRelationshipSaveRequest rsr; + QVERIFY(rsr.type() == QContactAbstractRequest::RelationshipSaveRequest); + + if (!cm->hasFeature(QContactManager::Relationships)) + { + // ensure saving returns errors + QContactRelationship rel; + rel.setFirst(contacts.at(0).id()); + rel.setSecond(contacts.at(1).id()); + rel.setRelationshipType(QContactRelationship::HasManager); + QList list; + list << rel; + rsr.setManager(cm); + QCOMPARE(rsr.manager(), cm); + QVERIFY(!rsr.isActive()); + QVERIFY(!rsr.isFinished()); + QVERIFY(!rsr.cancel()); + QVERIFY(!rsr.waitForFinished()); + QVERIFY(!rsr.waitForProgress()); + rsr.setRelationships(list); + QCOMPARE(rsr.relationships(), list); + QVERIFY(!rsr.cancel()); // not started + QVERIFY(rsr.start()); + QVERIFY(rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Active); + QVERIFY(!rsr.isFinished()); + QVERIFY(!rsr.start()); // already started. + QVERIFY(rsr.waitForFinished()); + QVERIFY(rsr.error() == QContactManager::NotSupportedError); + QVERIFY(rsr.isFinished()); + QVERIFY(!rsr.isActive()); + return; + } + + // use variables to make code more readable + QContactId aId = contacts.at(0).id(); + QContactId bId = contacts.at(1).id(); + QContactId cId = contacts.at(2).id(); + QContactId dId = contacts.at(3).id(); + QContactId eId = contacts.at(4).id(); + QContactId fId = contacts.at(5).id(); + QContactRelationship adRel = relationships.at(0); + QContactRelationship aeRel = relationships.at(1); + QContactRelationship beRel = relationships.at(2); + QContactRelationship ceRel = relationships.at(3); + QContactRelationship cfRel = relationships.at(4); + QString relType = adRel.relationshipType(); + + // initial state - not started, no manager. + QVERIFY(!rsr.isActive()); + QVERIFY(!rsr.isFinished()); + QVERIFY(!rsr.start()); + QVERIFY(!rsr.cancel()); + QVERIFY(!rsr.waitForFinished()); + QVERIFY(!rsr.waitForProgress()); + + // save a new relationship + int originalCount = cm->relationships(bId).size(); + QContactRelationship testRel; + testRel.setFirst(bId); + testRel.setSecond(dId); + testRel.setRelationshipType(relType); + QList saveList; + saveList << testRel; + rsr.setManager(cm); + QCOMPARE(rsr.manager(), cm); + QVERIFY(!rsr.isActive()); + QVERIFY(!rsr.isFinished()); + QVERIFY(!rsr.cancel()); + QVERIFY(!rsr.waitForFinished()); + QVERIFY(!rsr.waitForProgress()); + qRegisterMetaType("QContactRelationshipSaveRequest*"); + QSignalSpy spy(&rsr, SIGNAL(progress(QContactRelationshipSaveRequest*))); + rsr.setRelationships(saveList); + QCOMPARE(rsr.relationships(), saveList); + QVERIFY(!rsr.cancel()); // not started + QVERIFY(rsr.start()); + QVERIFY(rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Active); + QVERIFY(!rsr.isFinished()); + QVERIFY(!rsr.start()); // already started. + QVERIFY(rsr.waitForFinished()); + int expectedCount = 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rsr.isFinished()); + QVERIFY(!rsr.isActive()); + QList result = rsr.relationships(); + QVERIFY(result.contains(testRel)); + QList bRelationships = cm->relationships(relType, bId, QContactRelationshipFilter::First); + QVERIFY(bRelationships.contains(testRel)); + QCOMPARE(cm->relationships(bId).size(), originalCount + 1); // should be one extra + + // save a new relationship + testRel.setSecond(fId); + saveList.clear(); + saveList << testRel; + rsr.setRelationships(saveList); + QCOMPARE(rsr.relationships(), saveList); + QVERIFY(!rsr.cancel()); // not started + QVERIFY(rsr.start()); + QVERIFY(rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Active); + QVERIFY(!rsr.isFinished()); + QVERIFY(!rsr.start()); // already started. + QVERIFY(rsr.waitForFinished()); + expectedCount += 2; + QCOMPARE(spy.count(), expectedCount); // active + finished progress signals. + QVERIFY(rsr.isFinished()); + QVERIFY(!rsr.isActive()); + bRelationships.clear(); + bRelationships = cm->relationships(relType, bId, QContactRelationshipFilter::First); + result = rsr.relationships(); + QCOMPARE(result, QList() << testRel); + QVERIFY(bRelationships.contains(testRel)); + QCOMPARE(cm->relationships(bId).size(), originalCount + 2); // should now be two extra + + // cancelling + testRel.setSecond(bId); // shouldn't get saved (circular anyway) + saveList.clear(); + saveList << testRel; + rsr.setRelationships(saveList); + QCOMPARE(rsr.relationships(), saveList); + QVERIFY(!rsr.cancel()); // not started + QVERIFY(rsr.start()); + QVERIFY(rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Active); + QVERIFY(!rsr.isFinished()); + QVERIFY(rsr.cancel()); + QVERIFY(rsr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(rsr.isActive()); // still cancelling + QVERIFY(!rsr.isFinished()); // not finished cancelling + QVERIFY(!rsr.start()); // already started. + QVERIFY(rsr.waitForFinished()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(rsr.isFinished()); + QVERIFY(!rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Cancelled); + + // verify that the changes were not saved + QList aRels = cm->relationships(bId, QContactRelationshipFilter::First); + QVERIFY(!aRels.contains(testRel)); + QCOMPARE(cm->relationships(bId).size(), originalCount + 2); // should still only be two extra + + // restart, and wait for progress after cancel. + QVERIFY(!rsr.cancel()); // not started + QVERIFY(rsr.start()); + QVERIFY(rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Active); + QVERIFY(!rsr.isFinished()); + QVERIFY(rsr.cancel()); + QVERIFY(rsr.status() == QContactAbstractRequest::Cancelling); + QVERIFY(rsr.isActive()); // still cancelling + QVERIFY(!rsr.isFinished()); // not finished cancelling + QVERIFY(!rsr.start()); // already started. + QVERIFY(rsr.waitForProgress()); + expectedCount += 3; + QCOMPARE(spy.count(), expectedCount); // active + cancelling + cancelled progress signals. + QVERIFY(rsr.isFinished()); + QVERIFY(!rsr.isActive()); + QVERIFY(rsr.status() == QContactAbstractRequest::Cancelled); + + // verify that the changes were not saved + aRels = cm->relationships(bId); + QVERIFY(!aRels.contains(testRel)); + QCOMPARE(cm->relationships(bId).size(), originalCount + 2); // should still only be two extra + + delete cm; +} + +void tst_QContactAsync::maliciousManager() +{ + // use the invalid manager: passes all requests through to base class + QContactManager cm("invalid"); + QContactFilter fil; // matches everything + QContactFetchRequest cfr; + cfr.setFilter(fil); + cfr.setManager(&cm); + QVERIFY(!cfr.start()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + QVERIFY(!cfr.start()); + QVERIFY(!cfr.waitForProgress()); + + // ensure that the base class implementation of requestDestroyed() is called + QContactFetchRequest *destroyedRequest = new QContactFetchRequest; + destroyedRequest->setManager(&cm); + destroyedRequest->setFilter(fil); + QVERIFY(!destroyedRequest->start()); + delete destroyedRequest; + + // now use a malicious manager that deliberately calls + // incorrect "updateRequest" functions in base class: + QContactManager mcm("maliciousplugin"); + QCOMPARE(mcm.managerName(), QString("maliciousplugin")); + QList emptyCList; + QList emptyIList; + QList emptyDList; + QStringList emptyDNList; + QMap emptyDMap; + cfr.setFilter(fil); + cfr.setManager(&mcm); + QVERIFY(cfr.start()); + QVERIFY(cfr.cancel()); + QVERIFY(!cfr.waitForProgress(100)); + QVERIFY(!cfr.waitForFinished(100)); + QVERIFY(cfr.start()); + QVERIFY(!cfr.waitForProgress(100)); + QVERIFY(!cfr.waitForFinished(100)); + QVERIFY(cfr.cancel()); + + QContactLocalIdFetchRequest cifr; + cifr.setFilter(fil); + cifr.setManager(&mcm); + QVERIFY(cifr.start()); + QVERIFY(cifr.cancel()); + QVERIFY(!cifr.waitForProgress(100)); + QVERIFY(!cifr.waitForFinished(100)); + QVERIFY(cifr.start()); + QVERIFY(!cifr.waitForProgress(100)); + QVERIFY(!cifr.waitForFinished(100)); + QVERIFY(cifr.cancel()); + + QContactRemoveRequest crr; + crr.setFilter(fil); + crr.setManager(&mcm); + QVERIFY(crr.start()); + QVERIFY(crr.cancel()); + QVERIFY(!crr.waitForProgress(100)); + QVERIFY(!crr.waitForFinished(100)); + QVERIFY(crr.start()); + QVERIFY(!crr.waitForProgress(100)); + QVERIFY(!crr.waitForFinished(100)); + QVERIFY(crr.cancel()); + + QContactSaveRequest csr; + csr.setContacts(emptyCList); + csr.setManager(&mcm); + QVERIFY(csr.start()); + QVERIFY(csr.cancel()); + QVERIFY(!csr.waitForProgress(100)); + QVERIFY(!csr.waitForFinished(100)); + QVERIFY(csr.start()); + QVERIFY(!csr.waitForProgress(100)); + QVERIFY(!csr.waitForFinished(100)); + QVERIFY(csr.cancel()); + + QContactDetailDefinitionFetchRequest dfr; + dfr.setNames(emptyDNList); + dfr.setManager(&mcm); + QVERIFY(dfr.start()); + QVERIFY(dfr.cancel()); + QVERIFY(!dfr.waitForProgress(100)); + QVERIFY(!dfr.waitForFinished(100)); + QVERIFY(dfr.start()); + QVERIFY(!dfr.waitForProgress(100)); + QVERIFY(!dfr.waitForFinished(100)); + QVERIFY(dfr.cancel()); + + QContactDetailDefinitionSaveRequest dsr; + dsr.setDefinitions(emptyDList); + dsr.setManager(&mcm); + QVERIFY(dsr.start()); + QVERIFY(dsr.cancel()); + QVERIFY(!dsr.waitForProgress(100)); + QVERIFY(!dsr.waitForFinished(100)); + QVERIFY(dsr.start()); + QVERIFY(!dsr.waitForProgress(100)); + QVERIFY(!dsr.waitForFinished(100)); + QVERIFY(dsr.cancel()); + + QContactDetailDefinitionRemoveRequest drr; + drr.setNames(emptyDNList); + drr.setManager(&mcm); + QVERIFY(drr.start()); + QVERIFY(drr.cancel()); + QVERIFY(!drr.waitForProgress(100)); + QVERIFY(!drr.waitForFinished(100)); + QVERIFY(drr.start()); + QVERIFY(!drr.waitForProgress(100)); + QVERIFY(!drr.waitForFinished(100)); + QVERIFY(drr.cancel()); +} + +void tst_QContactAsync::threadDelivery() +{ + QFETCH(QString, uri); + QContactManager *cm(0); + QVERIFY(prepareModel(uri, cm)); + m_mainThreadId = cm->thread()->currentThreadId(); + m_progressSlotThreadId = m_mainThreadId; + + // now perform a fetch request and check that the progress is delivered to the correct thread. + QContactFetchRequest *req = new QContactFetchRequest; + req->setManager(cm); + connect(req, SIGNAL(progress(QContactFetchRequest*,bool)), this, SLOT(progressReceived(QContactFetchRequest*, bool))); + req->start(); + + int totalWaitTime = 0; + while (req->status() != QContactAbstractRequest::Finished) { + // ensure that the progress signal was delivered to the main thread. + QCOMPARE(m_mainThreadId, m_progressSlotThreadId); + + QTest::qWait(5); // spin until done + totalWaitTime += 5; + + // break after 30 seconds. + if (totalWaitTime > 30000) { + delete req; + delete cm; + QSKIP("Asynchronous request not complete after 30 seconds!", SkipSingle); + } + } + + // ensure that the progress signal was delivered to the main thread. + QCOMPARE(m_mainThreadId, m_progressSlotThreadId); + delete req; + delete cm; +} + +void tst_QContactAsync::progressReceived(QContactFetchRequest* request, bool appendOnly) +{ + Q_UNUSED(appendOnly); + m_progressSlotThreadId = request->thread()->currentThreadId(); +} + +void tst_QContactAsync::addManagers() +{ + QTest::addColumn("uri"); + + // retrieve the list of available managers + QStringList managers = QContactManager::availableManagers(); + + // remove ones that we know will not pass + managers.removeAll("invalid"); + managers.removeAll("maliciousplugin"); + managers.removeAll("testdummy"); + + foreach(QString mgr, managers) { + QMap params; + QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); + if (mgr == "memory") { + params.insert("id", "tst_QContactManager"); + QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); + } + } +} + +bool tst_QContactAsync::prepareModel(const QString &managerUri, QContactManager *&cm) +{ + QList contacts; + QList relationships; + return prepareModel(managerUri, cm, contacts, relationships); +} + +bool tst_QContactAsync::prepareModel(const QString &managerUri, QContactManager *&cm, QList &contacts, QList &relationships) +{ + cm = QContactManager::fromUri(managerUri); + + // XXX TODO: ensure that this is the case: + // there should be no contacts in the database. + QList toRemove = cm->contacts(); + foreach (const QContactLocalId& removeId, toRemove) { + if (!cm->removeContact(removeId)) + return false; + } + + QContact a, b, c, d, e, f; + QContactPhoneNumber n; + n.setNumber("1"); + a.saveDetail(&n); + n.setNumber("2"); + b.saveDetail(&n); + n.setNumber("3"); + c.saveDetail(&n); + n.setNumber("4"); + d.saveDetail(&n); + n.setNumber("5"); + e.saveDetail(&n); + n.setNumber("6"); + f.saveDetail(&n); + + QContactUrl url; + url.setUrl("http://test.nokia.com"); + a.saveDetail(&url); + + a.setType(QContactType::TypeGroup); + b.setType(QContactType::TypeGroup); + c.setType(QContactType::TypeGroup); + + if (!cm->saveContact(&a)) + return false; + if (!cm->saveContact(&b)) + return false; + if (!cm->saveContact(&c)) + return false; + if (!cm->saveContact(&d)) + return false; + if (!cm->saveContact(&e)) + return false; + if (!cm->saveContact(&f)) + return false; + + contacts.append(a); + contacts.append(b); + contacts.append(c); + contacts.append(d); + contacts.append(e); + contacts.append(f); + + if (cm->hasFeature(QContactManager::Relationships)) + { + QStringList supportedRelationshipTypes = cm->supportedRelationshipTypes(); + + if (cm->hasFeature(QContactManager::ArbitraryRelationshipTypes)) { + supportedRelationshipTypes.insert(0, "some-arbitrary-relationship"); + if (!supportedRelationshipTypes.contains(QContactRelationship::HasManager)) + supportedRelationshipTypes.append(QContactRelationship::HasManager); + if (!supportedRelationshipTypes.contains(QContactRelationship::HasAssistant)) + supportedRelationshipTypes.append(QContactRelationship::HasAssistant); + if (!supportedRelationshipTypes.contains(QContactRelationship::HasSpouse)) + supportedRelationshipTypes.append(QContactRelationship::HasSpouse); + } + + if (supportedRelationshipTypes.count() == 0) + return false; // should not happen + + QContactRelationship adRel; + adRel.setFirst(a.id()); + adRel.setSecond(d.id()); + adRel.setRelationshipType(supportedRelationshipTypes.at(0)); + if (!cm->saveRelationship(&adRel)) + return false; + relationships.append(adRel); + + QContactRelationship aeRel; + aeRel.setFirst(a.id()); + aeRel.setSecond(e.id()); + aeRel.setRelationshipType(supportedRelationshipTypes.at(0)); + if (!cm->saveRelationship(&aeRel)) + return false; + relationships.append(aeRel); + + QContactRelationship beRel; + beRel.setFirst(b.id()); + beRel.setSecond(e.id()); + beRel.setRelationshipType(supportedRelationshipTypes.at(0)); + if (!cm->saveRelationship(&beRel)) + return false; + relationships.append(beRel); + + QContactRelationship ceRel; + ceRel.setFirst(c.id()); + ceRel.setSecond(e.id()); + ceRel.setRelationshipType(supportedRelationshipTypes.at(0)); + if (!cm->saveRelationship(&ceRel)) + return false; + relationships.append(ceRel); + + QContactRelationship cfRel; + cfRel.setFirst(c.id()); + cfRel.setSecond(f.id()); + cfRel.setRelationshipType(supportedRelationshipTypes.at(0)); + if (!cm->saveRelationship(&cfRel)) + return false; + relationships.append(cfRel); + + if (supportedRelationshipTypes.count() > 1) + { + QContactRelationship daRel; + daRel.setFirst(d.id()); + daRel.setSecond(a.id()); + daRel.setRelationshipType(supportedRelationshipTypes.at(1)); + if (!cm->saveRelationship(&daRel)) + return false; + relationships.append(daRel); + } + + if (supportedRelationshipTypes.count() > 2) + { + QContactRelationship adRel2; + adRel2.setFirst(a.id()); + adRel2.setSecond(d.id()); + adRel2.setRelationshipType(supportedRelationshipTypes.at(2)); + if (!cm->saveRelationship(&adRel2)) + return false; + relationships.append(adRel2); + } + } + + return true; + + // TODO: cleanup once test is complete +} + +QTEST_MAIN(tst_QContactAsync) +#include "tst_qcontactasync.moc"