Changes: Skip detail fields with datatype URL in testSparqlSorting
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.git] / tests / ut_qtcontacts_trackerplugin / ut_qtcontacts_trackerplugin.cpp
1 /*********************************************************************************
2  ** This file is part of QtContacts tracker storage plugin
3  **
4  ** Copyright (c) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
5  **
6  ** Contact:  Nokia Corporation (info@qt.nokia.com)
7  **
8  ** GNU Lesser General Public License Usage
9  ** This file may be used under the terms of the GNU Lesser General Public License
10  ** version 2.1 as published by the Free Software Foundation and appearing in the
11  ** file LICENSE.LGPL included in the packaging of this file.  Please review the
12  ** following information to ensure the GNU Lesser General Public License version
13  ** 2.1 requirements will be met:
14  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
15  **
16  ** In addition, as a special exception, Nokia gives you certain additional rights.
17  ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
18  ** in the file LGPL_EXCEPTION.txt in this package.
19  **
20  ** Other Usage
21  ** Alternatively, this file may be used in accordance with the terms and
22  ** conditions contained in a signed written agreement between you and Nokia.
23  *********************************************************************************/
24
25 #include "ut_qtcontacts_trackerplugin.h"
26 #include "slots.h"
27
28 #include <dao/contactdetail.h>
29 #include <dao/contactdetailschema.h>
30 #include <dao/conversion.h>
31 #include <dao/support.h>
32
33 #include <engine/engine.h>
34 #include <engine/contactidfetchrequest.h>
35 #include <engine/relationshipsaverequest.h>
36 #include <engine/relationshipfetchrequest.h>
37
38 #include <lib/constants.h>
39 #include <lib/contactmergerequest.h>
40 #include <lib/customdetails.h>
41 #include <lib/phoneutils.h>
42 #include <lib/settings.h>
43 #include <lib/sparqlresolver.h>
44 #include <lib/trackerchangelistener.h>
45 #include <lib/unmergeimcontactsrequest.h>
46
47 #include <ontologies/nco.h>
48
49 ////////////////////////////////////////////////////////////////////////////////////////////////////
50
51 CUBI_USE_NAMESPACE
52 CUBI_USE_NAMESPACE_RESOURCES
53
54 ////////////////////////////////////////////////////////////////////////////////////////////////////
55
56 typedef QPair<QString, QString> PairOfStrings;
57 typedef QHash<QString, QSet<QString> > ImplictTypeHash;
58
59 ////////////////////////////////////////////////////////////////////////////////////////////////////
60
61 static ContactDetailSample
62 makeDetailSample(const QContactDetail &detail, const QString &value)
63 {
64     return qMakePair(detail, value);
65 }
66
67 static ContactDetailSample
68 makeDetailSample(const QString &detailName, const QString &fieldName, const QString &value)
69 {
70     QContactDetail detail(detailName);
71     detail.setValue(fieldName, value);
72     return qMakePair(detail, value);
73 }
74
75 ////////////////////////////////////////////////////////////////////////////////////////////////////
76
77 static QStringList
78 sortedStringList(QStringList list)
79 {
80     return list.sort(), list;
81 }
82
83 static QStringList
84 impliedTypes(const QStringList &subTypes, const ImplictTypeHash &implicitTypes)
85 {
86     QSet<QString> result = subTypes.toSet();
87
88     foreach(const QString &type, subTypes) {
89         ImplictTypeHash::ConstIterator it = implicitTypes.find(type);
90
91         if (it == implicitTypes.constEnd()) {
92             qWarning() << "unknown subtype:" << type;
93             continue;
94         }
95
96         result += it.value();
97     }
98
99     return sortedStringList(result.toList());
100 }
101
102 static QStringList
103 impliedPhoneNumberTypes(const QStringList &subTypes)
104 {
105     typedef QHash<QString, QSet<QString> > ImplictTypeHash;
106     static ImplictTypeHash implicitTypes;
107
108     if (implicitTypes.isEmpty()) {
109         implicitTypes.insert(QContactPhoneNumber::SubTypeAssistant, QSet<QString>() <<
110                              QContactPhoneNumber::SubTypeVoice);
111         implicitTypes.insert(QContactPhoneNumber::SubTypeBulletinBoardSystem, QSet<QString>() <<
112                              QContactPhoneNumber::SubTypeModem);
113         implicitTypes.insert(QContactPhoneNumber::SubTypeCar, QSet<QString>() <<
114                              QContactPhoneNumber::SubTypeVoice);
115         implicitTypes.insert(QContactPhoneNumber::SubTypeFax, QSet<QString>());
116         implicitTypes.insert(QContactPhoneNumber::SubTypeMessagingCapable, QSet<QString>());
117         implicitTypes.insert(QContactPhoneNumber::SubTypeMobile, QSet<QString>() <<
118                              QContactPhoneNumber::SubTypeMessagingCapable <<
119                              QContactPhoneNumber::SubTypeVoice);
120         implicitTypes.insert(QContactPhoneNumber::SubTypeModem, QSet<QString>());
121         implicitTypes.insert(QContactPhoneNumber::SubTypePager, QSet<QString>() <<
122                              QContactPhoneNumber::SubTypeMessagingCapable);
123         implicitTypes.insert(QContactPhoneNumber::SubTypeVideo, QSet<QString>() <<
124                              QContactPhoneNumber::SubTypeVoice);
125         implicitTypes.insert(QContactPhoneNumber::SubTypeVoice, QSet<QString>());
126     }
127
128     return impliedTypes(subTypes, implicitTypes);
129 }
130
131 static QStringList
132 impliedSubTypes(const QString &definitionName, const QStringList &values)
133 {
134     if (QContactPhoneNumber::DefinitionName == definitionName) {
135         return impliedPhoneNumberTypes(values);
136     }
137
138     return values;
139 }
140
141
142 static bool
143 detailMatches(const QContactDetail &reference, const QContactDetail &sample)
144 {
145     const QVariantMap fields = reference.variantValues();
146     QVariantMap::ConstIterator it;
147
148     for(it = fields.constBegin(); it != fields.constEnd(); ++it) {
149         Qt::CaseSensitivity cs = Qt::CaseSensitive;
150
151         QVariant sampleValue = sample.variantValue(it.key());
152         QVariant referenceValue = it.value();
153
154         if (referenceValue.toString().isEmpty() && sampleValue.isNull()) {
155             continue;
156         }
157
158         if (QVariant::Date == referenceValue.type() ||
159             QVariant::DateTime == referenceValue.type()) {
160             referenceValue.setValue(referenceValue.toDate().toString(Qt::ISODate));
161             sampleValue.setValue(sampleValue.toDate().toString(Qt::ISODate));
162         }
163
164         if (QVariant::StringList == referenceValue.type() &&
165             QVariant::String == sampleValue.type() &&
166             1 == referenceValue.toStringList().length()) {
167             referenceValue = referenceValue.toStringList().first();
168         }
169
170         if (QContactAddress::DefinitionName == reference.definitionName() &&
171             QContactAddress::FieldSubTypes == it.key()) {
172             referenceValue.setValue(sortedStringList(referenceValue.toStringList()));
173             sampleValue.setValue(sortedStringList(sampleValue.toStringList()));
174         }
175
176         if (QContactGender::DefinitionName == reference.definitionName()) {
177             cs = Qt::CaseInsensitive;
178         }
179
180         if (QContactUrl::DefinitionName == reference.definitionName() &&
181             QContactUrl::FieldUrl == it.key()) {
182             referenceValue.setValue(QUrl(referenceValue.toString()));
183             sampleValue.setValue(QUrl(sampleValue.toString()));
184         }
185
186         if (QVariant::StringList == referenceValue.type() && QLatin1String("SubTypes") == it.key()) {
187             // ignore value order for SubTypes lists
188             QStringList referenceList = impliedSubTypes(reference.definitionName(),
189                                                         referenceValue.toStringList());
190             referenceValue = (qSort(referenceList), referenceList);
191             QStringList sampleList = sampleValue.toStringList();
192             sampleValue = (qSort(sampleList), sampleList);
193         }
194
195         if (not qctEquals(sampleValue, referenceValue, cs)) {
196             return false;
197         }
198     }
199
200     return true;
201 }
202
203 static QStringList
204 getFuzzableDetailDefinitionNamesForPredicate(const QContactDetailDefinitionMap &definitions,
205                                              bool (*predicate)(const QContactDetailDefinition &))
206 {
207     QStringList names;
208
209     foreach(const QContactDetailDefinition &def, definitions) {
210         if (predicate(def) == true) {
211             names.append(def.name());
212         }
213     }
214
215     return names;
216 }
217
218 static bool
219 isFuzzableDetail(const QContactDetailDefinition &def)
220 {
221     return (QContactDisplayLabel::DefinitionName != def.name() &&
222             QContactGlobalPresence::DefinitionName != def.name() &&
223             QContactGuid::DefinitionName != def.name() &&
224             QContactPresence::DefinitionName != def.name() &&
225             QContactThumbnail::DefinitionName != def.name() &&
226             QContactType::DefinitionName != def.name());
227 }
228
229 static QStringList
230 getFuzzableDetailDefinitionNamesForType(const QContactDetailDefinitionMap &definitions)
231 {
232     return getFuzzableDetailDefinitionNamesForPredicate(definitions, isFuzzableDetail);
233 }
234
235 static bool
236 isSortableDetail(const QContactDetailDefinition &def)
237 {
238     return (isFuzzableDetail(def) &&
239             QContactRelevance::DefinitionName != def.name());
240 }
241
242 static QStringList
243 getSortableDetailDefinitionNamesForType(const QContactDetailDefinitionMap &definitions)
244 {
245     return getFuzzableDetailDefinitionNamesForPredicate(definitions, isSortableDetail);
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////////////////////////
249
250 ut_qtcontacts_trackerplugin::ut_qtcontacts_trackerplugin(QObject *parent)
251     : ut_qtcontacts_trackerplugin_common(QDir(DATADIR), QDir(SRCDIR), parent)
252 {
253 }
254
255 void
256 ut_qtcontacts_trackerplugin::initTestCase()
257 {
258     QScopedPointer<QSparqlResult> result
259             (executeQuery(loadReferenceFile("test-account-1.rq"),
260                           QSparqlQuery::InsertStatement));
261
262     QVERIFY(not result.isNull());
263 }
264
265 void
266 ut_qtcontacts_trackerplugin::testContacts()
267 {
268     QContact c1, c2;
269
270     QContactManager::Error error;
271
272     error = QContactManager::UnspecifiedError;
273     QVERIFY(engine()->saveContact(&c1, &error));
274     QCOMPARE(error, QContactManager::NoError);
275
276     error = QContactManager::UnspecifiedError;
277     QVERIFY(engine()->saveContact(&c2, &error));
278     QCOMPARE(error, QContactManager::NoError);
279
280     error = QContactManager::UnspecifiedError;
281
282     QContactFilter filter;
283     QList<QContactLocalId> contacts(engine()->contactIds(filter, NoSortOrders, &error));
284     QCOMPARE(error, QContactManager::NoError);
285
286     QVERIFY2(contacts.contains(c1.localId()), "Previously added contact is not found");
287     QVERIFY2(contacts.contains(c2.localId()), "Previously added contact is not found");
288 }
289
290 void
291 ut_qtcontacts_trackerplugin::testContact()
292 {
293     // Test invalid contact id
294     QContactManager::Error error;
295
296     error = QContactManager::UnspecifiedError;
297     QContact invalidContact = engine()->contactImpl( -1, NoFetchHint, &error);
298     QVERIFY(error != QContactManager::NoError);
299
300     // Add a contact
301     QContact newContact;
302     const QContactLocalId oldid = newContact.localId();
303     error = QContactManager::UnspecifiedError;
304     QVERIFY(engine()->saveContact(&newContact, &error));
305     QCOMPARE(error, QContactManager::NoError);
306
307     QContactLocalId id = newContact.localId();
308     QVERIFY(id != oldid);
309
310     // Find the added contact
311     error = QContactManager::UnspecifiedError;
312     QContact c = engine()->contactImpl(id, NoFetchHint, &error);
313     QCOMPARE(c.localId(), newContact.localId());
314     QCOMPARE(error, QContactManager::NoError);
315 }
316
317 void
318 ut_qtcontacts_trackerplugin::testSaveFullname()
319 {
320     const QString fullname = "A Full Name";
321
322     QList<QContact> contacts = parseVCards(referenceFileName("fullname.vcf"), 1);
323     QVERIFY(not contacts.isEmpty());
324
325     QContactName name = contacts.first().detail<QContactName>();
326     QCOMPARE(name.customLabel(), fullname);
327
328     QContactManager::Error error = QContactManager::UnspecifiedError;
329     bool success = engine()->saveContact(&contacts.first(), &error);
330     QCOMPARE(error, QContactManager::NoError);
331     QVERIFY(0 != contacts.first().localId());
332     QVERIFY(success);
333
334     name = contact(contacts.first().localId()).detail<QContactName>();
335     QCOMPARE(name.customLabel(), fullname);
336
337     loadRawTuples("fullname.ttl");
338     name = contact("contact:fullname").detail<QContactName>();
339     QCOMPARE(name.customLabel(), fullname);
340 }
341
342 void
343 ut_qtcontacts_trackerplugin::testSaveName()
344 {
345     QContact c;
346     QContactLocalId initialId = c.localId();
347     int detailsAdded = 0;
348
349     QMap<QString,QString> nameValues;
350     QContactName name;
351
352     nameValues.insert(QContactName::FieldPrefix, "Mr");
353     nameValues.insert(QContactName::FieldFirstName, "John");
354     nameValues.insert(QContactName::FieldMiddleName, "Rupert");
355     nameValues.insert(QContactName::FieldLastName, "Doe");
356     nameValues.insert(QContactName::FieldSuffix, "III");
357     nameValues.insert(QContactName::FieldCustomLabel, "The Duke");
358
359     foreach (QString field, nameValues.keys()) {
360         name.setValue(field, nameValues.value(field));
361     }
362     c.saveDetail(&name);
363
364     QContactNickname nick;
365     nick.setValue(QLatin1String(QContactNickname::FieldNickname), "Johnny");
366     c.saveDetail(&nick);
367
368     QCOMPARE(c.detail<QContactName>().prefix(), QLatin1String("Mr"));
369     QCOMPARE(c.detail<QContactName>().firstName(), QLatin1String("John"));
370     QCOMPARE(c.detail<QContactName>().middleName(), QLatin1String("Rupert"));
371     QCOMPARE(c.detail<QContactName>().lastName(), QLatin1String("Doe"));
372     QCOMPARE(c.detail<QContactName>().suffix(), QLatin1String("III"));
373     QCOMPARE(c.detail<QContactName>().customLabel(), QLatin1String("The Duke"));
374     QCOMPARE(c.detail<QContactNickname>().nickname(), QLatin1String("Johnny"));
375
376     detailsAdded++;
377
378     QContactManager::Error error(QContactManager::UnspecifiedError);
379     QVERIFY(engine()->saveContact(&c, &error));
380     QCOMPARE(error,  QContactManager::NoError);
381     QVERIFY(c.localId() != initialId);
382     QContact contact = this->contact(c.localId());
383     QList<QContactName> details = contact.details<QContactName>();
384     QList<QContactNickname> details2 = contact.details<QContactNickname>();
385     QCOMPARE(details.count(), detailsAdded);
386     QCOMPARE(details2.count(), detailsAdded);
387     // Name is unique
388     foreach(QString field, nameValues.keys()) {
389         QCOMPARE(details.first().value(field), nameValues.value(field));
390     }
391     QCOMPARE(details2.first().value(QLatin1String(QContactNickname::FieldNickname)), QString("Johnny"));
392
393     // Try changing the name of the saved contact.
394     {
395         QMap<QString,QString> nameValues;
396         QContactName name = c.detail<QContactName>();
397         nameValues.insert(QLatin1String(QContactName::FieldPrefix), "Mr2");
398         nameValues.insert(QLatin1String(QContactName::FieldFirstName), "John2");
399         nameValues.insert(QLatin1String(QContactName::FieldMiddleName), "Rupert2");
400         nameValues.insert(QLatin1String(QContactName::FieldLastName), "");
401         //    nameValues.insert(QContactName::FieldSuffix, "III");
402
403         foreach (QString field, nameValues.keys()) {
404             name.setValue(field, nameValues.value(field));
405         }
406         c.saveDetail(&name);
407
408         QContactNickname nick = c.detail<QContactNickname>();
409         nick.setValue(QLatin1String(QContactNickname::FieldNickname), "Johnny2");
410         c.saveDetail(&nick);
411
412
413         error = QContactManager::UnspecifiedError;
414         QVERIFY(engine()->saveContact(&c, &error));
415         QCOMPARE(error,  QContactManager::NoError);
416         QVERIFY(c.localId() != initialId);
417
418         error = QContactManager::UnspecifiedError;
419         QContact contact = engine()->contactImpl(c.localId(), NoFetchHint, &error);
420         QCOMPARE(error,  QContactManager::NoError);
421         QList<QContactName> details = contact.details<QContactName>();
422         QList<QContactNickname> details2 = contact.details<QContactNickname>();
423         QCOMPARE(details.count(), detailsAdded);
424         QCOMPARE(details2.count(), detailsAdded);
425
426         // Name is unique
427         foreach(QString field, nameValues.keys()) {
428             QCOMPARE(details.at(0).value(field), nameValues.value(field));
429         }
430
431         QCOMPARE(details2.at(0).value(QLatin1String(QContactNickname::FieldNickname)), QString("Johnny2"));
432      }
433 }
434
435 void
436 ut_qtcontacts_trackerplugin::testSaveNameUnique()
437 {
438     // save contact with one name
439     QContactName name1;
440     name1.setFirstName("Till");
441     name1.setLastName("Eulenspiegel");
442
443     QContact savedContact;
444     QVERIFY(savedContact.saveDetail(&name1));
445
446     QContactManager::Error error = QContactManager::UnspecifiedError;
447     QVERIFY(engine()->saveContact(&savedContact, &error));
448     QCOMPARE(error, QContactManager::NoError);
449     QVERIFY(0 != savedContact.localId());
450
451     // fetch the contact and compare content
452     error = QContactManager::UnspecifiedError;
453     QContact fetchedContact = engine()->contactImpl(savedContact.localId(), NoFetchHint, &error);
454     QCOMPARE(error, QContactManager::NoError);
455
456     QCOMPARE(fetchedContact.detail<QContactName>().firstName(), name1.firstName());
457     QCOMPARE(fetchedContact.detail<QContactName>().lastName(), name1.lastName());
458     QCOMPARE(fetchedContact.localId(), savedContact.localId());
459
460     // save contact with second name detail which is invalid
461     QContactName name2;
462     name2.setFirstName("Hans");
463     name2.setLastName("Wurst");
464     QVERIFY(savedContact.saveDetail(&name2));
465
466     // the engine shall drop the odd detail
467     QTest::ignoreMessage(QtWarningMsg, "Dropping odd details: Name detail must be unique");
468
469     qctLogger().setShowLocation(false);
470     error = QContactManager::UnspecifiedError;
471     const bool contactSaved = engine()->saveContact(&savedContact, &error);
472     qctLogger().setShowLocation(true);
473
474     QCOMPARE(error, QContactManager::NoError);
475     QVERIFY(contactSaved);
476
477     // again fetch the contact and compare content
478     error = QContactManager::UnspecifiedError;
479     fetchedContact = engine()->contactImpl(savedContact.localId(), NoFetchHint, &error);
480     QCOMPARE(error, QContactManager::NoError);
481
482     QCOMPARE(fetchedContact.detail<QContactName>().firstName(), name1.firstName());
483     QCOMPARE(fetchedContact.detail<QContactName>().lastName(), name1.lastName());
484     QCOMPARE(fetchedContact.localId(), savedContact.localId());
485 }
486
487 void
488 ut_qtcontacts_trackerplugin::testSaveNonLatin1Name_data()
489 {
490     QTest::addColumn<QString>("givenName");
491     QTest::addColumn<QString>("middleName");
492     QTest::addColumn<QString>("familyName");
493     QTest::addColumn<QString>("formattedName");
494
495     QTest::newRow("german name")
496             // given name
497             << QString::fromUtf8("J\xC3\xBCrgen") // Jürgen
498             // middle name
499             << QString::fromUtf8("H\xC3\xA4nschen") // Hänschen
500             // family name
501             << QString::fromUtf8("K\x6C\x65hler") // Köhler
502             // formatted name
503             << QString::fromUtf8("J\xC3\xBCrgen K\x6C\x65hler"); // Jürgen Köhler
504
505     QTest::newRow("chinese name")
506             // given name
507             << QString::fromUtf8("\xE6\x98\x8E") // 明
508             // middle name
509             << QString::fromUtf8("")
510             // family name
511             << QString::fromUtf8("\xE6\x9D\x8E") // 李
512             // formatted name
513             << QString::fromUtf8("\xE6\x98\x8E \xE6\x9D\x8E"); // 明 李
514
515     QTest::newRow("russian name")
516             // given name
517             << QString::fromUtf8("\xD0\xAE\xD1\x80\xD0\xb8\xD0\xB9") // Юрий
518             // middle name
519             << QString::fromUtf8("\xD0\x90\xD0\xBB\xD0\xB5\xD0\xBA\xD1\x81\xD0\xB5\xD0\xB5\xD0\xB2\xD0\xB8\xD1\x87") // Алексеевич
520             // family name
521             << QString::fromUtf8("\xD0\x93\xD0\xB0\xD0\xB3\xD0\xB0\xD1\x80\xD0\xB8\xD0\xBD") // Гагарин
522             // formatted name
523             << QString::fromUtf8("\xD0\xAE\xD1\x80\xD0\xB8\xD0\xB9\x20\xD0\x93\xD0\xB0\xD0\xB3\xD0\xB0\xD1\x80\xD0\xB8\xD0\xBD"); // Юрий Гагарин
524
525     // TODO: some name from RTL language, like arabic or hebrew
526 }
527
528 void
529 ut_qtcontacts_trackerplugin::testSaveNonLatin1Name()
530 {
531     QFETCH(QString, givenName);
532     QFETCH(QString, middleName);
533     QFETCH(QString, familyName);
534     QFETCH(QString, formattedName);
535
536     // create contact
537     QContact contact;
538     QContactName nameDetail;
539     nameDetail.setFirstName(givenName);
540     nameDetail.setMiddleName(middleName);
541     nameDetail.setLastName(familyName);
542     contact.saveDetail(&nameDetail);
543
544     QContactManager::Error error = QContactManager::UnspecifiedError;
545     QVERIFY(engine()->saveContact(&contact, &error));
546     registerForCleanup(contact);
547     QCOMPARE(error,  QContactManager::NoError);
548
549     // fetch contact
550     error = QContactManager::UnspecifiedError;
551     const QContact fetchedContact = engine()->contactImpl(contact.localId(), NoFetchHint, &error);
552     QCOMPARE(error,  QContactManager::NoError);
553     const QContactName fetchedContactName = fetchedContact.detail<QContactName>();
554     QCOMPARE(fetchedContactName.firstName(), givenName);
555     QCOMPARE(fetchedContactName.middleName(), middleName);
556     QCOMPARE(fetchedContactName.lastName(), familyName);
557     QCOMPARE(fetchedContact.displayLabel(), formattedName);
558 }
559
560 // test NB#189108
561 void
562 ut_qtcontacts_trackerplugin::testFetchAll()
563 {
564     // create some few contacts to have something in the db for sure
565     QList<QContact> contacts;
566
567     for(int i = 1; i <= 5; ++i) {
568         QContactNickname nickname;
569         nickname.setNickname(QString::fromLatin1("Fetchy %1").arg(i));
570
571         contacts.append(QContact());
572         QVERIFY(contacts.last().saveDetail(&nickname));
573     }
574
575     QContactManager::Error error = QContactManager::UnspecifiedError;
576     const bool contactsSaved = engine()->saveContacts(&contacts, 0, &error);
577     QCOMPARE(error, QContactManager::NoError);
578     QVERIFY(contactsSaved);
579
580     // directly asks tracker how many contacts are stored
581     const QString queryString =
582             "SELECT COUNT(?c) WHERE {"
583             "  { ?c a nco:PersonContact } UNION"
584             "  { ?c a nco:ContactGroup . ?c a nco:Contact }"
585             "}";
586
587     const QScopedPointer<QSparqlResult> result
588             (executeQuery(queryString, QSparqlQuery::SelectStatement));
589
590     QVERIFY(not result.isNull());
591     QVERIFY(result->next());
592
593     // fetch contacts via contact manager engine
594     contacts = engine()->contacts(QContactFilter(), NoSortOrders,
595                                   NoFetchHint, &error);
596
597     // compare sparql and engine result
598     QCOMPARE(result->value(0).toInt(), contacts.count());
599 }
600
601 void ut_qtcontacts_trackerplugin::testFetchById_data()
602 {
603     QTest::addColumn<bool>("useSyncEngineCall");
604     QTest::addColumn<bool>("doError");
605
606     QTest::newRow("async (without error)") << false << false;
607     QTest::newRow("sync (without error)")  << true  << false;
608     QTest::newRow("async (with error)")    << false << true;
609     QTest::newRow("sync (with error)")     << true  << true;
610 }
611
612 void ut_qtcontacts_trackerplugin::testFetchById()
613 {
614     QFETCH(bool, useSyncEngineCall);
615     QFETCH(bool, doError);
616
617     // create some few contacts to have something in the db for sure
618     QList<QContact> contacts;
619
620     for(int i = 1; i <= 5; ++i) {
621         QContact contact;
622
623         SET_TESTNICKNAME_TO_CONTACT(contact);
624         contacts << contact;
625     }
626
627     QContactManager::Error error = QContactManager::UnspecifiedError;
628     const bool contactsSaved = engine()->saveContacts(&contacts, 0, &error);
629     QCOMPARE(error, QContactManager::NoError);
630     QVERIFY(contactsSaved);
631
632     QList<QContactLocalId> ids = QList<QContactLocalId>()
633         << contacts.at(3).localId()
634         << contacts.at(2).localId()
635         << contacts.at(4).localId();
636
637     // insert in the middle, to see if contacts before and after bad ids are fetched
638     static const int posOfError = 2;
639     if (doError) {
640         // insert non existing id before last
641         // (using id of last save + random offset should make sure, as tracker
642         // uses increasing numbers for ids of new resources)
643         ids.insert(posOfError, contacts.last().localId() + 23);
644     }
645
646     QList<QContact> fetchedContacts;
647     QMap<int, QContactManager::Error> errorMap;
648     error = QContactManager::UnspecifiedError;
649
650     if (useSyncEngineCall) {
651         fetchedContacts = engine()->contacts(ids, NoFetchHint, &errorMap, &error);
652     } else {
653         QContactFetchByIdRequest request;
654
655         request.setLocalIds(ids);
656
657         QVERIFY(engine()->startRequest(&request));
658         QVERIFY(engine()->waitForRequestFinishedImpl(&request, DefaultTimeout));
659
660         QVERIFY(request.isFinished());
661
662         fetchedContacts = request.contacts();
663         error = request.error();
664         errorMap = request.errorMap();
665     }
666
667     if (doError) {
668         QCOMPARE(error, QContactManager::DoesNotExistError);
669         QCOMPARE(errorMap.size(), 1);
670         QCOMPARE(errorMap.value(posOfError), QContactManager::DoesNotExistError);
671     } else {
672         QCOMPARE(error, QContactManager::NoError);
673         QVERIFY(errorMap.isEmpty());
674     }
675
676     QCOMPARE(fetchedContacts.size(), ids.size());
677     QCOMPARE(fetchedContacts.at(0).localId(), ids.at(0));
678     QCOMPARE(fetchedContacts.at(1).localId(), ids.at(1));
679     if (doError) {
680         QVERIFY(fetchedContacts.at(2).isEmpty());
681         QCOMPARE(fetchedContacts.at(3).localId(), ids.at(3));
682     } else {
683         QCOMPARE(fetchedContacts.at(2).localId(), ids.at(2));
684     }
685 }
686
687 void
688 ut_qtcontacts_trackerplugin::testTorture_data()
689 {
690     QTest::addColumn<int>("duration");
691     QTest::addColumn<bool>("cancel");
692     QTest::newRow("gentle") << 0 << false;
693     QTest::newRow("painful") << 2500 << false;
694     QTest::newRow("gentle-cancel") << 0 << true;
695     QTest::newRow("painful-cancel") << 2500 << true;
696 }
697
698 void
699 ut_qtcontacts_trackerplugin::testTorture()
700 {
701     QContactManager manager(QLatin1String("tracker"), makeEngineParams());
702     QCOMPARE(manager.managerName(), QLatin1String("tracker"));
703
704     QList<QContactAbstractRequest *> requests;
705     QList<QContactLocalId> ids = manager.contactIds();
706     static const int n_requests = 50;
707     QFETCH(int, duration);
708     QFETCH(bool, cancel);
709
710
711     for (int i = 0; i < n_requests; i ++) {
712         requests += new QContactFetchRequest;
713         requests.last()->setManager(&manager);
714         QVERIFY(requests.last()->start());
715
716         requests += new QContactLocalIdFetchRequest;
717         requests.last()->setManager(&manager);
718         QVERIFY(requests.last()->start());
719
720         requests += new QContactRelationshipFetchRequest;
721         requests.last()->setManager(&manager);
722         QVERIFY(requests.last()->start());
723
724         QContactFetchByIdRequest *const fbir = new QContactFetchByIdRequest;
725         requests += fbir;
726         fbir->setLocalIds(ids);
727         fbir->setManager(&manager);
728         QVERIFY(fbir->start());
729     }
730
731     if (duration > 0) {
732         QEventLoop loop;
733         QTimer::singleShot(duration, &loop, SLOT(quit()));
734         loop.exec();
735     }
736
737     if (cancel) {
738         foreach (QContactAbstractRequest *request, requests) {
739             request->cancel();
740             delete request;
741         }
742     } else {
743         qDeleteAll(requests);
744     }
745 }
746
747 void
748 ut_qtcontacts_trackerplugin::testSaveNothing()
749 {
750     QContactList nothing;
751     QContactManager::Error error = QContactManager::UnspecifiedError;
752     const bool contactsSaved = engine()->saveContacts(&nothing, 0, &error);
753     QCOMPARE(error, QContactManager::NoError);
754     QVERIFY(contactsSaved );
755 }
756
757 struct PhoneValue {
758     QString number;
759     QString latinNumber;
760     QString context;
761     QString subtype;
762 };
763
764 static void
765 add(QMap<QString, PhoneValue> &phoneValues, const QString &number,
766     const QString &context = QString(), const QString &subtype = QString())
767 {
768     const PhoneValue value = { number, number, context, subtype };
769     phoneValues.insert(number, value);
770 }
771
772 static void
773 addWithLatin(QMap<QString, PhoneValue> &phoneValues, const QString &number, const QString &latinNumber,
774              const QString &context = QString(), const QString &subtype = QString())
775 {
776     const PhoneValue value = { number, latinNumber, context, subtype };
777     phoneValues.insert(latinNumber, value);
778 }
779
780 void
781 ut_qtcontacts_trackerplugin::testSavePhoneNumber_data()
782 {
783     QTest::addColumn<int>("iteration");
784
785     QTest::newRow("1st run") << 0;
786     QTest::newRow("2nd run") << 1;
787     QTest::newRow("3rd run") << 2;
788 }
789
790 void
791 ut_qtcontacts_trackerplugin::testSavePhoneNumber()
792 {
793     QFETCH(int, iteration);
794     Q_UNUSED(iteration);
795
796     // use the same values for 2 contacts
797     QContact c;
798     QContactLocalId initialId = c.localId();
799     int detailsAdded = 0;
800     QContactName name;
801     name.setFirstName("I have phone numbers");
802     name.setLastName("Girl");
803     c.saveDetail(&name);
804
805     QMap<QString, PhoneValue> phoneValues;
806
807     add(phoneValues, "(704)486-6472", QContactDetail::ContextHome);
808     add(phoneValues, "(765)957-1663", QContactDetail::ContextHome);
809     add(phoneValues, "(999)888-1111", QContactDetail::ContextHome, QContactPhoneNumber::SubTypeMobile);
810     add(phoneValues, "(999)888-4444", QContactDetail::ContextHome, QContactPhoneNumber::SubTypeLandline);
811     // Also check that non-latin scripts are properly converted to latin
812     addWithLatin(phoneValues,
813                  QString::fromUtf8("\331\240\331\241\331\242\331\243\331\244\331\245\331\246\331\247\331\250\331\251"),
814                  QString::fromLatin1("0123456789"),
815                  QContactDetail::ContextHome);
816
817     add(phoneValues, "(792)123-6113", QContactDetail::ContextWork);
818     add(phoneValues, "(918)491-7361", QContactDetail::ContextWork, QContactPhoneNumber::SubTypeMobile);
819     add(phoneValues, "(412)670-1514", QContactDetail::ContextWork, QContactPhoneNumber::SubTypeCar);
820
821     foreach(const QString &number, phoneValues.keys()) {
822         const PhoneValue &value(phoneValues[number]);
823         QContactPhoneNumber phone;
824
825         phone.setNumber(value.number);
826
827         if (not value.context.isEmpty()) {
828             phone.setContexts(value.context);
829         }
830         if (not value.subtype.isEmpty()) {
831             phone.setSubTypes(value.subtype);
832         }
833
834         QVERIFY(c.saveDetail(&phone));
835         detailsAdded++;
836     }
837
838     QContactManager::Error error(QContactManager::UnspecifiedError);
839     QVERIFY(engine()->saveContact(&c, &error));
840     QCOMPARE(error,  QContactManager::NoError);
841     const QContactLocalId savedId(c.localId());
842     QVERIFY(savedId != initialId);
843     // wait for commit transaction to be done, no signals yet
844     for(int i = 0; i < 100; i++) {
845         usleep(10000);
846         QCoreApplication::processEvents();
847     }
848
849     // verify with synchronous read too
850     error = QContactManager::UnspecifiedError;
851     QContact contact = engine()->contactImpl(c.localId(), NoFetchHint, &error);
852     QCOMPARE(error,  QContactManager::NoError);
853     QList<QContactPhoneNumber> details = contact.details<QContactPhoneNumber>();
854     QCOMPARE(details.count(), detailsAdded);
855
856     foreach(const QContactPhoneNumber &detail, details) {
857         QMap<QString, PhoneValue>::ConstIterator i = phoneValues.find(detail.number());
858
859         // Verify that the stored values and attributes are the same as given
860         QVERIFY2(i != phoneValues.constEnd(), qPrintable(detail.number()));
861
862         QCOMPARE(detail.number(), i->latinNumber);
863         QCOMPARE(detail.contexts().first(), i->context);
864
865         if (i->subtype.isEmpty()) { // default is voice
866             QVERIFY2(detail.subTypes().contains(QContactPhoneNumber::SubTypeVoice),
867                      qPrintable(detail.number()));
868         } else {
869             QVERIFY2(detail.subTypes().contains(i->subtype),
870                      qPrintable(detail.number()));
871         }
872
873         QVERIFY2(not detail.detailUri().isEmpty(), qPrintable(i->number));
874     }
875
876     // save again with normalized phone numbers
877     error = QContactManager::UnspecifiedError;
878     QVERIFY(engine()->saveContact(&contact, &error));
879     QCOMPARE(error,  QContactManager::NoError);
880     QCOMPARE(contact.localId(), savedId);
881     // wait for commit transaction to be done, no signals yet
882     for(int i = 0; i < 100; i++) {
883         usleep(10000);
884         QCoreApplication::processEvents();
885     }
886
887     error = QContactManager::UnspecifiedError;
888     contact = engine()->contactImpl(c.localId(), NoFetchHint, &error);
889     QCOMPARE(error,  QContactManager::NoError);
890     details = contact.details<QContactPhoneNumber>();
891     QCOMPARE(details.count(), detailsAdded);
892
893     foreach(QContactPhoneNumber detail, details) {
894         QMap<QString, PhoneValue>::ConstIterator i = phoneValues.find(detail.number());
895
896         // Verify that the stored values and attributes are the same as given
897         QVERIFY2(i != phoneValues.constEnd(), qPrintable(detail.number()));
898
899         QCOMPARE(detail.number(), i->latinNumber);
900         QCOMPARE(detail.contexts().first(), i->context);
901
902         if (i->subtype.isEmpty()) { // default is voice
903             QVERIFY2(detail.subTypes().contains(QContactPhoneNumber::SubTypeVoice),
904                      qPrintable(detail.detailUri()));
905         } else {
906             QVERIFY2(detail.subTypes().contains(i->subtype),
907                      qPrintable(detail.detailUri()));
908         }
909     }
910
911     // edit one of numbers . values, context and subtypes and save again
912     QString editedPhoneValue = "+7044866473";
913     QContactPhoneNumber phone = details[0];
914     phone.setNumber(editedPhoneValue);
915     phone.setContexts(QContactDetail::ContextWork);
916     phone.setSubTypes(QContactPhoneNumber::SubTypeMobile);
917     c = contact;
918     QCOMPARE(c.localId(), savedId);
919     QVERIFY(c.saveDetail(&phone));
920     error = QContactManager::UnspecifiedError;
921     QVERIFY(engine()->saveContact(&c, &error));
922     QCOMPARE(c.localId(), savedId);
923     QCOMPARE(error,  QContactManager::NoError);
924     c = this->contact(c.localId(), QStringList()<<QContactPhoneNumber::DefinitionName);
925     QCOMPARE(c.localId(), savedId);
926     details = c.details<QContactPhoneNumber>();
927     QCOMPARE(details.count(), detailsAdded);
928     bool found = false;
929     foreach (QContactPhoneNumber detail, details) {
930         if(detail.number() == phone.number())
931         {
932             found = true;
933             QVERIFY(detail.subTypes().contains(QContactPhoneNumber::SubTypeMobile));
934             QVERIFY(detail.contexts().contains(QContactPhoneNumber::ContextWork));
935             break;
936         }
937     }
938     QVERIFY(found);
939 }
940
941 void
942 ut_qtcontacts_trackerplugin::testSimilarPhoneNumber()
943 {
944     QStringList phoneNumbers;
945     QContact contact;
946
947     QContactPhoneNumber tel1;
948     tel1.setNumber("112233");
949     phoneNumbers += tel1.number();
950     QVERIFY(contact.saveDetail(&tel1));
951
952     QContactPhoneNumber tel2;
953     tel2.setNumber("112233 ");
954     phoneNumbers += tel2.number();
955     QVERIFY(contact.saveDetail(&tel2));
956
957     QContactPhoneNumber tel3;
958     tel3.setNumber("11-22-33");
959     phoneNumbers += tel3.number();
960     QVERIFY(contact.saveDetail(&tel3));
961
962     QContactManager::Error error = QContactManager::UnspecifiedError;
963     const bool contactSaved = engine()->saveContact(&contact, &error);
964     QCOMPARE(error, QContactManager::NoError);
965     QVERIFY(0 != contact.localId());
966     QVERIFY(contactSaved);
967
968     error = QContactManager::UnspecifiedError;
969     contact = engine()->contactImpl(contact.localId(), NoFetchHint, &error);
970     QCOMPARE(error, QContactManager::NoError);
971     QVERIFY(0 != contact.localId());
972
973     QCOMPARE(contact.details<QContactPhoneNumber>().count(), phoneNumbers.size());
974
975     foreach(const QContactPhoneNumber &tel, contact.details<QContactPhoneNumber>()) {
976         phoneNumbers.removeOne(tel.number());
977     }
978
979     QVERIFY(phoneNumbers.isEmpty());
980 }
981
982 void
983 ut_qtcontacts_trackerplugin::testEasternArabicPhoneNumber_data()
984 {
985     QTest::addColumn<QString>("queryPhoneNumber");
986     QTest::addColumn<QString>("savedPhoneNumber");
987     QTest::addColumn<QString>("otherPhoneNumber");
988
989     QTest::newRow("arabic-latin")
990             << QString::fromUtf8("11223344")
991             << QString::fromUtf8("\331\241\331\241.\331\242\331\242.\331\243\331\243.\331\244\331\244")
992             << QString();
993     QTest::newRow("latin-arabic")
994             << QString::fromUtf8("\331\242\331\242.\331\243\331\243.\331\244\331\244.\331\245\331\245")
995             << QString::fromUtf8("22-334-455")
996             << QString();
997     QTest::newRow("arabic-arabic")
998             << QString::fromUtf8("\331\243\331\243.\331\244\331\244.\331\245\331\245.\331\246\331\246")
999             << QString::fromUtf8("\331\243\331\243-\331\244\331\244\331\245-\331\245\331\246\331\246")
1000             << QString();
1001     QTest::newRow("latin-latin")
1002             << QString::fromUtf8("44 556(677)")
1003             << QString::fromUtf8("44-556-677")
1004             << QString();
1005     QTest::newRow("false-friend")
1006             << QString::fromUtf8("+493055667788")
1007             << QString::fromUtf8("+49-30-5566-7788")
1008             << QString::fromUtf8("+49-338-566-7788");
1009 }
1010
1011 void
1012 ut_qtcontacts_trackerplugin::testEasternArabicPhoneNumber()
1013 {
1014     QFETCH(QString, queryPhoneNumber);
1015     QFETCH(QString, savedPhoneNumber);
1016     QFETCH(QString, otherPhoneNumber);
1017
1018     QContact savedContact;
1019
1020     {
1021         QContactPhoneNumber tel;
1022         tel.setNumber(savedPhoneNumber);
1023         QVERIFY(savedContact.saveDetail(&tel));
1024
1025         QContactManager::Error error = QContactManager::UnspecifiedError;
1026         const bool contactSaved = engine()->saveContact(&savedContact, &error);
1027         QCOMPARE(QContactManager::NoError, error);
1028         QVERIFY(0 != savedContact.localId());
1029         QVERIFY(contactSaved);
1030     }
1031
1032     QContact otherContact;
1033
1034     if (not otherPhoneNumber.isEmpty()) {
1035         QContactPhoneNumber tel;
1036         tel.setNumber(otherPhoneNumber);
1037         QVERIFY(otherContact.saveDetail(&tel));
1038
1039         QContactManager::Error error = QContactManager::UnspecifiedError;
1040         const bool contactSaved = engine()->saveContact(&otherContact, &error);
1041         QCOMPARE(QContactManager::NoError, error);
1042         QVERIFY(0 != otherContact.localId());
1043         QVERIFY(contactSaved);
1044     }
1045
1046     QContactDetailFilter f;
1047     f.setDetailDefinitionName(QContactPhoneNumber::DefinitionName,
1048                               QContactPhoneNumber::FieldNumber);
1049     f.setMatchFlags(QContactDetailFilter::MatchPhoneNumber);
1050     f.setValue(queryPhoneNumber);
1051
1052     changeSetting(QctSettings::NumberMatchLengthKey, 7);
1053
1054     {
1055         QContactManager::Error error = QContactManager::UnspecifiedError;
1056         const QContactList result = engine()->contacts(f, NoSortOrders, fetchHint<QContactPhoneNumber>(), &error);
1057         QCOMPARE(QContactManager::NoError, error);
1058
1059         int contactIndex = -1, otherIndex = -1;
1060
1061         for(int i = 0; i < result.count(); ++i) {
1062             if (result.at(i).localId() == savedContact.localId()) {
1063                 contactIndex = i;
1064                 continue;
1065             }
1066
1067             if (result.at(i).localId() == otherContact.localId()) {
1068                 otherIndex = i;
1069                 continue;
1070             }
1071         }
1072
1073         QVERIFY(contactIndex >= 0);
1074         QVERIFY(otherIndex >= 0 || otherPhoneNumber.isEmpty());
1075     }
1076 }
1077
1078 void
1079 ut_qtcontacts_trackerplugin::testSamePhoneNumber()
1080 {
1081     QContactPhoneNumber tel;
1082     tel.setNumber("099-8877563");
1083
1084     QList<QContactLocalId> savedIds;
1085
1086     // save two different contacts with identical phone number
1087     for(int i = 0; i < 2; ++i) {
1088         QContact contact;
1089         QVERIFY(contact.saveDetail(&tel));
1090
1091         QContactManager::Error error = QContactManager::UnspecifiedError;
1092         const bool contactSaved = engine()->saveContact(&contact, &error);
1093         QCOMPARE(error, QContactManager::NoError);
1094         QVERIFY(0 != contact.localId());
1095         QVERIFY(contactSaved);
1096
1097         savedIds += contact.localId();
1098     }
1099
1100     // check that both contacts really got saved and got that phone number
1101     QList<QContact> contactList = contacts(localIds());
1102     QCOMPARE(contactList.count(), localIds().count());
1103     QString detailUri;
1104
1105     foreach(const QContact &c, contactList) {
1106         QCOMPARE(c.details<QContactPhoneNumber>().count(), 1);
1107
1108         const QContactPhoneNumber detail = c.detail<QContactPhoneNumber>();
1109
1110         if (detailUri.isEmpty()) {
1111             detailUri = detail.detailUri();
1112         }
1113
1114         QCOMPARE(detail.number(), tel.number());
1115         QCOMPARE(detail.detailUri(), detailUri);
1116
1117         QVERIFY(not detail.detailUri().isEmpty());
1118     }
1119
1120     // check if we really can lookup both contacts by phone number.
1121     // both with exact and with fuzzy matching.
1122     for(int i = 0; i < 2; ++i) {
1123         QContactDetailFilter filter;
1124
1125         filter.setDetailDefinitionName(QContactPhoneNumber::DefinitionName,
1126                                        QContactPhoneNumber::FieldNumber);
1127         filter.setValue(tel.number());
1128
1129         if (1 == i) {
1130             filter.setMatchFlags(QContactFilter::MatchPhoneNumber);
1131         }
1132
1133         QContactManager::Error error = QContactManager::UnspecifiedError;
1134         QList<QContactLocalId> fetchedIds = engine()->contactIds(filter, NoSortOrders, &error);
1135         QCOMPARE(error, QContactManager::NoError);
1136
1137         foreach(QContactLocalId localId, savedIds) {
1138             QVERIFY(fetchedIds.contains(localId));
1139         }
1140     }
1141 }
1142
1143 void
1144 ut_qtcontacts_trackerplugin::testPhoneNumberContext()
1145 {
1146     QContact c;
1147     QContactPhoneNumber phone;
1148     phone.setContexts(QContactDetail::ContextHome);
1149     phone.setNumber("555-888");
1150     phone.setSubTypes(QContactPhoneNumber::SubTypeMobile);
1151     c.saveDetail(&phone);
1152     QContact contactToSave = c;
1153     // Let's do this all twice, first time save new detail, and next iteration change the context
1154     for (int iterations = 0; iterations < 2; iterations++) {
1155         QContactManager::Error error(QContactManager::UnspecifiedError);
1156         QVERIFY(engine()->saveContact(&contactToSave, &error));
1157         QCOMPARE(error, QContactManager::NoError);
1158         // wait for commit transaction to be done, no signals yet
1159         for(int i = 0; i < 100; i++) {
1160             usleep(10000);
1161             QCoreApplication::processEvents();
1162         }
1163
1164         QContactFetchRequest request;
1165         request.setFilter(localIdFilter(contactToSave.localId()));
1166         request.setFetchHint(fetchHint<QContactPhoneNumber>());
1167
1168         Slots slot;
1169         QObject::connect(&request, SIGNAL(resultsAvailable()),
1170                 &slot, SLOT(resultsAvailable()));
1171
1172         engine()->startRequest(&request);
1173
1174         engine()->waitForRequestFinishedImpl(&request, 1000);
1175
1176         // if it takes more, then something is wrong
1177         QVERIFY(request.isFinished());
1178         QVERIFY(!slot.contacts.isEmpty());
1179
1180         QContact contactToTest;
1181         foreach (QContact savedContact, slot.contacts) {
1182             if (savedContact.localId() == contactToSave.localId()) {
1183                 contactToTest = savedContact;
1184             }
1185         }
1186         QCOMPARE(contactToTest.localId(), contactToSave.localId()); // Just to be sure we got the saved contact
1187         QCOMPARE(contactToTest.details<QContactPhoneNumber>().count(), 1);
1188         if (0 == iterations) {
1189             // perform context change
1190             QContactPhoneNumber phoneToEdit = contactToTest.detail<QContactPhoneNumber>();
1191             phoneToEdit.setContexts(QContactDetail::ContextWork);
1192             contactToTest.saveDetail(&phoneToEdit);
1193             contactToSave = contactToTest;
1194         }
1195         QVERIFY(contactToTest.details<QContactPhoneNumber>().count() == 1);
1196     }
1197 }
1198
1199 void
1200 ut_qtcontacts_trackerplugin::testWritingOnlyWorkMobile()
1201 {
1202     QContact c;
1203     QContactPhoneNumber phone;
1204     phone.setContexts(QContactDetail::ContextWork);
1205     phone.setNumber("555999");
1206     phone.setSubTypes(QContactPhoneNumber::SubTypeMobile);
1207     c.saveDetail(&phone);
1208     QContact& contactToSave = c;
1209     QContactManager::Error error(QContactManager::UnspecifiedError);
1210     QVERIFY(engine()->saveContact(&contactToSave, &error));
1211     QCOMPARE(error, QContactManager::NoError);
1212     // wait for commit transaction to be done, no signals yet
1213     for(int i = 0; i < 100; i++) {
1214         usleep(10000);
1215         QCoreApplication::processEvents();
1216     }
1217
1218     QContactFetchRequest request;
1219     request.setFilter(localIdFilter(contactToSave.localId()));
1220     request.setFetchHint(fetchHint<QContactPhoneNumber>());
1221
1222     Slots slot;
1223     QObject::connect(&request, SIGNAL(resultsAvailable()),
1224             &slot, SLOT(resultsAvailable()));
1225
1226     engine()->startRequest(&request);
1227
1228     engine()->waitForRequestFinishedImpl(&request, 1000);
1229
1230     // if it takes more, then something is wrong
1231     QVERIFY(request.isFinished());
1232     QVERIFY(!slot.contacts.isEmpty());
1233
1234     QContact contactToTest;
1235     foreach (QContact savedContact, slot.contacts) {
1236         if (savedContact.localId() == c.localId()) {
1237             contactToTest = savedContact;
1238         }
1239     }
1240
1241     // add implicied subtypes for new fetch request
1242     phone.setSubTypes(phone.subTypes() <<
1243                       QContactPhoneNumber::SubTypeMessagingCapable <<
1244                       QContactPhoneNumber::SubTypeVoice);
1245
1246     QCOMPARE(contactToTest.localId(), c.localId()); // Just to be sure we got the saved contact
1247     QCOMPARE(contactToTest.details<QContactPhoneNumber>().count(), 1);
1248     QCOMPARE(contactToTest.detail<QContactPhoneNumber>().number(), phone.number());
1249     QCOMPARE(contactToTest.detail<QContactPhoneNumber>().subTypes().toSet(), phone.subTypes().toSet());
1250     QCOMPARE(contactToTest.detail<QContactPhoneNumber>().contexts(), phone.contexts());
1251 }
1252
1253 void
1254 ut_qtcontacts_trackerplugin::testSaveAddress()
1255 {
1256     QContact c;
1257     QContactName name;
1258     name.setFirstName("Aruba & Barbados");
1259     name.setLastName("Girl");
1260     c.saveDetail(&name);
1261     QContactLocalId initialId = c.localId();
1262     int detailsAdded = 0;
1263
1264     // List of pairs of field-value map and context
1265     typedef QMap<QString,QString> typeAddress;
1266     typedef QPair<typeAddress,QString> typeAddressWithContext;
1267     QList<typeAddressWithContext> addressValues;
1268
1269     // TODO check status of 137174 and other libqttracker1pre6 bugs before refactoring
1270     typeAddress values;
1271     values.insert(QLatin1String(QContactAddress::FieldCountry), "Barbados");
1272     values.insert(QLatin1String(QContactAddress::FieldPostcode), "55555");
1273     values.insert(QLatin1String(QContactAddress::FieldStreet), "Martindales Rd");
1274     values.insert(QLatin1String(QContactAddress::FieldExtendedAddress), "In the Front Garden");
1275     values.insert(QLatin1String(QContactAddress::FieldRegion), "Bridgetown");
1276     values.insert(QLatin1String(QContactAddress::FieldLocality), "Bridgetown town");
1277
1278
1279
1280     addressValues.append(typeAddressWithContext(values, QLatin1String(QContactDetail::ContextWork)));
1281     values.clear();
1282     values.insert(QLatin1String(QContactAddress::FieldCountry), "Aruba");
1283     values.insert(QLatin1String(QContactAddress::FieldPostcode), "44444");
1284     values.insert(QLatin1String(QContactAddress::FieldStreet), "Brazilie Straat");
1285     values.insert(QLatin1String(QContactAddress::FieldRegion), "Oranjestad");
1286     values.insert(QLatin1String(QContactAddress::FieldLocality), "Bridgetown town");
1287     values.insert(QLatin1String(QContactAddress::FieldPostOfficeBox), "00011102");
1288
1289     addressValues.append(typeAddressWithContext(values, QLatin1String(QContactDetail::ContextHome)));
1290     values.clear();
1291     values.insert(QLatin1String(QContactAddress::FieldCountry), "ArubaWork");
1292     values.insert(QLatin1String(QContactAddress::FieldPostcode), "44445");
1293     values.insert(QLatin1String(QContactAddress::FieldStreet), "Sunset Blvd");
1294     values.insert(QLatin1String(QContactAddress::FieldRegion), "Oranjestad");
1295     values.insert(QLatin1String(QContactAddress::FieldLocality), "Bridgetown town");
1296     values.insert(QLatin1String(QContactAddress::FieldPostOfficeBox), "00011103");
1297
1298     addressValues.append(typeAddressWithContext(values, QLatin1String(QContactDetail::ContextHome)));
1299     foreach (typeAddressWithContext addressWithContext, addressValues) {
1300         QContactAddress address;
1301         foreach (QString field, addressWithContext.first.keys()) {
1302             address.setValue(field, addressWithContext.first.value(field));
1303         }
1304         address.setContexts(addressWithContext.second);
1305         c.saveDetail(&address);
1306         detailsAdded++;
1307     }
1308
1309     QContactManager::Error error(QContactManager::UnspecifiedError);
1310     engine()->saveContact(&c, &error);
1311     QCOMPARE(error,  QContactManager::NoError);
1312     QVERIFY(c.localId() != initialId);
1313     error = QContactManager::UnspecifiedError;
1314     QContact contact = engine()->contactImpl(c.localId(), NoFetchHint, &error);
1315     QCOMPARE(error,  QContactManager::NoError);
1316     QList<QContactAddress> details = contact.details<QContactAddress>();
1317     QCOMPARE(details.count(), detailsAdded);
1318     bool found = false;
1319     // Test if inserted values are found in some of the details
1320     foreach (typeAddressWithContext addressWithContext, addressValues) {
1321         foreach (QContactAddress detail, details) {
1322             foreach (QString field, addressWithContext.first.keys()) {
1323                 found = (detail.value(field) == addressWithContext.first.value(field));
1324                 if (!found)
1325                     break;
1326             }
1327             if (found)
1328                 break;
1329         }
1330         QVERIFY2(found, "Inserted detail was not found in the fetched details");
1331     }
1332 }
1333
1334 // Related bug: NB#248183
1335 void
1336 ut_qtcontacts_trackerplugin::testExtendedAddress()
1337 {
1338     // Check parsing from vcard
1339     QList<QContact> contacts = parseVCards(referenceFileName("NB248183.vcf"));
1340     QCOMPARE(contacts.count(), 1);
1341
1342     QContact contact = contacts.first();
1343     QCOMPARE(contact.details<QContactAddress>().count(), 1);
1344
1345     const QString extendedAddress = contact.detail<QContactAddress>().extendedAddress();
1346     QCOMPARE(extendedAddress, QString::fromLatin1("Lawspet"));
1347
1348     // Check saving and fetching
1349     QContactManager::Error error = QContactManager::UnspecifiedError;
1350     bool contactSaved = engine()->saveContact(&contact, &error);
1351     QCOMPARE(error, QContactManager::NoError);
1352     QVERIFY(contactSaved);
1353
1354     QVERIFY(0 != contact.localId());
1355
1356     error = QContactManager::UnspecifiedError;
1357     QContact fetchedContact = engine()->contactImpl(contact.localId(), fetchHint<QContactAddress>(), &error);
1358     QCOMPARE(error, QContactManager::NoError);
1359
1360     QCOMPARE(fetchedContact.localId(), contact.localId());
1361     QCOMPARE(fetchedContact.details<QContactAddress>().count(), 1);
1362     QCOMPARE(fetchedContact.detail<QContactAddress>().extendedAddress(), extendedAddress);
1363 }
1364
1365 void
1366 ut_qtcontacts_trackerplugin::testSaveOrganization()
1367 {
1368     QList<PairOfStrings> samples;
1369
1370     samples << PairOfStrings(QContactOrganization::FieldName, "Nokia");
1371     samples << PairOfStrings(QContactOrganization::FieldLogoUrl, "nokia.jpg");
1372     samples << PairOfStrings(QContactOrganization::FieldDepartment, "Meego R&D");
1373     samples << PairOfStrings(QContactOrganization::FieldLocation, "Helsinki");
1374     samples << PairOfStrings(QContactOrganization::FieldRole, "Developer");
1375     samples << PairOfStrings(QContactOrganization::FieldTitle, "Code Guru");
1376
1377     QContact contact;
1378
1379     for(int i = 0; i < samples.count(); ++i) {
1380         QContactOrganization org = contact.detail<QContactOrganization>();
1381         org.setValue(samples[i].first, samples[i].second);
1382         contact.saveDetail(&org);
1383
1384         QContactManager::Error error = QContactManager::UnspecifiedError;
1385         bool success = engine()->saveContact(&contact, &error);
1386         QCOMPARE(error,  QContactManager::NoError);
1387         QVERIFY(0 != contact.localId());
1388         QVERIFY(success);
1389
1390         error = QContactManager::UnspecifiedError;
1391         const QContactLocalId contactLocalId = contact.localId();
1392         contact = engine()->contactImpl(contact.localId(), NoFetchHint, &error);
1393         QCOMPARE(contact.localId(), contactLocalId);
1394         QCOMPARE(error,  QContactManager::NoError);
1395
1396         org = contact.detail<QContactOrganization>();
1397
1398         for(int j = 0; j <= i; ++j) {
1399             QCOMPARE(org.value(samples[j].first), samples[j].second);
1400         }
1401     }
1402 }
1403
1404 void
1405 ut_qtcontacts_trackerplugin::testSaveEmailAddress()
1406 {
1407     QContact c;
1408     QContactLocalId initialId = c.localId();
1409     int detailsAdded = 0;
1410
1411     QMap<QString,QString> values;
1412     values.insert("john.does@hotmail.com", QContactDetail::ContextHome);
1413     values.insert("john.doe@gmail.com", QContactDetail::ContextWork);
1414     values.insert("john.doe@nokia.com", QContactDetail::ContextWork);
1415     values.insert("john.doe@johndoe.com", QContactDetail::ContextHome);
1416     foreach(QString address, values.keys()) {
1417         QContactEmailAddress emailAddress;
1418         emailAddress.setEmailAddress(address);
1419         emailAddress.setContexts(values.value(address));
1420         c.saveDetail(&emailAddress);
1421         detailsAdded++;
1422     }
1423     QContactName name;
1424     name.setFirstName("Jo");
1425     name.setLastName("H N Doe");
1426     c.saveDetail(&name);
1427
1428     QContactManager::Error error = QContactManager::UnspecifiedError;
1429     bool contactSaved = engine()->saveContact(&c, &error);
1430     QCOMPARE(error,  QContactManager::NoError);
1431     QVERIFY(c.localId() != initialId);
1432     QVERIFY(contactSaved);
1433
1434     error = QContactManager::UnspecifiedError;
1435     QContact contact = engine()->contactImpl(c.localId(), NoFetchHint, &error);
1436     QCOMPARE(error,  QContactManager::NoError);
1437     QList<QContactEmailAddress> details = contact.details<QContactEmailAddress>();
1438     QCOMPARE(details.count(), detailsAdded);
1439     foreach (QContactEmailAddress detail, details) {
1440         QString address = detail.value(QContactEmailAddress::FieldEmailAddress);
1441         QVERIFY(values.contains(address));
1442         QCOMPARE(detail.contexts()[0], values.value(address));
1443     }
1444 }
1445
1446 void
1447 ut_qtcontacts_trackerplugin::testSaveCustomValues()
1448 {
1449     // create contact with custom subtypes
1450     QContactName name;
1451     name.setFirstName("Teppo");
1452     name.setLastName("Virtanen");
1453
1454     QContactPhoneNumber savedNumber;
1455     savedNumber.setNumber("+23234234");
1456     savedNumber.setSubTypes(QStringList() <<
1457                             QContactPhoneNumber::SubTypeVoice <<
1458                             "CrazyA" << "CrazyB" << "CrazyC");
1459
1460     QContactOnlineAccount savedAccount;
1461     savedAccount.setAccountUri("jepa@account.org");
1462     savedAccount.setValue(QContactOnlineAccount__FieldAccountPath, "/org/freedesktop/testSaveCustomValues/account");
1463     savedAccount.setContexts(QContactDetail::ContextHome);
1464     savedAccount.setSubTypes(QStringList() << "FunkyA" << "FunkyB" << "FunkyC");
1465
1466     QContact savedContact;
1467     savedContact.saveDetail(&name);
1468     savedContact.saveDetail(&savedNumber);
1469     savedContact.saveDetail(&savedAccount);
1470
1471     // save the test contact
1472     QContactManager::Error error = QContactManager::UnspecifiedError;
1473     bool contactSaved = engine()->saveContact(&savedContact, &error);
1474     QCOMPARE(error,  QContactManager::NoError);
1475     QVERIFY(0 != savedContact.localId());
1476     QVERIFY(contactSaved);
1477
1478     // restore the contact
1479     QContactFetchHint fetchHint;
1480     fetchHint.setDetailDefinitionsHint(QStringList() <<
1481                                        QContactPhoneNumber::DefinitionName <<
1482                                        QContactOnlineAccount::DefinitionName);
1483
1484     error = QContactManager::UnspecifiedError;
1485     QContact fetchedContact = engine()->contactImpl(savedContact.localId(), fetchHint, &error);
1486
1487     QCOMPARE(error,  QContactManager::NoError);
1488     QCOMPARE(fetchedContact.localId(), savedContact.localId());
1489
1490     // compare the fetched contact with saved contact
1491     const QList<QContactPhoneNumber> fetchedNumbers =
1492             fetchedContact.details<QContactPhoneNumber>();
1493
1494     QCOMPARE(fetchedNumbers.count(), 1);
1495     QCOMPARE(fetchedNumbers.first().number(), savedNumber.number());
1496     QCOMPARE(fetchedNumbers.first().subTypes().count(), savedNumber.subTypes().count());
1497     QCOMPARE(fetchedNumbers.first().subTypes().toSet(), savedNumber.subTypes().toSet());
1498
1499     const QList<QContactOnlineAccount> fetchedAccounts =
1500             fetchedContact.details<QContactOnlineAccount>();
1501
1502     QCOMPARE(fetchedAccounts.count(), 1);
1503     QCOMPARE(fetchedAccounts.first().accountUri(), savedAccount.accountUri());
1504     QCOMPARE(fetchedAccounts.first().contexts().count(), savedAccount.contexts().count());
1505     QCOMPARE(fetchedAccounts.first().contexts().toSet(), savedAccount.contexts().toSet());
1506     QCOMPARE(fetchedAccounts.first().subTypes().count(), savedAccount.subTypes().count());
1507     QCOMPARE(fetchedAccounts.first().subTypes().toSet(), savedAccount.subTypes().toSet());
1508 }
1509
1510 void
1511 ut_qtcontacts_trackerplugin::testRemoveContact()
1512 {
1513     QContact c;
1514     QContactPhoneNumber phone;
1515     phone.setNumber("+358501234567");
1516     c.saveDetail(&phone);
1517     QContactEmailAddress email;
1518     email.setEmailAddress("super.man@hotmail.com");
1519     c.saveDetail(&email);
1520     QContactName name;
1521     name.setFirstName("Super");
1522     name.setLastName("Man");
1523     c.saveDetail(&name);
1524
1525     QContactManager::Error error(QContactManager::UnspecifiedError);
1526     QVERIFY(engine()->saveContact(&c, &error));
1527     QCOMPARE(error,  QContactManager::NoError);
1528
1529     error = QContactManager::UnspecifiedError;
1530     QVERIFY2(engine()->removeContact(c.localId(), &error), "Removing a contact failed");
1531     QCOMPARE(error, QContactManager::NoError);
1532
1533     QVERIFY2(engine()->contactImpl(c.localId(), NoFetchHint, &error) == QContact(),
1534              "Found a contact, which should have been removed");
1535 }
1536
1537 void
1538 ut_qtcontacts_trackerplugin::testRemoveSelfContact()
1539 {
1540     QContactManager::Error error(QContactManager::UnspecifiedError);
1541     QList<QContactLocalId> idsToRemove;
1542
1543     for (int i = 0; i < 3; ++i) {
1544         QContact contact;
1545         QContactName contactName;
1546         contactName.setFirstName(QString::fromLatin1("%1 %2").arg(Q_FUNC_INFO).arg(i));
1547         contact.saveDetail(&contactName);
1548
1549         QVERIFY(engine()->saveContact(&contact, &error));
1550         QCOMPARE(error, QContactManager::NoError);
1551         idsToRemove.append(contact.localId());
1552     }
1553
1554     error = QContactManager::UnspecifiedError;
1555     QContactLocalId selfId = engine()->selfContactId(&error);
1556     QCOMPARE(error, QContactManager::NoError);
1557
1558     idsToRemove.append(selfId);
1559
1560     QContactRemoveRequest request;
1561     request.setContactIds(idsToRemove);
1562
1563     QVERIFY(engine()->startRequest(&request));
1564     QVERIFY(engine()->waitForRequestFinishedImpl(&request, 2000));
1565     QCOMPARE(request.error(), QContactManager::PermissionsError);
1566     QCOMPARE(request.errorMap().size(), 1);
1567     QCOMPARE(request.errorMap().value(3, QContactManager::NoError), QContactManager::PermissionsError);
1568
1569     idsToRemove.removeLast();
1570
1571     // Rest of the contacts should have been deleted properly
1572     foreach (QContactLocalId id, idsToRemove) {
1573         error = QContactManager::UnspecifiedError;
1574         QContact contact(engine()->contact(id, QContactFetchHint(), &error));
1575         QCOMPARE(error, QContactManager::DoesNotExistError);
1576     }
1577
1578     // test to only remove self contact
1579     error = QContactManager::UnspecifiedError;
1580     QVERIFY(not engine()->removeContact(selfId, &error));
1581     QCOMPARE(error, QContactManager::PermissionsError);
1582 }
1583
1584 void
1585 ut_qtcontacts_trackerplugin::testSaveContacts()
1586 {
1587     QList<QContact> contacts;
1588     QStringList fixedGuids;
1589
1590     for (int i = 0; i < 3; i++) {
1591         QContact c;
1592
1593         QContactName name;
1594         name.setFirstName("John");
1595         name.setLastName(QString::number(i));
1596         QVERIFY(c.saveDetail(&name));
1597
1598         // skip first contact to test GUID detail auto-creation
1599         fixedGuids += (i > 0 ? QUuid::createUuid().toString() : QString());
1600
1601         if (not fixedGuids.last().isEmpty()) {
1602             QContactGuid uid;
1603             uid.setGuid(fixedGuids.last());
1604             QVERIFY(c.saveDetail(&uid));
1605         }
1606
1607         QContactGender gender;
1608
1609         switch(i % 3) {
1610         case 0:
1611             gender.setGender(QContactGender::GenderMale);
1612             break;
1613         case 1:
1614             gender.setGender(QContactGender::GenderFemale);
1615             break;
1616         default:
1617             gender.setGender(QContactGender::GenderUnspecified);
1618             break;
1619         }
1620
1621         QVERIFY(c.saveDetail(&gender));
1622         contacts.append(c);
1623
1624         QVERIFY(c.displayLabel().isEmpty());
1625     }
1626
1627     QMap<int, QContactManager::Error> errorMap;
1628     QContactManager::Error error = QContactManager::UnspecifiedError;
1629     const QDateTime earlier = QDateTime::currentDateTime().addSecs(-1);
1630
1631     changeSetting(QctSettings::NameOrderKey, QContactDisplayLabel__FieldOrderFirstName);
1632     const bool contactsSaved = engine()->saveContacts(&contacts, &errorMap, &error);
1633
1634     QCOMPARE(error, QContactManager::NoError);
1635     QVERIFY(contactsSaved);
1636
1637     for (int i = 0; i < contacts.count(); i++) {
1638         QVERIFY(contacts[i].localId() != 0);
1639         QCOMPARE(contacts[i].displayLabel(), QString::fromLatin1("John %1").arg(i));
1640
1641         error = QContactManager::UnspecifiedError;
1642
1643         const QContact contact = engine()->contactImpl(contacts[i].localId(), NoFetchHint, &error);
1644         QCOMPARE(error,  QContactManager::NoError);
1645         QCOMPARE(contact.localId(), contacts[i].localId());
1646
1647         const QList<QContactName> details = contact.details<QContactName>();
1648         QCOMPARE(details.count(), 1);
1649         QCOMPARE(details.first().lastName(), QString("%1").arg(QString::number(i,10)));
1650
1651         const QList<QContactGender> genders = contact.details<QContactGender>();
1652         QCOMPARE(genders.count(), 1);
1653         QCOMPARE(genders.first().gender(),contacts[i].detail<QContactGender>().gender());
1654
1655         const QList<QContactGuid> guids = contact.details<QContactGuid>();
1656         QCOMPARE(guids.count(), 1);
1657
1658         if (fixedGuids.at(i).isEmpty()) {
1659             QVERIFY(not guids.first().guid().isEmpty());
1660         } else {
1661             QCOMPARE(guids.first().guid(), fixedGuids.at(i));
1662         }
1663
1664         const QList<QContactTimestamp> timestamps = contact.details<QContactTimestamp>();
1665
1666         QCOMPARE(timestamps.count(), 1);
1667         QVERIFY(not timestamps.first().lastModified().isNull());
1668         QVERIFY(timestamps.first().lastModified() >= earlier);
1669         QVERIFY(not timestamps.first().created().isNull());
1670         QVERIFY(timestamps.first().created() >= earlier);
1671     }
1672
1673     // save contacts again to check if timestamps get updated
1674     sleep(1);
1675
1676     const QDateTime later = QDateTime::currentDateTime().addSecs(-1);
1677     QVERIFY(later > earlier);
1678
1679     error = QContactManager::UnspecifiedError;
1680     engine()->saveContacts(&contacts, &errorMap, &error);
1681     QCOMPARE(error, QContactManager::NoError);
1682
1683     for (int i = 0; i < contacts.count(); i++) {
1684         QVERIFY(contacts[i].localId() != 0);
1685         error = QContactManager::UnspecifiedError;
1686         QContact contact = engine()->contactImpl(contacts[i].localId(), NoFetchHint, &error);
1687         QCOMPARE(error,  QContactManager::NoError);
1688         QCOMPARE(contact.localId(), contacts[i].localId());
1689
1690         const QList<QContactTimestamp> timestamps = contact.details<QContactTimestamp>();
1691         QCOMPARE(timestamps.count(), 1);
1692
1693         QVERIFY(not timestamps.first().lastModified().isNull());
1694         QVERIFY(timestamps.first().lastModified() >= later);
1695
1696         QVERIFY(not timestamps.first().created().isNull());
1697         QVERIFY(timestamps.first().created() >= earlier);
1698         QVERIFY(timestamps.first().created() < later);
1699     }
1700 }
1701
1702 void
1703 ut_qtcontacts_trackerplugin::testRemoveContacts()
1704 {
1705     QList<QContactLocalId> addedIds;
1706     for (int i = 0; i < 5; i++) {
1707         QContact c;
1708         QContactName name;
1709         name.setFirstName(QString("John%1").arg(QString::number(i,10)));
1710         c.saveDetail(&name);
1711
1712         QContactManager::Error error(QContactManager::UnspecifiedError);
1713         QVERIFY(engine()->saveContact(&c, &error));
1714         QCOMPARE(error,  QContactManager::NoError);
1715
1716         addedIds.append(c.localId());
1717     }
1718     QList<QContactLocalId> toApiRemove;
1719     toApiRemove.append(addedIds.takeLast());
1720     toApiRemove.append(addedIds.takeLast());
1721     QList<QContactLocalId> toPluginRemove(addedIds);
1722
1723     // Remove all, but last of the added contacts
1724     QMap<int, QContactManager::Error> errorMap;
1725     QContactManager::Error error(QContactManager::UnspecifiedError);
1726     QVERIFY(engine()->removeContacts(toPluginRemove, &errorMap, &error));
1727     QCOMPARE(error,  QContactManager::NoError);
1728     for (int i = 0; i < errorMap.count(); i++) {
1729         QCOMPARE(toPluginRemove[i], 0U);
1730     }
1731
1732     error = QContactManager::UnspecifiedError;
1733     QVERIFY(engine()->removeContacts(toApiRemove, &errorMap, &error));
1734     QCOMPARE(error, QContactManager::NoError);
1735     for (int i = 0; i < errorMap.count(); i++) {
1736         QCOMPARE(toApiRemove[i], 0U);
1737     }
1738 }
1739
1740 static bool
1741 haveAvatarPath(const QList<QContactAvatar> &avatars, const QString &filename)
1742 {
1743     foreach(const QContactAvatar &a, avatars) {
1744         if (a.imageUrl().toLocalFile() == filename) {
1745             return true;
1746         }
1747     }
1748
1749     return false;
1750 }
1751
1752 void
1753 ut_qtcontacts_trackerplugin::testAvatar()
1754 {
1755     const uint baseId = qAbs(qrand());
1756
1757     const QString accountId1 = QString::fromLatin1("test-%1@ovi.com").arg(baseId);
1758     const QString accountId2 = QString::fromLatin1("test-%1@ovi.com").arg(baseId + 1);
1759     const QString accountId3 = QString::fromLatin1("test-%1@ovi.com").arg(baseId + 2);
1760     const QString accountPath1 = QString::fromLatin1("/org/freedesktop/testAvatar/account/%1").arg(baseId);
1761     const QString accountPath2 = QString::fromLatin1("/org/freedesktop/testAvatar/account/%1").arg(baseId + 1);
1762     const QString accountPath3 = QString::fromLatin1("/org/freedesktop/testAvatar/account/%1").arg(baseId + 2);
1763     const QString contactIri = QString::fromLatin1("contact:testAvatar:%1").arg(baseId);
1764
1765     const QString serviceProvider = QLatin1String("ovi.com");
1766     const QString protocol = QLatin1String("jabber");
1767
1768     const QContactLocalId contactId1 = insertIMContact(contactIri, accountId1,
1769                                                        QLatin1String("nco:presence-status-available"),
1770                                                        QLatin1String("In Helsinki"), accountPath1,
1771                                                        protocol, serviceProvider);
1772     const QContactLocalId contactId2 = insertIMContact(contactIri, accountId2,
1773                                                        QLatin1String("nco:presence-status-busy"),
1774                                                        QLatin1String("In Brisbane"), accountPath2,
1775                                                        protocol, serviceProvider);
1776     const QContactLocalId contactId3 = insertIMContact(contactIri, accountId3,
1777                                                        QLatin1String("nco:presence-status-available"),
1778                                                        QLatin1String("In Berlin"), accountPath3,
1779                                                        protocol, serviceProvider);
1780
1781     QVERIFY(contactId1 != 0);
1782     QCOMPARE(contactId2, contactId1);
1783     QCOMPARE(contactId3, contactId1);
1784
1785     QContactAvatar personalAvatar;
1786     QList<QContactAvatar> avatars;
1787     QContact contactWithAvatar = contact(contactId1, QStringList());
1788     avatars = contactWithAvatar.details<QContactAvatar>();
1789     QCOMPARE(avatars.size(), 3);
1790
1791     QContactName nameDetail = contactWithAvatar.detail<QContactName>();
1792     nameDetail.setCustomLabel(__func__);
1793
1794     QVERIFY(contactWithAvatar.saveDetail(&nameDetail));
1795
1796     QContactManager::Error error = QContactManager::UnspecifiedError;
1797     QVERIFY(engine()->saveContact(&contactWithAvatar, &error));
1798     QCOMPARE(error,  QContactManager::NoError);
1799     QVERIFY(0 != contactWithAvatar.localId());
1800
1801     contactWithAvatar = contact(contactId1, QStringList());
1802     avatars = contactWithAvatar.details<QContactAvatar>();
1803
1804     QCOMPARE(avatars.size(), 3);
1805
1806     QVERIFY(haveAvatarPath(avatars, onlineAvatarPath(accountPath1)));
1807     QVERIFY(haveAvatarPath(avatars, onlineAvatarPath(accountPath2)));
1808
1809     QCOMPARE(avatars[2].imageUrl().toLocalFile(), onlineAvatarPath(accountPath2));
1810
1811     personalAvatar.setImageUrl(QUrl("file:///home/user/.contacts/avatars/default_avatar.png"));
1812
1813     contactWithAvatar.saveDetail(&personalAvatar);
1814
1815     error = QContactManager::UnspecifiedError;
1816     QVERIFY(engine()->saveContact( &contactWithAvatar, &error));
1817     QCOMPARE(error,  QContactManager::NoError);
1818
1819     error = QContactManager::UnspecifiedError;
1820     QContact c = engine()->contactImpl(contactWithAvatar.localId(), NoFetchHint, &error);
1821     QCOMPARE(error,  QContactManager::NoError);
1822
1823     avatars = c.details<QContactAvatar>();
1824
1825     QCOMPARE(avatars.size(), 4);
1826
1827     QCOMPARE(avatars[0].imageUrl(), personalAvatar.imageUrl());
1828     QCOMPARE(avatars[0].linkedDetailUris(), QStringList());
1829
1830     QVERIFY(haveAvatarPath(avatars, onlineAvatarPath(accountPath1)));
1831     QVERIFY(haveAvatarPath(avatars, onlineAvatarPath(accountPath2)));
1832
1833     QCOMPARE(avatars[3].imageUrl().toLocalFile(), onlineAvatarPath(accountPath2));
1834 }
1835
1836 void
1837 ut_qtcontacts_trackerplugin::testOnlineAvatar_data()
1838 {
1839     QTest::addColumn<QString>("context");
1840
1841     QTest::newRow("none") << QString();
1842     QTest::newRow("home") << QContactDetail::ContextHome.latin1();
1843     QTest::newRow("work") << QContactDetail::ContextWork.latin1();
1844     QTest::newRow("other") << QContactDetail::ContextOther.latin1();
1845 }
1846
1847 void
1848 ut_qtcontacts_trackerplugin::testOnlineAvatar()
1849 {
1850     QFETCH(QString, context);
1851
1852     const QString avatarUri = "file:///home/user/.cache/avatars/a888d5a6-2434-480a-8798-23875437bcf3";
1853     const QString accountPath = "/org/freedesktop/fake/account";
1854     const QString accountUri = "first.last@talk.com";
1855
1856     QContactOnlineAccount account;
1857     account.setValue(QContactOnlineAccount::FieldAccountUri, accountUri);
1858     account.setValue(QContactOnlineAccount__FieldAccountPath, accountPath);
1859     account.setDetailUri(makeTelepathyIri(accountPath, accountUri));
1860
1861     if (not context.isEmpty()) {
1862         account.setContexts(context);
1863     }
1864
1865     QContact savedContact;
1866     QVERIFY(savedContact.saveDetail(&account));
1867
1868     QContactManager::Error error = QContactManager::UnspecifiedError;
1869     QVERIFY(engine()->saveContact(&savedContact, &error));
1870     QCOMPARE(error, QContactManager::NoError);
1871     QVERIFY(0 != savedContact.localId());
1872
1873     error = QContactManager::UnspecifiedError;
1874     QContact fetchedContact = engine()->contactImpl(savedContact.localId(),
1875                                                     NoFetchHint, &error);
1876     QCOMPARE(fetchedContact.localId(), savedContact.localId());
1877     QCOMPARE(error, QContactManager::NoError);
1878
1879     QStringList expectedContexts = account.contexts();
1880
1881     const QList<QContactOnlineAccount> fetchedAccounts = fetchedContact.details<QContactOnlineAccount>();
1882     const QList<QContactAvatar> fetchedAvatars = fetchedContact.details<QContactAvatar>();
1883
1884     QCOMPARE(fetchedAccounts.count(), 1);
1885     QCOMPARE(fetchedAccounts.first().detailUri(), account.detailUri());
1886     QCOMPARE(fetchedAccounts.first().contexts(), expectedContexts);
1887
1888     QCOMPARE(fetchedAvatars.count(), 1);
1889     QCOMPARE(fetchedAvatars.first().contexts(), QStringList());
1890     QCOMPARE(fetchedAvatars.first().imageUrl().toString(), avatarUri);
1891     QCOMPARE(fetchedAvatars.first().linkedDetailUris(), QStringList(account.detailUri()));
1892 }
1893
1894 void
1895 ut_qtcontacts_trackerplugin::testAvatarTypes_data()
1896 {
1897     const QString testContactIri = QLatin1String("contact:avatar-types");
1898     const QStringList contactIris = loadRawContacts(QLatin1String("avatars.ttl"));
1899     QVERIFY(contactIris.contains(testContactIri));
1900
1901     QctTrackerIdResolver resolver(QStringList() << testContactIri);
1902     QVERIFY(resolver.lookupAndWait());
1903
1904     QContactLocalId testContactId = resolver.trackerIds().first();
1905     QVERIFY(testContactId != 0);
1906
1907     QTest::addColumn<QString>("avatarTypes");
1908     QTest::addColumn<QString>("personalAvatarIri");
1909     QTest::addColumn<QString>("onlineAvatarIri");
1910     QTest::addColumn<QString>("socialAvatarIri");
1911     QTest::addColumn<uint>("contactLocalId");
1912
1913     const QString personalAvatarIri = QLatin1String("file://home/user/.cache/avatars/default/sunshine.jpg");
1914     const QString onlineAvatarIri = QLatin1String("file://home/user/.cache/avatars/square/snowball.jpg");
1915     const QString socialAvatarIri = QLatin1String("file://home/user/.cache/avatars/large/snowball.jpg");
1916
1917     QTest::newRow("default")
1918             << QString::fromLatin1("")
1919             << personalAvatarIri << onlineAvatarIri << QString()
1920             << testContactId;
1921
1922     QTest::newRow("personal")
1923             << QString::fromLatin1("personal")
1924             << personalAvatarIri << QString() << QString()
1925             << testContactId;
1926     QTest::newRow("online")
1927             << QString::fromLatin1("online")
1928             << QString() << onlineAvatarIri << QString()
1929             << testContactId;
1930     QTest::newRow("social")
1931             << QString::fromLatin1("social")
1932             << QString() << QString() << socialAvatarIri
1933             << testContactId;
1934
1935     QTest::newRow("personal,online,social")
1936             << QString::fromLatin1("personal,online")
1937             << personalAvatarIri << onlineAvatarIri << QString()
1938             << testContactId;
1939     QTest::newRow("online,social")
1940             << QString::fromLatin1("online,social")
1941             << QString() << onlineAvatarIri << socialAvatarIri
1942             << testContactId;
1943
1944     QTest::newRow("all")
1945             << QString::fromLatin1("all")
1946             << personalAvatarIri << onlineAvatarIri << socialAvatarIri
1947             << testContactId;
1948 }
1949
1950 void
1951 ut_qtcontacts_trackerplugin::testAvatarTypes()
1952 {
1953     QFETCH(QString, avatarTypes);
1954     QFETCH(QString, personalAvatarIri);
1955     QFETCH(QString, onlineAvatarIri);
1956     QFETCH(QString, socialAvatarIri);
1957     QFETCH(uint, contactLocalId);
1958
1959     QMap<QString, QString> params = makeEngineParams();
1960
1961     if (avatarTypes.isEmpty()) {
1962         params.remove(QLatin1String("avatar-types"));
1963     } else {
1964         params.insert(QLatin1String("avatar-types"), avatarTypes);
1965     }
1966
1967     QScopedPointer<QContactManager> cm(new QContactManager(QLatin1String("tracker"), params));
1968     const QContact contact = cm->contact(contactLocalId);
1969     QCOMPARE(QContactManager::NoError, cm->error());
1970     QCOMPARE(contact.localId(), contactLocalId);
1971
1972     bool personalAvatarFound = false;
1973     bool onlineAvatarFound = false;
1974     bool socialAvatarFound = false;
1975
1976     foreach(const QContactAvatar &avatar, contact.details<QContactAvatar>()) {
1977         if (avatar.imageUrl() == personalAvatarIri) {
1978             QVERIFY(not personalAvatarFound);
1979             personalAvatarFound = true;
1980             continue;
1981         }
1982
1983         if (avatar.imageUrl() == onlineAvatarIri) {
1984             QVERIFY(not onlineAvatarFound);
1985             onlineAvatarFound = true;
1986             continue;
1987         }
1988
1989         if (avatar.imageUrl() == socialAvatarIri) {
1990             QVERIFY(not socialAvatarFound);
1991             socialAvatarFound = true;
1992             continue;
1993         }
1994
1995         QFAIL(QLatin1String("Unexpected avatar: ") + avatar.imageUrl().toString());
1996     }
1997
1998     QVERIFY(personalAvatarIri.isEmpty() || personalAvatarFound);
1999     QVERIFY(onlineAvatarIri.isEmpty() || onlineAvatarFound);
2000     QVERIFY(socialAvatarIri.isEmpty() || socialAvatarFound);
2001 }
2002
2003 void
2004 ut_qtcontacts_trackerplugin::testDateDetail_data()
2005 {
2006     QTest::addColumn<QString>("definitionName");
2007     QTest::addColumn<QString>("fieldName");
2008
2009     QTest::newRow("QContactBirthday::Birthday") << QString(QContactBirthday::DefinitionName.latin1())
2010                                                 << QString(QContactBirthday::FieldBirthday.latin1());
2011     QTest::newRow("QContactAnniversary::OriginalDate") << QString(QContactAnniversary::DefinitionName.latin1())
2012                                                        << QString(QContactAnniversary::FieldOriginalDate.latin1());
2013     QTest::newRow("Made up detail") << QString("MadeUpDetail")
2014                                     << QString("Date");
2015 }
2016
2017 void
2018 ut_qtcontacts_trackerplugin::testDateDetail()
2019 {
2020     static QStringList foreignTimeZoneDates;
2021
2022     foreignTimeZoneDates
2023         << QLatin1String("1917-02-12T00:00:00+05:54")
2024         << QLatin1String("2010-01-01T00:00:00+05:30")
2025         << QLatin1String("2010-01-01T00:00:00-05:00");
2026
2027     QFETCH(QString, definitionName);
2028     QFETCH(QString, fieldName);
2029
2030     QContact c;
2031     QDate date;
2032     QDateTime dateTime;
2033
2034     QContactName name;
2035     name.setFirstName("test");
2036     name.setLastName(definitionName);
2037     c.saveDetail(&name);
2038
2039     date = QDate::fromString("1960-03-24", Qt::ISODate);
2040     dateTime.setDate(date);
2041     QContactDetail detail = QContactDetail(definitionName);
2042     detail.setValue(fieldName, date);
2043     c.saveDetail(&detail);
2044
2045     QContactManager::Error error = QContactManager::UnspecifiedError;
2046     QVERIFY(engine()->saveContact(&c, &error));
2047     QCOMPARE(error, QContactManager::NoError);
2048
2049     QContactDetail savedDetail = contact(c.localId()).detail(definitionName);
2050     QVERIFY(not savedDetail.isEmpty());
2051
2052     QCOMPARE(savedDetail.variantValue(fieldName).toDate(), date);
2053     QCOMPARE(savedDetail.variantValue(fieldName).toDateTime().toLocalTime(), dateTime);
2054
2055     foreach (const QString date, foreignTimeZoneDates) {
2056         QString query;
2057
2058         if (definitionName == QContactBirthday::DefinitionName) {
2059             static const QString queryTemplate = QLatin1String
2060                     ("INSERT OR REPLACE { ?c nco:birthDate '%1' }"
2061                      "WHERE { ?c a nco:PersonContact . FILTER (tracker:id(?c)=%2) }");
2062             query = queryTemplate.arg(date).arg(c.localId());
2063         } else if (definitionName == QContactAnniversary::DefinitionName) {
2064             static const QString queryTemplate = QLatin1String
2065                     ("INSERT OR REPLACE { ?e ncal:dateTime \"%1\" }"
2066                      "WHERE { ?c a nco:PersonContact; ncal:anniversary [ ncal:dtstart ?e ] . "
2067                      "        FILTER(tracker:id(?c)=%2) }");
2068             query = queryTemplate.arg(date).arg(c.localId());
2069         } else if (definitionName == QLatin1String("MadeUpDetail")) {
2070             static const QString queryTemplate = QLatin1String
2071                     ("INSERT OR REPLACE { ?p nao:propertyValue \"%1\" }"
2072                      "WHERE { ?c a nco:PersonContact; "
2073                      "           nao:hasProperty [ nao:propertyName \"%3\"; nao:hasProperty ?p ] ."
2074                      "        ?p nao:propertyName \"Date\" . "
2075                      "        FILTER(tracker:id(?c)=%2) }");
2076             query = queryTemplate.arg(date).arg(c.localId()).arg(definitionName);
2077         } else {
2078             QFAIL("Unknown detail definition");
2079         }
2080
2081         QScopedPointer<QSparqlResult> result(executeQuery(query, QSparqlQuery::InsertStatement));
2082
2083         QVERIFY(not result.isNull());
2084
2085         savedDetail = contact(c.localId()).detail(definitionName);
2086         QVERIFY(not savedDetail.isEmpty());
2087         QCOMPARE(savedDetail.value(fieldName), date);
2088     }
2089 }
2090
2091 template<class T> static bool
2092 listContainsDetail(const QList<T> &list, const T &detail)
2093 {
2094     foreach (const T &d, list) {
2095         if (compareFuzzedDetail(detail, d)) {
2096             return true;
2097         }
2098     }
2099
2100     return false;
2101 }
2102
2103 void
2104 ut_qtcontacts_trackerplugin::testOrganization()
2105 {
2106     // Company information
2107     QContact contactWithCompany1, contactWithoutCompany;
2108     QContactOrganization company1;
2109     company1.setName("Nokia");
2110     company1.setDepartment(QStringList() << "Mobile");
2111     company1.setTitle("First Bug Hunter");
2112     company1.setRole("Developer");
2113     QContactOrganization company2;
2114     company2.setName("ANPE");
2115     company2.setDepartment(QStringList() << "Jobless");
2116     company2.setTitle("The hopeless case");
2117     company2.setRole("Jobseeker");
2118     QContactName name1, name2;
2119     name1.setFirstName("John");
2120     name1.setLastName("TestCompany1");
2121     name2.setFirstName("Frankie");
2122     name2.setLastName("Flowers");
2123     contactWithCompany1.saveDetail(&name1);
2124     contactWithCompany1.saveDetail(&company1);
2125     contactWithCompany1.saveDetail(&company2);
2126     contactWithoutCompany.saveDetail(&name2);
2127     QContactManager::Error error(QContactManager::UnspecifiedError);
2128     QVERIFY(engine()->saveContact(&contactWithCompany1, &error));
2129     QCOMPARE(error,  QContactManager::NoError);
2130     QVERIFY(engine()->saveContact(&contactWithoutCompany, &error));
2131     QCOMPARE(error,  QContactManager::NoError);
2132
2133     QContactLocalId id1 = contactWithCompany1.localId();
2134     const QList<QContactOrganization> organizations = contact(id1).details<QContactOrganization>();
2135     QCOMPARE(organizations.size(), 2);
2136     QVERIFY(listContainsDetail<QContactOrganization>(organizations, company1));
2137     QVERIFY(listContainsDetail<QContactOrganization>(organizations, company2));
2138
2139     // NB#192947: Ensure there is no organization in that contact
2140     QContactLocalId id2 = contactWithoutCompany.localId();
2141     QCOMPARE(contact(id2).details<QContactOrganization>().size(), 0);
2142 }
2143
2144 void
2145 ut_qtcontacts_trackerplugin::testUrl_data()
2146 {
2147     QTest::addColumn<QString>("url");
2148     QTest::addColumn<QString>("context");
2149     QTest::addColumn<QString>("subtype");
2150
2151     QTest::newRow("homepage/home")
2152             << "http://home.homepage/"
2153             << QContactDetail::ContextHome.latin1()
2154             << QContactUrl::SubTypeHomePage.latin1();
2155     QTest::newRow("homepage/work")
2156             << "http://work.homepage/"
2157             << QContactDetail::ContextWork.latin1()
2158             << QContactUrl::SubTypeHomePage.latin1();
2159     QTest::newRow("homepage/other")
2160             << "http://other.homepage/"
2161             << QContactDetail::ContextOther.latin1()
2162             << QContactUrl::SubTypeHomePage.latin1();
2163
2164     QTest::newRow("favourite/home")
2165             << "http://home.favourite/"
2166             << QContactDetail::ContextHome.latin1()
2167             << QContactUrl::SubTypeFavourite.latin1();
2168     QTest::newRow("favourite/work")
2169             << "http://work.favourite/"
2170             << QContactDetail::ContextWork.latin1()
2171             << QContactUrl::SubTypeFavourite.latin1();
2172     QTest::newRow("favourite/other")
2173             << "http://other.favourite/"
2174             << QContactDetail::ContextOther.latin1()
2175             << QContactUrl::SubTypeFavourite.latin1();
2176
2177     QTest::newRow("blog/home")
2178             << "http://home.blog/"
2179             << QContactDetail::ContextHome.latin1()
2180             << QContactUrl::SubTypeBlog.latin1();
2181     QTest::newRow("blog/work")
2182             << "http://work.blog/"
2183             << QContactDetail::ContextWork.latin1()
2184             << QContactUrl::SubTypeBlog.latin1();
2185     QTest::newRow("blog/other")
2186             << "http://other.blog/"
2187             << QContactDetail::ContextOther.latin1()
2188             << QContactUrl::SubTypeBlog.latin1();
2189
2190     QTest::newRow("default/home")
2191             << "http://home.default/"
2192             << QContactDetail::ContextHome.latin1()
2193             << QString();
2194     QTest::newRow("default/work")
2195             << "http://work.default/"
2196             << QContactDetail::ContextWork.latin1()
2197             << QString();
2198     QTest::newRow("default/other")
2199             << "http://other.default/"
2200             << QContactDetail::ContextOther.latin1()
2201             << QString();
2202 }
2203
2204 void
2205 ut_qtcontacts_trackerplugin::testUrl()
2206 {
2207     QFETCH(QString, url);
2208     QFETCH(QString, context);
2209     QFETCH(QString, subtype);
2210
2211     // construct and save contact
2212     QContact savedContact;
2213
2214     QContactUrl savedUrl;
2215     savedUrl.setUrl(url);
2216     savedUrl.setContexts(context);
2217
2218     if (not subtype.isEmpty()) {
2219         savedUrl.setSubType(subtype);
2220     }
2221
2222     QVERIFY(savedContact.saveDetail(&savedUrl));
2223
2224     QContactManager::Error error = QContactManager::UnspecifiedError;
2225     QVERIFY(engine()->saveContact(&savedContact, &error));
2226     QCOMPARE(error,  QContactManager::NoError);
2227
2228     // check fetched contact
2229     const QContactUrl fetchedUrl = contact(savedContact.localId()).detail<QContactUrl>();
2230
2231     if (subtype.isEmpty()) {
2232         // test implicit default when needed
2233         subtype = QContactUrl::SubTypeFavourite.latin1();
2234     }
2235
2236     QVERIFY(not fetchedUrl.isEmpty());
2237     QCOMPARE(fetchedUrl.url(), url);
2238     QCOMPARE(fetchedUrl.contexts(), QStringList(context));
2239     QCOMPARE(fetchedUrl.subType(), subtype);
2240
2241     // edit type
2242     QContact c1 = contact(savedContact.localId());
2243     QCOMPARE(c1.details<QContactUrl>().size(), 1);
2244     QContactUrl detail = c1.detail<QContactUrl>();
2245     detail.setContexts(QContactDetail::ContextWork);
2246
2247     c1.saveDetail(&detail);
2248     QVERIFY(c1.detail<QContactUrl>().contexts().size() == 1);
2249     error = QContactManager::UnspecifiedError;
2250     QVERIFY(engine()->saveContact(&c1, &error));
2251     QCOMPARE(error,  QContactManager::NoError);
2252     c1 = contact(savedContact.localId());
2253     QVERIFY(c1.details<QContactUrl>().size() == 1);
2254
2255
2256     // add additional URL (NB#178354)
2257     detail = QContactUrl();
2258     detail.setUrl("http://meego.com/");
2259     QVERIFY(c1.saveDetail(&detail));
2260
2261     error = QContactManager::UnspecifiedError;
2262     QVERIFY(engine()->saveContact(&c1, &error));
2263     QCOMPARE(error,  QContactManager::NoError);
2264
2265     QSet<QString> expectedUrls = QSet<QString>() << savedUrl.url() << detail.url();
2266     QSet<QString> fetchedUrls;
2267
2268     foreach(const QContactUrl &u, contact(savedContact.localId()).details<QContactUrl>()) {
2269         fetchedUrls.insert(u.url());
2270     }
2271
2272     QCOMPARE(fetchedUrls, expectedUrls);
2273 }