Changes: Fix wrong asserting in testSaveThumbnail()
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.git] / tests / ut_qtcontacts_trackerplugin / ut_qtcontacts_trackerplugin.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "ut_qtcontacts_trackerplugin.h"
43 #include "slots.h"
44
45 #include <dao/classhierarchy.h>
46 #include <dao/contactdetailschema.h>
47 #include <dao/conversion.h>
48 #include <dao/settings.h>
49 #include <dao/support.h>
50
51 #include <engine/engine.h>
52 #include <engine/contactidfetchrequest.h>
53 #include <engine/relationshipsaverequest.h>
54 #include <engine/relationshipfetchrequest.h>
55 #include <engine/trackerchangelistener.h>
56
57 #include <QtTracker/ontologies/nco.h>
58 #include <QtTracker/ontologies/nie.h>
59
60 using namespace SopranoLive;
61
62 typedef QPair<QContactDetail, QString> ContactDetailSample;
63 typedef QPair<QString, QString> PairOfStrings;
64
65 static ContactDetailSample makeDetailSample(const QContactDetail &detail, const QString &value)
66 {
67     return qMakePair(detail, value);
68 }
69
70 static ContactDetailSample makeDetailSample(const QString &detailName,
71                                             const QString &fieldName,
72                                             const QString &value)
73 {
74     QContactDetail detail(detailName);
75     detail.setValue(fieldName, value);
76     return qMakePair(detail, value);
77 }
78
79 ut_qtcontacts_trackerplugin::ut_qtcontacts_trackerplugin(QObject *parent)
80     : ut_qtcontacts_trackerplugin_common(QDir(DATADIR), QDir(SRCDIR), parent)
81 {
82 }
83
84 void ut_qtcontacts_trackerplugin::initTestCase()
85 {
86     BackEnds::Tracker::tracker()->rawLoad(referenceFileUrl("test-account-1.ttl"));
87 }
88
89 void ut_qtcontacts_trackerplugin::testContacts()
90 {
91     QContact c1, c2;
92
93     QContactManager::Error error;
94
95     error = QContactManager::UnspecifiedError;
96     QVERIFY(engine()->saveContact(&c1, &error));
97     QCOMPARE(error, QContactManager::NoError);
98
99     error = QContactManager::UnspecifiedError;
100     QVERIFY(engine()->saveContact(&c2, &error));
101     QCOMPARE(error, QContactManager::NoError);
102
103     error = QContactManager::UnspecifiedError;
104
105     QContactFilter filter;
106     QList<QContactSortOrder> sortOrders;
107     QList<QContactLocalId> contacts(engine()->contactIds(filter, sortOrders, &error));
108     QCOMPARE(error, QContactManager::NoError);
109
110     QVERIFY2(contacts.contains(c1.localId()), "Previously added contact is not found");
111     QVERIFY2(contacts.contains(c2.localId()), "Previously added contact is not found");
112 }
113
114 void ut_qtcontacts_trackerplugin::testContact()
115 {
116     // Test invalid contact id
117     QContactManager::Error error;
118     const QContactFetchHint hints;
119
120     error = QContactManager::UnspecifiedError;
121     QContact invalidContact = engine()->contactImpl( -1, hints, &error);
122     QVERIFY(error != QContactManager::NoError);
123
124     // Add a contact
125     QContact newContact;
126     const QContactLocalId oldid = newContact.localId();
127     error = QContactManager::UnspecifiedError;
128     QVERIFY(engine()->saveContact(&newContact, &error));
129     QCOMPARE(error, QContactManager::NoError);
130
131     QContactLocalId id = newContact.localId();
132     QVERIFY(id != oldid);
133
134     // Find the added contact
135     error = QContactManager::UnspecifiedError;
136     QContact c = engine()->contactImpl(id, hints, &error);
137     QCOMPARE(c.localId(), newContact.localId());
138     QCOMPARE(error, QContactManager::NoError);
139 }
140
141 void ut_qtcontacts_trackerplugin::testSaveFullname()
142 {
143     const QString fullname = "A Full Name";
144
145     QList<QContact> contacts = parseVCards(referenceFileName("fullname.vcf"), 1);
146     QVERIFY(not contacts.isEmpty());
147
148     QContactName name = contacts.first().detail<QContactName>();
149     QCOMPARE(name.customLabel(), fullname);
150
151     QContactManager::Error error = QContactManager::UnspecifiedError;
152     bool success = engine()->saveContact(&contacts.first(), &error);
153     QCOMPARE(error, QContactManager::NoError);
154     QVERIFY(0 != contacts.first().localId());
155     QVERIFY(success);
156
157     name = contact(contacts.first().localId()).detail<QContactName>();
158     QCOMPARE(name.customLabel(), fullname);
159 }
160
161 void ut_qtcontacts_trackerplugin::testSaveName()
162 {
163     QContact c;
164     QContactLocalId initialId = c.localId();
165     int detailsAdded = 0;
166
167     QMap<QString,QString> nameValues;
168     QContactName name;
169
170     nameValues.insert(QContactName::FieldPrefix, "Mr");
171     nameValues.insert(QContactName::FieldFirstName, "John");
172     nameValues.insert(QContactName::FieldMiddleName, "Rupert");
173     nameValues.insert(QContactName::FieldLastName, "Doe");
174     nameValues.insert(QContactName::FieldSuffix, "III");
175     nameValues.insert(QContactName::FieldCustomLabel, "The Duke");
176
177     foreach (QString field, nameValues.keys()) {
178         name.setValue(field, nameValues.value(field));
179     }
180     c.saveDetail(&name);
181
182     QContactNickname nick;
183     nick.setValue(QLatin1String(QContactNickname::FieldNickname), "Johnny");
184     c.saveDetail(&nick);
185
186     QCOMPARE(c.detail<QContactName>().prefix(), QLatin1String("Mr"));
187     QCOMPARE(c.detail<QContactName>().firstName(), QLatin1String("John"));
188     QCOMPARE(c.detail<QContactName>().middleName(), QLatin1String("Rupert"));
189     QCOMPARE(c.detail<QContactName>().lastName(), QLatin1String("Doe"));
190     QCOMPARE(c.detail<QContactName>().suffix(), QLatin1String("III"));
191     QCOMPARE(c.detail<QContactName>().customLabel(), QLatin1String("The Duke"));
192     QCOMPARE(c.detail<QContactNickname>().nickname(), QLatin1String("Johnny"));
193
194     detailsAdded++;
195
196     QContactManager::Error error(QContactManager::UnspecifiedError);
197     QVERIFY(engine()->saveContact(&c, &error));
198     QCOMPARE(error,  QContactManager::NoError);
199     QVERIFY(c.localId() != initialId);
200     QContact contact = this->contact(c.localId());
201     QList<QContactName> details = contact.details<QContactName>();
202     QList<QContactNickname> details2 = contact.details<QContactNickname>();
203     QCOMPARE(details.count(), detailsAdded);
204     QCOMPARE(details2.count(), detailsAdded);
205     // Name is unique
206     foreach(QString field, nameValues.keys()) {
207         QCOMPARE(details.first().value(field), nameValues.value(field));
208     }
209     QCOMPARE(details2.first().value(QLatin1String(QContactNickname::FieldNickname)), QString("Johnny"));
210
211     // Try changing the name of the saved contact.
212     {
213         QMap<QString,QString> nameValues;
214         QContactName name = c.detail<QContactName>();
215         nameValues.insert(QLatin1String(QContactName::FieldPrefix), "Mr2");
216         nameValues.insert(QLatin1String(QContactName::FieldFirstName), "John2");
217         nameValues.insert(QLatin1String(QContactName::FieldMiddleName), "Rupert2");
218         nameValues.insert(QLatin1String(QContactName::FieldLastName), "");
219         //    nameValues.insert(QContactName::FieldSuffix, "III");
220
221         foreach (QString field, nameValues.keys()) {
222             name.setValue(field, nameValues.value(field));
223         }
224         c.saveDetail(&name);
225
226         QContactNickname nick = c.detail<QContactNickname>();
227         nick.setValue(QLatin1String(QContactNickname::FieldNickname), "Johnny2");
228         c.saveDetail(&nick);
229
230
231         error = QContactManager::UnspecifiedError;
232         QVERIFY(engine()->saveContact(&c, &error));
233         QCOMPARE(error,  QContactManager::NoError);
234         QVERIFY(c.localId() != initialId);
235
236         const QtMobility::QContactFetchHint hints;
237         error = QContactManager::UnspecifiedError;
238         QContact contact = engine()->contactImpl(c.localId(), hints, &error);
239         QCOMPARE(error,  QContactManager::NoError);
240         QList<QContactName> details = contact.details<QContactName>();
241         QList<QContactNickname> details2 = contact.details<QContactNickname>();
242         QCOMPARE(details.count(), detailsAdded);
243         QCOMPARE(details2.count(), detailsAdded);
244
245         // Name is unique
246         foreach(QString field, nameValues.keys()) {
247             QCOMPARE(details.at(0).value(field), nameValues.value(field));
248         }
249
250         QCOMPARE(details2.at(0).value(QLatin1String(QContactNickname::FieldNickname)), QString("Johnny2"));
251      }
252 }
253
254 void ut_qtcontacts_trackerplugin::testSaveNameUnique()
255 {
256     // save contact with one name
257     QContactName name1;
258     name1.setFirstName("Till");
259     name1.setLastName("Eulenspiegel");
260
261     QContact savedContact;
262     QVERIFY(savedContact.saveDetail(&name1));
263
264     QContactManager::Error error(QContactManager::AlreadyExistsError);
265     QVERIFY(engine()->saveContact(&savedContact, &error));
266     QCOMPARE(error, QContactManager::NoError);
267     QVERIFY(0 != savedContact.localId());
268
269     // fetch the contact and compare content
270     QContactFetchHint fetchHint;
271     error = QContactManager::AlreadyExistsError;
272     QContact fetchedContact = engine()->contactImpl(savedContact.localId(), fetchHint, &error);
273     QCOMPARE(error, QContactManager::NoError);
274
275     QCOMPARE(fetchedContact.detail<QContactName>().firstName(), name1.firstName());
276     QCOMPARE(fetchedContact.detail<QContactName>().lastName(), name1.lastName());
277     QCOMPARE(fetchedContact.localId(), savedContact.localId());
278
279     // save contact with second name detail which is invalid
280     QContactName name2;
281     name2.setFirstName("Hans");
282     name2.setLastName("Wurst");
283     QVERIFY(savedContact.saveDetail(&name2));
284
285     // the engine shall drop the odd detail
286     QTest::ignoreMessage(QtWarningMsg,
287                          "libqtcontacts-tracker: Dropping odd details for contact 0: "
288                          "Name detail must be unique");
289
290
291     qctLogger().setShowLocation(false);
292     error = QContactManager::NoError;
293     const bool contactSaved = engine()->saveContact(&savedContact, &error);
294     qctLogger().setShowLocation(true);
295
296     QCOMPARE(error, QContactManager::NoError);
297     QVERIFY(contactSaved);
298
299     // again fetch the contact and compare content
300     error = QContactManager::AlreadyExistsError;
301     fetchedContact = engine()->contactImpl(savedContact.localId(), fetchHint, &error);
302     QCOMPARE(error, QContactManager::NoError);
303
304     QCOMPARE(fetchedContact.detail<QContactName>().firstName(), name1.firstName());
305     QCOMPARE(fetchedContact.detail<QContactName>().lastName(), name1.lastName());
306     QCOMPARE(fetchedContact.localId(), savedContact.localId());
307 }
308
309 // test NB#189108
310 void ut_qtcontacts_trackerplugin::testFetchAll()
311 {
312     // create some few contacts to have something in the db for sure
313     QList<QContact> contacts;
314
315     for(int i = 1; i <= 5; ++i) {
316         QContactNickname nickname;
317         nickname.setNickname(QString::fromLatin1("Fetchy %1").arg(i));
318
319         contacts.append(QContact());
320         QVERIFY(contacts.last().saveDetail(&nickname));
321     }
322
323     QContactManager::Error error = QContactManager::UnspecifiedError;
324     const bool contactsSaved = engine()->saveContacts(&contacts, 0, &error);
325     QCOMPARE(error, QContactManager::NoError);
326     QVERIFY(contactsSaved);
327
328     // directly asks tracker how many contacts are stored
329     RDFVariable contactVariable;
330
331     contactVariable.union_().child().isOfType<nco::PersonContact>();
332     contactVariable.union_().child().isOfType<nco::ContactList>().isOfType<nco::Contact>();
333
334     RDFSelect contactQuery = RDFSelect().addCountColumn(contactVariable);
335     LiveNodes nodes = ::BackEnds::Tracker::tracker()->modelQuery(contactQuery);
336     QVERIFY(nodes->refreshModel(LiveNodeModel::Block));
337     QCOMPARE(nodes->rowCount(), 1);
338
339     // fetch contacts via contact manager engine
340     contacts = engine()->contacts(QContactFilter(), QList<QContactSortOrder>(),
341                                   QContactFetchHint(), &error);
342
343     // compare sparql and engine result
344     QCOMPARE(contacts.count(), nodes->rawRow(0).first().toInt());
345 }
346
347
348 void ut_qtcontacts_trackerplugin::testSaveNothing()
349 {
350     QContactList nothing;
351     QContactManager::Error error = QContactManager::UnspecifiedError;
352     const bool contactsSaved = engine()->saveContacts(&nothing, 0, &error);
353     QCOMPARE(error, QContactManager::NoError);
354     QVERIFY(contactsSaved );
355 }
356
357 struct PhoneValue {
358     QString number;
359     QString context;
360     QString subtype;
361 };
362
363 static void add(QMap<QString, PhoneValue> &phoneValues,
364                 const QString detailUri, const QString &number,
365                 const QString &context, const QString &subtype = QString())
366 {
367     const PhoneValue value = { number, context, subtype };
368     phoneValues.insert(detailUri, value);
369 }
370
371 void ut_qtcontacts_trackerplugin::testSavePhoneNumber_data()
372 {
373     QTest::addColumn<int>("iteration");
374
375     QTest::newRow("1th run") << 0;
376     QTest::newRow("2nd run") << 1;
377     QTest::newRow("3rd run") << 2;
378 }
379
380 void ut_qtcontacts_trackerplugin::testSavePhoneNumber()
381 {
382     QFETCH(int, iteration);
383     Q_UNUSED(iteration);
384
385     // use the same values for 2 contacts
386     QContact c;
387     QContactLocalId initialId = c.localId();
388     int detailsAdded = 0;
389     QContactName name;
390     name.setFirstName("I have phone numbers");
391     name.setLastName("Girl");
392     c.saveDetail(&name);
393
394     QMap<QString, PhoneValue> phoneValues;
395
396     add(phoneValues, "tel:7044866472", "(704)486-6472", QContactDetail::ContextHome);
397     add(phoneValues, "tel:7659571663", "(765)957-1663", QContactDetail::ContextHome);
398     add(phoneValues, "tel:9998881111", "(999)888-1111", QContactDetail::ContextHome, QContactPhoneNumber::SubTypeMobile);
399
400     add(phoneValues, "tel:7921236113", "(792)123-6113", QContactDetail::ContextWork);
401     add(phoneValues, "tel:9184917361", "(918)491-7361", QContactDetail::ContextWork, QContactPhoneNumber::SubTypeMobile);
402     add(phoneValues, "tel:4126701514", "(412)670-1514", QContactDetail::ContextWork, QContactPhoneNumber::SubTypeCar);
403
404     foreach(const QString &detailUri, phoneValues.keys()) {
405         const PhoneValue &value(phoneValues[detailUri]);
406         QContactPhoneNumber phone;
407
408         phone.setNumber(value.number);
409
410         if (not value.context.isEmpty()) {
411             phone.setContexts(value.context);
412         }
413         if (not value.subtype.isEmpty()) {
414             phone.setSubTypes(value.subtype);
415         }
416
417         QVERIFY(c.saveDetail(&phone));
418         detailsAdded++;
419     }
420
421     QContactManager::Error error(QContactManager::UnspecifiedError);
422     QVERIFY(engine()->saveContact(&c, &error));
423     QCOMPARE(error,  QContactManager::NoError);
424     const QContactLocalId savedId(c.localId());
425     QVERIFY(savedId != initialId);
426     // wait for commit transaction to be done, no signals yet
427     for(int i = 0; i < 100; i++) {
428         usleep(10000);
429         QCoreApplication::processEvents();
430     }
431
432     // verify with synchronous read too
433     const QtMobility::QContactFetchHint hints; 
434     error = QContactManager::UnspecifiedError;
435     QContact contact = engine()->contactImpl(c.localId(), hints, &error);
436     QCOMPARE(error,  QContactManager::NoError);
437     QList<QContactPhoneNumber> details = contact.details<QContactPhoneNumber>();
438     QCOMPARE(details.count(), detailsAdded);
439
440     foreach(QContactPhoneNumber detail, details) {
441         QMap<QString, PhoneValue>::ConstIterator i = phoneValues.find(detail.detailUri());
442
443         // Verify that the stored values and attributes are the same as given
444         QVERIFY2(i != phoneValues.end(), qPrintable(detail.number()));
445
446         QCOMPARE(detail.number(), i->number);
447         QCOMPARE(detail.contexts().first(), i->context);
448
449         if (i->subtype.isEmpty()) { // default is voice
450             QVERIFY2(detail.subTypes().contains(QContactPhoneNumber::SubTypeVoice),
451                      qPrintable(detail.number()));
452         } else {
453             QVERIFY2(detail.subTypes().contains(i->subtype),
454                      qPrintable(detail.number()));
455         }
456
457     }
458
459     // save again with normalized phone numbers
460     error = QContactManager::UnspecifiedError;
461     QVERIFY(engine()->saveContact(&contact, &error));
462     QCOMPARE(error,  QContactManager::NoError);
463     QCOMPARE(contact.localId(), savedId);
464     // wait for commit transaction to be done, no signals yet
465     for(int i = 0; i < 100; i++) {
466         usleep(10000);
467         QCoreApplication::processEvents();
468     }
469
470     error = QContactManager::UnspecifiedError;
471     contact = engine()->contactImpl(c.localId(), hints, &error);
472     QCOMPARE(error,  QContactManager::NoError);
473     details = contact.details<QContactPhoneNumber>();
474     QCOMPARE(details.count(), detailsAdded);
475
476     foreach(QContactPhoneNumber detail, details) {
477         QMap<QString, PhoneValue>::ConstIterator i = phoneValues.find(detail.detailUri());
478
479         // Verify that the stored values and attributes are the same as given
480         QVERIFY2(i != phoneValues.end(), qPrintable(detail.detailUri()));
481
482         QCOMPARE(detail.number(), i->number);
483         QCOMPARE(detail.contexts().first(), i->context);
484
485         if (i->subtype.isEmpty()) { // default is voice
486             QVERIFY2(detail.subTypes().contains(QContactPhoneNumber::SubTypeVoice),
487                      qPrintable(detail.detailUri()));
488         } else {
489             QVERIFY2(detail.subTypes().contains(i->subtype),
490                      qPrintable(detail.detailUri()));
491         }
492     }
493
494     // edit one of numbers . values, context and subtypes and save again
495     QString editedPhoneValue = "+7044866473";
496     QContactPhoneNumber phone = details[0];
497     phone.setNumber(editedPhoneValue);
498     phone.setContexts(QContactDetail::ContextWork);
499     phone.setSubTypes(QContactPhoneNumber::SubTypeMobile);
500     c = contact;
501     QCOMPARE(c.localId(), savedId);
502     QVERIFY(c.saveDetail(&phone));
503     error = QContactManager::UnspecifiedError;
504     QVERIFY(engine()->saveContact(&c, &error));
505     QCOMPARE(c.localId(), savedId);
506     QCOMPARE(error,  QContactManager::NoError);
507     c = this->contact(c.localId(), QStringList()<<QContactPhoneNumber::DefinitionName);
508     QCOMPARE(c.localId(), savedId);
509     details = c.details<QContactPhoneNumber>();
510     QCOMPARE(details.count(), detailsAdded);
511     bool found = false;
512     foreach (QContactPhoneNumber detail, details) {
513         if(detail.number() == phone.number())
514         {
515             found = true;
516             QVERIFY(detail.subTypes().contains(QContactPhoneNumber::SubTypeMobile));
517             QVERIFY(detail.contexts().contains(QContactPhoneNumber::ContextWork));
518             break;
519         }
520     }
521     QVERIFY(found);
522 }
523
524 void ut_qtcontacts_trackerplugin::testPhoneNumberContext()
525 {
526     QContact c;
527     QContactPhoneNumber phone;
528     phone.setContexts(QContactDetail::ContextHome);
529     phone.setNumber("555-888");
530     phone.setSubTypes(QContactPhoneNumber::SubTypeMobile);
531     c.saveDetail(&phone);
532     QContact contactToSave = c;
533     // Let's do this all twice, first time save new detail, and next iteration change the context
534     for (int iterations = 0; iterations < 2; iterations++) {
535         QContactManager::Error error(QContactManager::UnspecifiedError);
536         QVERIFY(engine()->saveContact(&contactToSave, &error));
537         QCOMPARE(error, QContactManager::NoError);
538         // wait for commit transaction to be done, no signals yet
539         for(int i = 0; i < 100; i++) {
540             usleep(10000);
541             QCoreApplication::processEvents();
542         }
543
544         QContactFetchRequest request;
545         QContactLocalIdFilter filter;
546         QList<QContactLocalId> ids;
547         ids.append(contactToSave.localId());
548         filter.setIds(ids);
549         request.setFilter(filter);
550
551         QStringList details;
552         details << QContactPhoneNumber::DefinitionName;
553         QContactFetchHint fetchHint;
554         fetchHint.setDetailDefinitionsHint(details);
555         request.setFetchHint(fetchHint);
556
557         Slots slot;
558         QObject::connect(&request, SIGNAL(resultsAvailable()),
559                 &slot, SLOT(resultsAvailable()));
560
561         engine()->startRequest(&request);
562
563         engine()->waitForRequestFinished(&request, 1000);
564
565         // if it takes more, then something is wrong
566         QVERIFY(request.isFinished());
567         QVERIFY(!slot.contacts.isEmpty());
568
569         QContact contactToTest;
570         foreach (QContact savedContact, slot.contacts) {
571             if (savedContact.localId() == contactToSave.localId()) {
572                 contactToTest = savedContact;
573             }
574         }
575         QCOMPARE(contactToTest.localId(), contactToSave.localId()); // Just to be sure we got the saved contact
576         QCOMPARE(contactToTest.details<QContactPhoneNumber>().count(), 1);
577         if (0 == iterations) {
578             // perform context change
579             QContactPhoneNumber phoneToEdit = contactToTest.detail<QContactPhoneNumber>();
580             phoneToEdit.setContexts(QContactDetail::ContextWork);
581             contactToTest.saveDetail(&phoneToEdit);
582             contactToSave = contactToTest;
583         }
584         QVERIFY(contactToTest.details<QContactPhoneNumber>().count() == 1);
585     }
586 }
587
588 void ut_qtcontacts_trackerplugin::testWritingOnlyWorkMobile()
589 {
590     QContact c;
591     QContactPhoneNumber phone;
592     phone.setContexts(QContactDetail::ContextWork);
593     phone.setNumber("555999");
594     phone.setSubTypes(QContactPhoneNumber::SubTypeMobile);
595     c.saveDetail(&phone);
596     QContact& contactToSave = c;
597     QContactManager::Error error(QContactManager::UnspecifiedError);
598     QVERIFY(engine()->saveContact(&contactToSave, &error));
599     QCOMPARE(error, QContactManager::NoError);
600     // wait for commit transaction to be done, no signals yet
601     for(int i = 0; i < 100; i++) {
602         usleep(10000);
603         QCoreApplication::processEvents();
604     }
605
606     QContactFetchRequest request;
607     QContactLocalIdFilter filter;
608     QList<QContactLocalId> ids;
609     ids.append(contactToSave.localId());
610     filter.setIds(ids);
611     request.setFilter(filter);
612     QStringList details;
613     details << QContactPhoneNumber::DefinitionName;
614     QContactFetchHint fetchHint;
615     fetchHint.setDetailDefinitionsHint(details);
616     request.setFetchHint(fetchHint);
617
618     Slots slot;
619     QObject::connect(&request, SIGNAL(resultsAvailable()),
620             &slot, SLOT(resultsAvailable()));
621
622     engine()->startRequest(&request);
623
624     engine()->waitForRequestFinished(&request, 1000);
625
626     // if it takes more, then something is wrong
627     QVERIFY(request.isFinished());
628     QVERIFY(!slot.contacts.isEmpty());
629
630     QContact contactToTest;
631     foreach (QContact savedContact, slot.contacts) {
632         if (savedContact.localId() == c.localId()) {
633             contactToTest = savedContact;
634         }
635     }
636
637     // add implicied subtypes for new fetch request
638     phone.setSubTypes(phone.subTypes() <<
639                       QContactPhoneNumber::SubTypeMessagingCapable <<
640                       QContactPhoneNumber::SubTypeVoice);
641
642     QCOMPARE(contactToTest.localId(), c.localId()); // Just to be sure we got the saved contact
643     QCOMPARE(contactToTest.details<QContactPhoneNumber>().count(), 1);
644     QCOMPARE(contactToTest.detail<QContactPhoneNumber>().number(), phone.number());
645     QCOMPARE(contactToTest.detail<QContactPhoneNumber>().subTypes().toSet(), phone.subTypes().toSet());
646     QCOMPARE(contactToTest.detail<QContactPhoneNumber>().contexts(), phone.contexts());
647 }
648
649 void ut_qtcontacts_trackerplugin::testSaveAddress()
650 {
651     QContact c;
652     QContactName name;
653     name.setFirstName("Aruba & Barbados");
654     name.setLastName("Girl");
655     c.saveDetail(&name);
656     QContactLocalId initialId = c.localId();
657     int detailsAdded = 0;
658
659     // List of pairs of field-value map and context
660     typedef QMap<QString,QString> typeAddress;
661     typedef QPair<typeAddress,QString> typeAddressWithContext;
662     QList<typeAddressWithContext> addressValues;
663
664     // TODO check status of 137174 and other libqttracker1pre6 bugs before refactoring
665     typeAddress values;
666     values.insert(QLatin1String(QContactAddress::FieldCountry), "Barbados");
667     values.insert(QLatin1String(QContactAddress::FieldPostcode), "55555");
668     values.insert(QLatin1String(QContactAddress::FieldStreet), "Martindales Rd");
669     values.insert(QLatin1String(QContactAddress::FieldRegion), "Bridgetown");
670     values.insert(QLatin1String(QContactAddress::FieldLocality), "Bridgetown town");
671
672
673
674     addressValues.append(typeAddressWithContext(values, QLatin1String(QContactDetail::ContextWork)));
675     values.clear();
676     values.insert(QLatin1String(QContactAddress::FieldCountry), "Aruba");
677     values.insert(QLatin1String(QContactAddress::FieldPostcode), "44444");
678     values.insert(QLatin1String(QContactAddress::FieldStreet), "Brazilie Straat");
679     values.insert(QLatin1String(QContactAddress::FieldRegion), "Oranjestad");
680     values.insert(QLatin1String(QContactAddress::FieldLocality), "Bridgetown town");
681     values.insert(QLatin1String(QContactAddress::FieldPostOfficeBox), "00011102");
682
683     addressValues.append(typeAddressWithContext(values, QLatin1String(QContactDetail::ContextHome)));
684     values.clear();
685     values.insert(QLatin1String(QContactAddress::FieldCountry), "ArubaWork");
686     values.insert(QLatin1String(QContactAddress::FieldPostcode), "44445");
687     values.insert(QLatin1String(QContactAddress::FieldStreet), "Sunset Blvd");
688     values.insert(QLatin1String(QContactAddress::FieldRegion), "Oranjestad");
689     values.insert(QLatin1String(QContactAddress::FieldLocality), "Bridgetown town");
690     values.insert(QLatin1String(QContactAddress::FieldPostOfficeBox), "00011103");
691
692     addressValues.append(typeAddressWithContext(values, QLatin1String(QContactDetail::ContextHome)));
693     foreach (typeAddressWithContext addressWithContext, addressValues) {
694         QContactAddress address;
695         foreach (QString field, addressWithContext.first.keys()) {
696             address.setValue(field, addressWithContext.first.value(field));
697         }
698         address.setContexts(addressWithContext.second);
699         c.saveDetail(&address);
700         detailsAdded++;
701     }
702
703     QContactManager::Error error(QContactManager::UnspecifiedError);
704     engine()->saveContact(&c, &error);
705     QCOMPARE(error,  QContactManager::NoError);
706     QVERIFY(c.localId() != initialId);
707     const QtMobility::QContactFetchHint hints;
708     error = QContactManager::UnspecifiedError;
709     QContact contact = engine()->contactImpl(c.localId(), hints, &error);
710     QCOMPARE(error,  QContactManager::NoError);
711     QList<QContactAddress> details = contact.details<QContactAddress>();
712     QCOMPARE(details.count(), detailsAdded);
713     bool found = false;
714     // Test if inserted values are found in some of the details
715     foreach (typeAddressWithContext addressWithContext, addressValues) {
716         foreach (QContactAddress detail, details) {
717             foreach (QString field, addressWithContext.first.keys()) {
718                 found = (detail.value(field) == addressWithContext.first.value(field));
719                 if (!found)
720                     break;
721             }
722             if (found)
723                 break;
724         }
725         QVERIFY2(found, "Inserted detail was not found in the fetched details");
726     }
727 }
728
729 void ut_qtcontacts_trackerplugin::testSaveOrganization()
730 {
731     QList<PairOfStrings> samples;
732
733     samples << PairOfStrings(QContactOrganization::FieldName, "Nokia");
734     samples << PairOfStrings(QContactOrganization::FieldLogoUrl, "nokia.jpg");
735     samples << PairOfStrings(QContactOrganization::FieldDepartment, "Meego R&D");
736     samples << PairOfStrings(QContactOrganization::FieldLocation, "Helsinki");
737     samples << PairOfStrings(QContactOrganization::FieldRole, "Developer");
738     samples << PairOfStrings(QContactOrganization::FieldTitle, "Code Guru");
739
740     QContact contact;
741
742     for(int i = 0; i < samples.count(); ++i) {
743         QContactOrganization org = contact.detail<QContactOrganization>();
744         org.setValue(samples[i].first, samples[i].second);
745         contact.saveDetail(&org);
746
747         QContactManager::Error error = QContactManager::UnspecifiedError;
748         bool success = engine()->saveContact(&contact, &error);
749         QCOMPARE(error,  QContactManager::NoError);
750         QVERIFY(0 != contact.localId());
751         QVERIFY(success);
752
753         error = QContactManager::UnspecifiedError;
754         const QContactLocalId contactLocalId = contact.localId();
755         contact = engine()->contactImpl(contact.localId(), QContactFetchHint(), &error);
756         QCOMPARE(contact.localId(), contactLocalId);
757         QCOMPARE(error,  QContactManager::NoError);
758
759         org = contact.detail<QContactOrganization>();
760
761         for(int j = 0; j < i; ++j) {
762             QCOMPARE(org.value(samples[j].first), samples[j].second);
763         }
764     }
765 }
766
767 void ut_qtcontacts_trackerplugin::testSaveEmailAddress()
768 {
769     QContact c;
770     QContactLocalId initialId = c.localId();
771     int detailsAdded = 0;
772
773     QMap<QString,QString> values;
774     values.insert("john.does@hotmail.com", QContactDetail::ContextHome);
775     values.insert("john.doe@gmail.com", QContactDetail::ContextWork);
776     values.insert("john.doe@nokia.com", QContactDetail::ContextWork);
777     values.insert("john.doe@johndoe.com", QContactDetail::ContextHome);
778     foreach(QString address, values.keys()) {
779         QContactEmailAddress emailAddress;
780         emailAddress.setEmailAddress(address);
781         emailAddress.setContexts(values.value(address));
782         c.saveDetail(&emailAddress);
783         detailsAdded++;
784     }
785     QContactName name;
786     name.setFirstName("Jo");
787     name.setLastName("H N Doe");
788     c.saveDetail(&name);
789
790     QContactManager::Error error = QContactManager::UnspecifiedError;
791     bool contactSaved = engine()->saveContact(&c, &error);
792     QCOMPARE(error,  QContactManager::NoError);
793     QVERIFY(c.localId() != initialId);
794     QVERIFY(contactSaved);
795
796     const QtMobility::QContactFetchHint hints;
797     error = QContactManager::UnspecifiedError;
798     QContact contact = engine()->contactImpl(c.localId(), hints, &error);
799     QCOMPARE(error,  QContactManager::NoError);
800     QList<QContactEmailAddress> details = contact.details<QContactEmailAddress>();
801     QCOMPARE(details.count(), detailsAdded);
802     foreach (QContactEmailAddress detail, details) {
803         QString address = detail.value(QContactEmailAddress::FieldEmailAddress);
804         QVERIFY(values.contains(address));
805         QCOMPARE(detail.contexts()[0], values.value(address));
806     }
807 }
808
809 void ut_qtcontacts_trackerplugin::testSaveCustomValues()
810 {
811     // create contact with custom subtypes
812     QContactName name;
813     name.setFirstName("Teppo");
814     name.setLastName("Virtanen");
815
816     QContactPhoneNumber savedNumber;
817     savedNumber.setNumber("+23234234");
818     savedNumber.setSubTypes(QStringList() <<
819                             QContactPhoneNumber::SubTypeVoice <<
820                             "CrazyA" << "CrazyB" << "CrazyC");
821
822     QContactOnlineAccount savedAccount;
823     savedAccount.setAccountUri("jepa@account.org");
824     savedAccount.setContexts(QContactDetail::ContextHome);
825     savedAccount.setSubTypes(QStringList() << "FunkyA" << "FunkyB" << "FunkyC");
826
827     QContact savedContact;
828     savedContact.saveDetail(&name);
829     savedContact.saveDetail(&savedNumber);
830     savedContact.saveDetail(&savedAccount);
831
832     // save the test contact
833     QContactManager::Error error = QContactManager::UnspecifiedError;
834     bool contactSaved = engine()->saveContact(&savedContact, &error);
835     QCOMPARE(error,  QContactManager::NoError);
836     QVERIFY(0 != savedContact.localId());
837     QVERIFY(contactSaved);
838
839     // restore the contact
840     QContactFetchHint fetchHint;
841     fetchHint.setDetailDefinitionsHint(QStringList() <<
842                                        QContactPhoneNumber::DefinitionName <<
843                                        QContactOnlineAccount::DefinitionName);
844
845     error = QContactManager::UnspecifiedError;
846     QContact fetchedContact = engine()->contactImpl(savedContact.localId(), fetchHint, &error);
847
848     QCOMPARE(error,  QContactManager::NoError);
849     QCOMPARE(fetchedContact.localId(), savedContact.localId());
850
851     // compare the fetched contact with saved contact
852     const QList<QContactPhoneNumber> fetchedNumbers =
853             fetchedContact.details<QContactPhoneNumber>();
854
855     QCOMPARE(fetchedNumbers.count(), 1);
856     QCOMPARE(fetchedNumbers.first().number(), savedNumber.number());
857     QCOMPARE(fetchedNumbers.first().subTypes().count(), savedNumber.subTypes().count());
858     QCOMPARE(fetchedNumbers.first().subTypes().toSet(), savedNumber.subTypes().toSet());
859
860     const QList<QContactOnlineAccount> fetchedAccounts =
861             fetchedContact.details<QContactOnlineAccount>();
862
863     QCOMPARE(fetchedAccounts.count(), 1);
864     QCOMPARE(fetchedAccounts.first().accountUri(), savedAccount.accountUri());
865     QCOMPARE(fetchedAccounts.first().contexts().count(), savedAccount.contexts().count());
866     QCOMPARE(fetchedAccounts.first().contexts().toSet(), savedAccount.contexts().toSet());
867     QCOMPARE(fetchedAccounts.first().subTypes().count(), savedAccount.subTypes().count());
868     QCOMPARE(fetchedAccounts.first().subTypes().toSet(), savedAccount.subTypes().toSet());
869 }
870
871 void ut_qtcontacts_trackerplugin::testRemoveContact()
872 {
873     QContact c;
874     QContactPhoneNumber phone;
875     phone.setNumber("+358501234567");
876     c.saveDetail(&phone);
877     QContactEmailAddress email;
878     email.setEmailAddress("super.man@hotmail.com");
879     c.saveDetail(&email);
880     QContactName name;
881     name.setFirstName("Super");
882     name.setLastName("Man");
883     c.saveDetail(&name);
884
885     const QtMobility::QContactFetchHint hints;
886
887     QContactManager::Error error(QContactManager::UnspecifiedError);
888     QVERIFY(engine()->saveContact(&c, &error));
889     QCOMPARE(error,  QContactManager::NoError);
890
891     error = QContactManager::UnspecifiedError;
892     QVERIFY2(engine()->removeContact(c.localId(), &error), "Removing a contact failed");
893     QCOMPARE(error, QContactManager::NoError);
894
895     QVERIFY2(engine()->contactImpl(c.localId(), hints, &error) == QContact(),
896              "Found a contact, which should have been removed");
897 }
898
899 void ut_qtcontacts_trackerplugin::testSaveContacts()
900 {
901     QList<QContact> contacts;
902     for (int i = 0; i < 3; i++) {
903         QContact c;
904         QContactName name;
905         name.setFirstName("John");
906         name.setLastName(QString::number(i,10));
907         c.saveDetail(&name);
908
909         // skip first contact to test GUID detail auto-creation
910         if (i > 0) {
911             QContactGuid uid;
912             uid.setGuid(QUuid::createUuid().toString());
913             c.saveDetail(&uid);
914         }
915
916         QContactGender gender;
917         if ((i%2))
918             gender.setGender(QContactGender::GenderMale);
919         else
920             gender.setGender(QContactGender::GenderFemale);
921         c.saveDetail(&gender);
922         contacts.append(c);
923     }
924
925     QDateTime now(QDateTime::currentDateTime().addSecs(-1));
926
927     QMap<int, QContactManager::Error> errorMap;
928     QContactManager::Error error(QContactManager::UnspecifiedError);
929     engine()->saveContacts(&contacts, &errorMap, &error);
930     QCOMPARE(error, QContactManager::NoError);
931
932     for (int i = 0; i < contacts.count(); i++) {
933         QVERIFY(contacts[i].localId() != 0);
934         error = QContactManager::UnspecifiedError;
935         QContact contact = engine()->contactImpl(contacts[i].localId(), QContactFetchHint(), &error);
936         QCOMPARE(error,  QContactManager::NoError);
937         QCOMPARE(contact.localId(), contacts[i].localId());
938         QList<QContactName> details = contact.details<QContactName>();
939         QCOMPARE(details.count(), 1);
940         QCOMPARE(details.first().lastName(),
941                  QString("%1").arg(QString::number(i,10)));
942         QList<QContactGender> genders = contact.details<QContactGender>();
943         QCOMPARE(genders.count(), 1);
944         QCOMPARE(genders.first().gender(),contacts[i].detail<QContactGender>().gender());
945         QList<QContactGuid> guids = contact.details<QContactGuid>();
946         QCOMPARE(guids.count(), 1);
947         QVERIFY(not guids.first().guid().isEmpty());
948
949         QList<QContactTimestamp> timestamps = contact.details<QContactTimestamp>();
950
951         QCOMPARE(timestamps.count(), 1);
952         QVERIFY(not timestamps.first().lastModified().isNull());
953         QVERIFY(timestamps.first().lastModified() >= now);
954         QVERIFY(not timestamps.first().created().isNull());
955         QVERIFY(timestamps.first().created() >= now);
956     }
957
958     // save contacts again to check if timestamps get updated
959     sleep(1);
960
961     QDateTime later(QDateTime::currentDateTime().addSecs(-1));
962
963     error = QContactManager::UnspecifiedError;
964     engine()->saveContacts(&contacts, &errorMap, &error);
965     QCOMPARE(error, QContactManager::NoError);
966
967     for (int i = 0; i < contacts.count(); i++) {
968         QVERIFY(contacts[i].localId() != 0);
969         error = QContactManager::UnspecifiedError;
970         QContact contact = engine()->contactImpl(contacts[i].localId(), QContactFetchHint(), &error);
971         QCOMPARE(error,  QContactManager::NoError);
972         QCOMPARE(contact.localId(), contacts[i].localId());
973
974         QList<QContactTimestamp> timestamps = contact.details<QContactTimestamp>();
975
976         QCOMPARE(timestamps.count(), 1);
977         QVERIFY(not timestamps.first().lastModified().isNull());
978         QVERIFY(timestamps.first().lastModified() >= later);
979         QVERIFY(not timestamps.first().created().isNull());
980         QVERIFY(timestamps.first().created() >= now);
981         QVERIFY(timestamps.first().created() < later);
982     }
983 }
984
985 void ut_qtcontacts_trackerplugin::testRemoveContacts()
986 {
987     QList<QContactLocalId> addedIds;
988     for (int i = 0; i < 5; i++) {
989         QContact c;
990         QContactName name;
991         name.setFirstName(QString("John%1").arg(QString::number(i,10)));
992         c.saveDetail(&name);
993
994         QContactManager::Error error(QContactManager::UnspecifiedError);
995         QVERIFY(engine()->saveContact(&c, &error));
996         QCOMPARE(error,  QContactManager::NoError);
997
998         addedIds.append(c.localId());
999     }
1000     QList<QContactLocalId> toApiRemove;
1001     toApiRemove.append(addedIds.takeLast());
1002     toApiRemove.append(addedIds.takeLast());
1003     QList<QContactLocalId> toPluginRemove(addedIds);
1004
1005     // Remove all, but last of the added contacts
1006     QMap<int, QContactManager::Error> errorMap;
1007     QContactManager::Error error(QContactManager::UnspecifiedError);
1008     QVERIFY(engine()->removeContacts(toPluginRemove, &errorMap, &error));
1009     QCOMPARE(error,  QContactManager::NoError);
1010     for (int i = 0; i < errorMap.count(); i++) {
1011         QCOMPARE(toPluginRemove[i], 0U);
1012     }
1013
1014     error = QContactManager::UnspecifiedError;
1015     QVERIFY(engine()->removeContacts(toApiRemove, &errorMap, &error));
1016     QCOMPARE(error, QContactManager::NoError);
1017     for (int i = 0; i < errorMap.count(); i++) {
1018         QCOMPARE(toApiRemove[i], 0U);
1019     }
1020 }
1021
1022 void ut_qtcontacts_trackerplugin::testAvatar()
1023 {
1024     QContact contactWithAvatar;
1025     QContactAvatar avatar;
1026
1027     avatar.setImageUrl(QUrl("file:///home/user/.contacts/avatars/default_avatar.png"));
1028     contactWithAvatar.saveDetail(&avatar);
1029     QContactName name;
1030     name.setFirstName("John");name.setLastName("A Frog");
1031     contactWithAvatar.saveDetail(&name);
1032     QContactManager::Error error(QContactManager::UnspecifiedError);
1033     QVERIFY(engine()->saveContact( &contactWithAvatar, &error));
1034     QCOMPARE(error,  QContactManager::NoError);
1035
1036     const QtMobility::QContactFetchHint hints;
1037     error = QContactManager::UnspecifiedError;
1038     QContact c = engine()->contactImpl( contactWithAvatar.localId(), hints, &error);
1039     QCOMPARE(error,  QContactManager::NoError);
1040     QList<QContactAvatar> avatars = c.details<QContactAvatar>();
1041     QVERIFY( avatars.size() );
1042     QCOMPARE( avatars[0].imageUrl(), avatar.imageUrl() );
1043 }
1044
1045 void ut_qtcontacts_trackerplugin::testOrganization()
1046 {
1047     // Company information
1048     QContact contactWithCompany1;
1049     QContactOrganization company1;
1050     company1.setName("Nokia");
1051     company1.setDepartment(QStringList() << "Mobile");
1052     company1.setRole("Developer");
1053     QContactName name;
1054     name.setFirstName("John");
1055     name.setLastName("TestCompany1");
1056     contactWithCompany1.saveDetail(&name);
1057     contactWithCompany1.saveDetail(&company1);
1058     QContactManager::Error error(QContactManager::UnspecifiedError);
1059     QVERIFY(engine()->saveContact(&contactWithCompany1, &error));
1060     QCOMPARE(error,  QContactManager::NoError);
1061
1062     QContactLocalId id1 = contactWithCompany1.localId();
1063     QCOMPARE(contact(id1).detail<QContactOrganization>().name(), QString("Nokia"));
1064     QCOMPARE(contact(id1).detail<QContactOrganization>().department(), QStringList() << "Mobile");
1065     QCOMPARE(contact(id1).detail<QContactOrganization>().role(), QString("Developer"));
1066 }
1067
1068 void ut_qtcontacts_trackerplugin::testUrl()
1069 {
1070     //Context home, homepage url
1071     QContact contactWithUrl1;
1072     QContactUrl url1;
1073     url1.setUrl("http://home.homepage");
1074     url1.setContexts(QContactDetail::ContextHome);
1075     url1.setSubType(QContactUrl::SubTypeHomePage);
1076     QContactName name;
1077     name.setFirstName("John");name.setLastName("TestUrl1");
1078     contactWithUrl1.saveDetail(&name);
1079     contactWithUrl1.saveDetail(&url1);
1080     QContactManager::Error error(QContactManager::UnspecifiedError);
1081     QVERIFY(engine()->saveContact(&contactWithUrl1, &error));
1082     QCOMPARE(error,  QContactManager::NoError);
1083
1084     //Context work, homepage url
1085     QContact contactWithUrl2;
1086     QContactUrl url2;
1087     url2.setUrl("http://work.homepage");
1088     url2.setContexts(QContactDetail::ContextWork);
1089     url2.setSubType(QContactUrl::SubTypeHomePage);
1090     QContactName name2;
1091     name2.setLastName("TestUrl2");
1092     contactWithUrl2.saveDetail(&name2);
1093     contactWithUrl2.saveDetail(&url2);
1094     error = QContactManager::UnspecifiedError;
1095     QVERIFY(engine()->saveContact(&contactWithUrl2, &error));
1096     QCOMPARE(error,  QContactManager::NoError);
1097
1098     //Context home, favourite url
1099     QContact contactWithUrl3;
1100     QContactUrl url3;
1101     url3.setUrl("http://home.favourite");
1102     url3.setContexts(QContactDetail::ContextHome);
1103     url3.setSubType(QContactUrl::SubTypeFavourite);
1104
1105     name2.setLastName("TestUrl3");
1106     contactWithUrl3.saveDetail(&name2);
1107     contactWithUrl3.saveDetail(&url3);
1108     error = QContactManager::UnspecifiedError;
1109     QVERIFY(engine()->saveContact(&contactWithUrl3, &error));
1110     QCOMPARE(error,  QContactManager::NoError);
1111
1112
1113     QContactLocalId id1 = contactWithUrl1.localId();
1114     QContactLocalId id2 = contactWithUrl2.localId();
1115     QContactLocalId id3 = contactWithUrl3.localId();
1116     QCOMPARE(contact(id1).detail<QContactUrl>().url(), QString("http://home.homepage"));
1117     QCOMPARE(contact(id2).detail<QContactUrl>().url(), QString("http://work.homepage"));
1118     QCOMPARE(contact(id3).detail<QContactUrl>().url(), QString("http://home.favourite"));
1119
1120     QVERIFY(contact(id1).detail<QContactUrl>().contexts()[0] == QContactDetail::ContextHome );
1121     QVERIFY(contact(id2).detail<QContactUrl>().contexts()[0] == QContactDetail::ContextWork );
1122     QVERIFY(contact(id3).detail<QContactUrl>().contexts()[0] == QContactDetail::ContextHome );
1123
1124     QVERIFY(contact(id1).detail<QContactUrl>().subType() == QContactUrl::SubTypeHomePage );
1125     QVERIFY(contact(id2).detail<QContactUrl>().subType() == QContactUrl::SubTypeHomePage );
1126     QVERIFY(contact(id3).detail<QContactUrl>().subType() == QContactUrl::SubTypeFavourite );
1127
1128     // edit type
1129     QContact c1 = contact(id1);
1130     QCOMPARE(c1.details<QContactUrl>().size(), 1);
1131     QContactUrl detail = c1.detail<QContactUrl>();
1132     detail.setContexts(QContactDetail::ContextWork);
1133
1134     c1.saveDetail(&detail);
1135     QVERIFY(c1.detail<QContactUrl>().contexts().size() == 1);
1136     error = QContactManager::UnspecifiedError;
1137     QVERIFY(engine()->saveContact(&c1, &error));
1138     QCOMPARE(error,  QContactManager::NoError);
1139     c1 = contact(id1);
1140     QVERIFY(c1.details<QContactUrl>().size() == 1);
1141
1142
1143     // add additional URL (NB#178354)
1144     detail = QContactUrl();
1145     detail.setUrl("http://meego.com/");
1146     QVERIFY(c1.saveDetail(&detail));
1147
1148     error = QContactManager::UnspecifiedError;
1149     QVERIFY(engine()->saveContact(&c1, &error));
1150     QCOMPARE(error,  QContactManager::NoError);
1151
1152     QSet<QString> expectedUrls = QSet<QString>() << url1.url() << detail.url();
1153     QSet<QString> fetchedUrls;
1154
1155     foreach(const QContactUrl &u, contact(id1).details<QContactUrl>()) {
1156         fetchedUrls.insert(u.url());
1157     }
1158
1159     QCOMPARE(fetchedUrls, expectedUrls);
1160 }
1161
1162 void ut_qtcontacts_trackerplugin::testUniqueDetails_data()
1163 {
1164     QTest::addColumn<QString>("definitionName");
1165     QTest::addColumn<QString>("fieldName");
1166     QTest::addColumn<QVariant>("firstValue");
1167     QTest::addColumn<QVariant>("secondValue");
1168
1169     QTest::newRow("organization")
1170             << QString(QContactOrganization::DefinitionName.latin1())
1171             << QString(QContactOrganization::FieldTitle.latin1())
1172             << QVariant("First Organization")
1173             << QVariant("Second Organization");
1174
1175     QTest::newRow("note")
1176             << QString(QContactNote::DefinitionName.latin1())
1177             << QString(QContactNote::FieldNote.latin1())
1178             << QVariant("First Note")
1179             << QVariant("Second Note");
1180 }
1181
1182 void ut_qtcontacts_trackerplugin::testUniqueDetails()
1183 {
1184     QFETCH(QString, definitionName);
1185     QFETCH(QString, fieldName);
1186     QFETCH(QVariant, firstValue);
1187     QFETCH(QVariant, secondValue);
1188
1189     QContact savedContact;
1190
1191     QContactDetail first(definitionName);
1192     first.setValue(fieldName, firstValue);
1193     QVERIFY(savedContact.saveDetail(&first));
1194
1195     QContactDetail second(definitionName);
1196     second.setValue(fieldName, secondValue);
1197     QVERIFY(savedContact.saveDetail(&second));
1198
1199     const QString warning = "libqtcontacts-tracker: Dropping odd details for contact 0: "
1200                             "%1 detail must be unique";
1201     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning.arg(definitionName)));
1202
1203     qctLogger().setShowLocation(false);
1204     QContactManager::Error error(QContactManager::UnspecifiedError);
1205     bool success(engine()->saveContact(&savedContact, &error));
1206     qctLogger().setShowLocation(true);
1207
1208     QCOMPARE(error, QContactManager::NoError);
1209     QVERIFY(0 != savedContact.localId());
1210     QCOMPARE(success, true);
1211
1212     error = QContactManager::UnspecifiedError;
1213     QContact fetchedContact(engine()->contactImpl(savedContact.localId(),
1214                                                   QContactFetchHint(), &error));
1215     QCOMPARE(error, QContactManager::NoError);
1216     QCOMPARE(fetchedContact.localId(), savedContact.localId());
1217
1218     QCOMPARE(fetchedContact.details(definitionName).count(), 1);
1219     QCOMPARE(fetchedContact.detail(definitionName).variantValue(fieldName), firstValue);
1220 }
1221
1222 template<class K, class V> static QHash<K, V>
1223 toHash(const QMap<K,V> &map)
1224 {
1225     QHash<K,V> hash;
1226
1227     for(typename QMap<K,V>::ConstIterator i = map.begin();
1228     i != map.end(); ++i) {
1229         hash.insert(i.key(), i.value());
1230     }
1231
1232     return hash;
1233 }
1234
1235 void ut_qtcontacts_trackerplugin::testCustomDetails()
1236 {
1237     QContact savedContact;
1238
1239     QContactDetail simple("FavoriteColor");
1240     simple.setValue(simple.definitionName(), "Blue");
1241     QVERIFY(savedContact.saveDetail(&simple));
1242
1243     QContactDetail complex("AllTimeFavorites");
1244     complex.setValue("Song", "Underworld - Born Slippy");
1245     complex.setValue("Phone", "Nokia N900");
1246     complex.setValue("Place", "Home");
1247     QVERIFY(savedContact.saveDetail(&complex));
1248
1249     QContactDetail multi("CreditCardNumber");
1250     multi.setValue(multi.definitionName(), "1122334455");
1251     QVERIFY(savedContact.saveDetail(&multi));
1252
1253     QContactDetail multi2(multi.definitionName());
1254     multi2.setValue(multi2.definitionName(), "5544332211");
1255     QVERIFY(savedContact.saveDetail(&multi2));
1256
1257     QContactManager::Error error(QContactManager::UnspecifiedError);
1258     bool success(engine()->saveContact(&savedContact, &error));
1259     QCOMPARE(error, QContactManager::NoError);
1260     QVERIFY(0 != savedContact.localId());
1261     QCOMPARE(success, true);
1262
1263     QContactFetchHint fetchHint;
1264     fetchHint.setDetailDefinitionsHint(QStringList() <<
1265                                        simple.definitionName() <<
1266                                        complex.definitionName() <<
1267                                        multi.definitionName());
1268
1269     error = QContactManager::UnspecifiedError;
1270     QContact fetchedContact(engine()->contactImpl(savedContact.localId(), fetchHint, &error));
1271     QCOMPARE(error, QContactManager::NoError);
1272     QCOMPARE(fetchedContact.localId(), savedContact.localId());
1273
1274     QCOMPARE(fetchedContact.details(simple.definitionName()).count(), 1);
1275     QCOMPARE(fetchedContact.detail(simple.definitionName()).variantValues(),
1276              simple.variantValues());
1277
1278     QCOMPARE(fetchedContact.details(complex.definitionName()).count(), 1);
1279     QCOMPARE(toHash(fetchedContact.detail(complex.definitionName()).variantValues()),
1280              toHash(complex.variantValues()));
1281
1282     QCOMPARE(fetchedContact.details(multi.definitionName()).count(), 2);
1283
1284     bool multiFound = false;
1285     bool multi2Found = false;
1286
1287     foreach(const QContactDetail &detail, fetchedContact.details(multi.definitionName())) {
1288         if (detail == multi) {
1289             multiFound = true;
1290             continue;
1291         }
1292
1293         if (detail == multi2) {
1294             multi2Found = true;
1295             continue;
1296         }
1297
1298         // yes this leaks, but this is ok when a test fails
1299         QVERIFY2(false, QTest::toString(detail.variantValues()));
1300     }
1301
1302     QVERIFY(multiFound);
1303     QVERIFY(multi2Found);
1304 }
1305
1306 typedef QPair<QStringList, QStringList> SubTypeSample;
1307 typedef QList<SubTypeSample> SubTypeSampleList;
1308
1309 Q_DECLARE_METATYPE(SubTypeSampleList);
1310
1311 void ut_qtcontacts_trackerplugin::testRemoveSubType_data()
1312 {
1313     QTest::addColumn<QString>("detailName");
1314     QTest::addColumn<QString>("valueField");
1315     QTest::addColumn<QVariant>("value");
1316     QTest::addColumn<QString>("subTypesField");
1317     QTest::addColumn<SubTypeSampleList>("samples");
1318
1319     QTest::newRow("phone number")
1320             << (QString::fromLatin1(QContactPhoneNumber::DefinitionName.latin1()))
1321             << (QString::fromLatin1(QContactPhoneNumber::FieldNumber.latin1()))
1322             << (QVariant(QString::fromLatin1("33445566")))
1323             << (QString::fromLatin1(QContactPhoneNumber::FieldSubTypes.latin1()))
1324             << (SubTypeSampleList() <<
1325                 qMakePair(QStringList() <<
1326                           QContactPhoneNumber::SubTypeFax <<
1327                           QContactPhoneNumber::SubTypeMobile,
1328                           QStringList() <<
1329                           QContactPhoneNumber::SubTypeFax <<
1330                           QContactPhoneNumber::SubTypeMessagingCapable <<
1331                           QContactPhoneNumber::SubTypeMobile <<
1332                           QContactPhoneNumber::SubTypeVoice) <<
1333                 qMakePair(QStringList() <<
1334                           QContactPhoneNumber::SubTypeMobile,
1335                           QStringList() <<
1336                           QContactPhoneNumber::SubTypeMessagingCapable <<
1337                           QContactPhoneNumber::SubTypeMobile <<
1338                           QContactPhoneNumber::SubTypeVoice) <<
1339                 qMakePair(QStringList() <<
1340                           QContactPhoneNumber::SubTypeLandline,
1341                           QStringList() <<
1342                           QContactPhoneNumber::SubTypeVoice));
1343
1344     QTest::newRow("street address")
1345             << (QString::fromLatin1(QContactAddress::DefinitionName.latin1()))
1346             << (QString::fromLatin1(QContactAddress::FieldCountry.latin1()))
1347             << (QVariant(QString::fromLatin1("Finnland")))
1348             << (QString::fromLatin1(QContactAddress::FieldSubTypes.latin1()))
1349             << (SubTypeSampleList() <<
1350                 qMakePair(QStringList() <<
1351                           QContactAddress::SubTypeInternational <<
1352                           QContactAddress::SubTypeParcel,
1353                           QStringList() <<
1354                           QContactAddress::SubTypeInternational <<
1355                           QContactAddress::SubTypeParcel <<
1356                           QContactAddress::SubTypePostal) <<
1357                 qMakePair(QStringList() <<
1358                           QContactAddress::SubTypeInternational,
1359                           QStringList() <<
1360                           QContactAddress::SubTypeInternational <<
1361                           QContactAddress::SubTypePostal));
1362
1363 }
1364
1365 void ut_qtcontacts_trackerplugin::testRemoveSubType()
1366 {
1367     QFETCH(QString, detailName);
1368     QFETCH(QString, valueField);
1369     QFETCH(QVariant, value);
1370     QFETCH(QString, subTypesField);
1371     QFETCH(SubTypeSampleList, samples);
1372
1373     foreach(const SubTypeSample &sample, samples) {
1374         QContactDetail detail(detailName);
1375         detail.setValue(valueField, value);
1376         detail.setValue(subTypesField, sample.first);
1377
1378         QContact savedContact;
1379         QVERIFY(savedContact.saveDetail(&detail));
1380
1381         QContactManager::Error error(QContactManager::NoError);
1382         QVERIFY(engine()->saveContact(&savedContact, &error));
1383         QCOMPARE(error, QContactManager::NoError);
1384         QVERIFY(0 != savedContact.localId());
1385
1386         QContact fetchedContact(engine()->contactImpl(savedContact.localId(),
1387                                                            QContactFetchHint(), &error));
1388
1389         QCOMPARE(error, QContactManager::NoError);
1390         QCOMPARE(fetchedContact.localId(), savedContact.localId());
1391
1392         detail = fetchedContact.detail(detailName);
1393         QCOMPARE(detail.value<QStringList>(subTypesField).toSet(), sample.second.toSet());
1394         QCOMPARE(detail.variantValue(valueField), value);
1395     }
1396 }
1397
1398 void ut_qtcontacts_trackerplugin::testTags()
1399 {
1400     static const QLatin1String favorite("favourite");
1401
1402     QContactName name;
1403     name.setFirstName("Tuck");
1404     name.setLastName("Sherwood");
1405     name.resetKey(); // XXX workaround for qtcontacts bug
1406
1407     QContactTag tag;
1408     tag.setTag(favorite);
1409     tag.resetKey(); // XXX workaround for qtcontacts bug
1410
1411     // save contact with favorite tag
1412     QContact savedContact;
1413     QVERIFY(savedContact.saveDetail(&name));
1414     QVERIFY(savedContact.saveDetail(&tag));
1415
1416     QList<QContactTag> favoriteTags;
1417     favoriteTags = savedContact.details<QContactTag>(QContactTag::FieldTag, favorite);
1418     QCOMPARE(favoriteTags.count(), 1);
1419
1420     QContactManager::Error error(QContactManager::UnspecifiedError);
1421     QVERIFY(engine()->saveContact(&savedContact, &error));
1422     QCOMPARE(error, QContactManager::NoError);
1423     QVERIFY(0 != savedContact.localId());
1424
1425     // fetch contact with favorite tag
1426     QContact fetchedContact(engine()->contactImpl(savedContact.localId(),
1427                                                   QContactFetchHint(), &error));
1428     QCOMPARE(fetchedContact.localId(), savedContact.localId());
1429     QCOMPARE(error, QContactManager::NoError);
1430
1431     favoriteTags = fetchedContact.details<QContactTag>(QContactTag::FieldTag, favorite);
1432     QCOMPARE(favoriteTags.count(), 1);
1433
1434     // save same contact without favorite tag
1435     QVERIFY(savedContact.removeDetail(&tag));
1436     favoriteTags = savedContact.details<QContactTag>(QContactTag::FieldTag, favorite);
1437     QCOMPARE(favoriteTags.count(), 0);
1438
1439     QVERIFY(engine()->saveContact(&savedContact, &error));
1440     QCOMPARE(error, QContactManager::NoError);
1441     QVERIFY(0 != savedContact.localId());
1442
1443     // fetch contact without favorite tag
1444     fetchedContact = engine()->contactImpl(savedContact.localId(),
1445                                            QContactFetchHint(), &error);
1446     QCOMPARE(fetchedContact.localId(), savedContact.localId());
1447     QCOMPARE(error, QContactManager::NoError);
1448
1449     favoriteTags = fetchedContact.details<QContactTag>(QContactTag::FieldTag, favorite);
1450     QCOMPARE(favoriteTags.count(), 0);
1451 }
1452
1453 /*
1454 void ut_qtcontacts_trackerplugin::testGroups()
1455 {
1456     qDebug() << "Not implemented";
1457     QVERIFY(false);
1458 }
1459
1460 void ut_qtcontacts_trackerplugin::testGroup()
1461 {
1462     qDebug() << "Not implemented";
1463     QVERIFY(false);
1464 }
1465
1466 void ut_qtcontacts_trackerplugin::testSaveGroup()
1467 {
1468     qDebug() << "Not implemented";
1469     QVERIFY(false);
1470 }
1471
1472 void ut_qtcontacts_trackerplugin::testRemoveGroup()
1473 {
1474     qDebug() << "Not implemented";
1475     QVERIFY(false);
1476 }
1477
1478 void ut_qtcontacts_trackerplugin::testDetailDefinitions()
1479 {
1480     qDebug() << "Not implemented";
1481     QVERIFY(false);
1482 }
1483
1484 void ut_qtcontacts_trackerplugin::testDetailDefinition()
1485 {
1486     qDebug() << "Not implemented";
1487     QVERIFY(false);
1488 }
1489
1490 void ut_qtcontacts_trackerplugin::testSaveDetailDefinition()
1491 {
1492     qDebug() << "Not implemented";
1493     QVERIFY(false);
1494 }
1495
1496 void ut_qtcontacts_trackerplugin::testRemoveDetailDefinition()
1497 {
1498     qDebug() << "Not implemented";
1499     QVERIFY(false);
1500 }
1501 */
1502
1503 void ut_qtcontacts_trackerplugin::testSyncContactManagerContactsAddedSince()
1504 {
1505     QDateTime start;
1506     QList<QContactLocalId> addedIds;
1507     syncContactsAddedSinceHelper(start, addedIds);
1508
1509     QContactChangeLogFilter filter(QContactChangeLogFilter::EventAdded);
1510     filter.setSince(start);
1511
1512     QList<QContactSortOrder> sortOrder;
1513     
1514     QContactManager::Error error(QContactManager::UnspecifiedError);
1515     QList<QContactLocalId> contactIds = engine()->contactIds(filter, sortOrder, &error);
1516     QCOMPARE(error, QContactManager::NoError);
1517     QCOMPARE(contactIds.size(), addedIds.size());
1518 }
1519
1520 void ut_qtcontacts_trackerplugin::testSyncTrackerEngineContactsIdsAddedSince()
1521 {
1522     QDateTime start;
1523     QList<QContactLocalId> addedIds;
1524     syncContactsAddedSinceHelper(start, addedIds);
1525
1526     QContactChangeLogFilter filter(QContactChangeLogFilter::EventAdded);
1527     filter.setSince(start);
1528
1529     QList<QContactSortOrder> sortOrder;
1530     QContactManager::Error error(QContactManager::UnspecifiedError);
1531     QList<QContactLocalId> contactIds = engine()->contactIds( filter, sortOrder, &error);
1532     QCOMPARE(contactIds.size(), addedIds.size());
1533 }
1534
1535 void ut_qtcontacts_trackerplugin::testSyncContactManagerContactIdsAddedSince()
1536 {
1537     QDateTime start;
1538     QList<QContactLocalId> addedIds;
1539     syncContactsAddedSinceHelper(start, addedIds);
1540     QContactChangeLogFilter filter(QContactChangeLogFilter::EventAdded);
1541     filter.setSince(start);
1542
1543     QList<QContactSortOrder> sortOrder;
1544     QContactManager::Error error(QContactManager::UnspecifiedError);
1545     QList<QContactLocalId> contactIds = engine()->contactIds(filter, sortOrder, &error);
1546     QCOMPARE(error,  QContactManager::NoError);
1547     QCOMPARE(contactIds.size(), addedIds.size());
1548 }
1549
1550
1551 void ut_qtcontacts_trackerplugin::syncContactsAddedSinceHelper(QDateTime& start, QList<QContactLocalId>& addedIds)
1552 {
1553     for (int i = 0; i < 3; i++) {
1554         QContact c;
1555         QContactName name;
1556         name.setFirstName("A"+QString::number(i));
1557         QVERIFY2(c.saveDetail(&name), qPrintable(name.firstName()));
1558         QContactManager::Error error(QContactManager::UnspecifiedError);
1559         QVERIFY2(engine()->saveContact(&c, &error), qPrintable(name.firstName()));
1560         QCOMPARE(error,  QContactManager::NoError);
1561     }
1562
1563     QTest::qWait(1000);
1564     start = QDateTime::currentDateTime();
1565
1566     for (int i = 0; i < 3; i++) {
1567         QContact c;
1568         QContactName name;
1569         name.setFirstName("B"+QString::number(i));
1570         QVERIFY2(c.saveDetail(&name), qPrintable(name.firstName()));
1571         QContactManager::Error error(QContactManager::UnspecifiedError);
1572         QVERIFY2(engine()->saveContact(&c, &error), qPrintable(name.firstName()));
1573         QCOMPARE(error,  QContactManager::NoError);
1574         addedIds.append(c.localId());
1575     }
1576 }
1577
1578 void ut_qtcontacts_trackerplugin::testContactsAddedSince()
1579 {
1580     QList<QContactLocalId> addedIds;
1581     QDateTime start;
1582     for (int i = 0; i < 3; i++) {
1583         QContact c;
1584         QContactName name;
1585         name.setFirstName("A"+QString::number(i));
1586         QVERIFY2(c.saveDetail(&name), qPrintable(name.firstName()));
1587         QContactManager::Error error(QContactManager::UnspecifiedError);
1588         QVERIFY2(engine()->saveContact(&c, &error), qPrintable(name.firstName()));
1589         QCOMPARE(error,  QContactManager::NoError);
1590     }
1591
1592     QTest::qWait(2000);
1593     start = QDateTime::currentDateTime().addSecs(-1);
1594
1595     for (int i = 0; i < 3; i++) {
1596         QContact c;
1597         QContactName name;
1598         name.setFirstName("B"+QString::number(i));
1599         QVERIFY2(c.saveDetail(&name), qPrintable(name.firstName()));
1600         QContactManager::Error error(QContactManager::UnspecifiedError);
1601         QVERIFY2(engine()->saveContact(&c, &error), qPrintable(name.firstName()));
1602         QCOMPARE(error,  QContactManager::NoError);
1603         addedIds.append(c.localId());
1604     }
1605     QTest::qWait(2000);
1606
1607     for(int i = 0; i < 100; i++)
1608     {
1609         usleep(20000);
1610         QCoreApplication::processEvents();
1611     }
1612
1613
1614     // now one asynchronous request to read all the
1615     QContactFetchRequest request;
1616     QContactChangeLogFilter filter(QContactChangeLogFilter::EventAdded);
1617     filter.setSince(start);
1618     request.setFilter(filter);
1619
1620     // here You specify which details are of interest
1621     QStringList details;
1622     details << QContactAvatar::DefinitionName
1623             << QContactBirthday::DefinitionName
1624             << QContactAddress::DefinitionName
1625             << QContactEmailAddress::DefinitionName
1626             << QContactDisplayLabel::DefinitionName
1627             << QContactGender::DefinitionName
1628             << QContactAnniversary::DefinitionName
1629             << QContactName::DefinitionName
1630             << QContactOnlineAccount::DefinitionName
1631             << QContactOrganization::DefinitionName
1632             << QContactPhoneNumber::DefinitionName;
1633     QContactFetchHint fetchHint;
1634     fetchHint.setDetailDefinitionsHint(details);
1635     request.setFetchHint(fetchHint);
1636
1637
1638     Slots slot;
1639     QObject::connect(&request, SIGNAL(resultsAvailable()),
1640             &slot, SLOT(resultsAvailable()));
1641
1642     // start. clients should, instead of following use
1643     // request.setManager(trackermanagerinstance);
1644     // request.start();
1645     engine()->startRequest(&request);
1646     engine()->waitForRequestFinished(&request, 10000);
1647     // if it takes more, then something is wrong
1648     QVERIFY(request.isFinished());
1649     QCOMPARE(request.error(), QContactManager::NoError);
1650     QCOMPARE(slot.contacts.count(), addedIds.count());
1651
1652     foreach(QContact cont, slot.contacts) {
1653         QVERIFY2(addedIds.contains(cont.localId()), "One of the added contacts was not reported as added");
1654     }
1655
1656     QContactLocalIdFetchRequest idreq;
1657     filter.setSince(start);
1658     idreq.setFilter(filter);
1659
1660     Slots slot2;
1661     QObject::connect(&idreq, SIGNAL(resultsAvailable()),
1662             &slot2, SLOT(idResultsAvailable()));
1663     engine()->startRequest(&idreq);
1664     engine()->waitForRequestFinished(&idreq, 10000);
1665     QVERIFY(idreq.isFinished());
1666     QCOMPARE(slot2.ids.count(), addedIds.count());
1667     foreach(QContactLocalId id, slot2.ids) {
1668         QVERIFY2(addedIds.contains(id), "One of the added contacts was not reported as added");
1669     }
1670
1671 }
1672
1673 void ut_qtcontacts_trackerplugin::testContactsModifiedSince()
1674 {
1675     QDateTime start;
1676     QList<QContactLocalId> addedIds;
1677     QList<QContactLocalId> modified;
1678
1679     const int contactsToAdd = 5;
1680     const int contactsToModify = 3;
1681     QVERIFY2(contactsToAdd >= contactsToModify, "Cannot modify more contacts than this test has added");
1682     QVERIFY2(contactsToModify+1 <= contactsToAdd, "Cannot modify more contacts than this test has added");
1683
1684     // Add contacts with only first name and store them to list of added
1685     for (int i = 0; i < contactsToAdd; i++) {
1686         QContact c;
1687         QContactName name;
1688         name.setFirstName("A"+QString::number(i));
1689         QVERIFY2(c.saveDetail(&name), qPrintable(name.firstName()));
1690         QContactManager::Error error(QContactManager::UnspecifiedError);
1691         QVERIFY2(engine()->saveContact(&c, &error), qPrintable(name.firstName()));
1692         QCOMPARE(error,  QContactManager::NoError);
1693         addedIds.append(c.localId());
1694     }
1695
1696     QTest::qWait(2000);
1697     start = QDateTime::currentDateTime();
1698
1699    // Modify and save rest of the contacts
1700     for (int i = 0; i < contactsToModify; i++) {
1701         QContact c = contact(addedIds[i]);
1702         QContactName name = c.detail<QContactName>();
1703         // Modify name
1704         name.setFirstName("B"+QString::number(i));
1705         QVERIFY2(c.saveDetail(&name), qPrintable(name.firstName()));
1706         QContactManager::Error error(QContactManager::UnspecifiedError);
1707         QVERIFY2(engine()->saveContact(&c, &error), qPrintable(name.firstName()));
1708         QCOMPARE(error,  QContactManager::NoError);
1709         modified.append(c.localId());
1710     }
1711     // Set filter
1712     QContactChangeLogFilter filter(QContactChangeLogFilter::EventChanged);
1713     filter.setSince(start);
1714
1715     QContactLocalIdFetchRequest idfetch;
1716     QContactFetchRequest fetch;
1717     idfetch.setFilter(filter);
1718     fetch.setFilter(filter);
1719     engine()->startRequest(&idfetch);
1720     engine()->waitForRequestFinished(&idfetch, 10000);
1721     QVERIFY2(idfetch.isFinished(), "Id fetch request did not finish on time");
1722     QVERIFY2(idfetch.error() == QContactManager::NoError, "Id fetch request finished with errors");
1723     QList<QContactLocalId> actuallyModifiedIds = idfetch.ids();
1724     engine()->startRequest(&fetch);
1725     engine()->waitForRequestFinished(&fetch, 10000);
1726     QVERIFY2(fetch.isFinished(), "Fetch request did not finish on time");
1727     QVERIFY2(fetch.error() == QContactManager::NoError, "Fetch request finished with errors");
1728     QList<QContact> actuallyModified = fetch.contacts();
1729
1730     // Num of actually modified should be same as supposedly modified
1731     QCOMPARE(actuallyModifiedIds.count(), modified.count());
1732     QCOMPARE(actuallyModified.count(), modified.count());
1733     // All the ids of the modified contacts should be found in the result list
1734     foreach (QContactLocalId id, modified) {
1735         QVERIFY2(actuallyModifiedIds.contains(id), "One the modified contacts was not reported as modified");
1736     }
1737 }
1738
1739 void ut_qtcontacts_trackerplugin::testContactsRemovedSince()
1740 {
1741     QDateTime start = QDateTime::currentDateTime();
1742     QContactChangeLogFilter filter(QContactChangeLogFilter::EventRemoved);
1743     filter.setSince(start);
1744     QList<QContactSortOrder> sorts;
1745
1746     QTest::ignoreMessage(QtWarningMsg,
1747                          "libqtcontacts-tracker: QContactFilter::ChangeLogFilter: "
1748                          "Unsupported event type: QContactChangeLogFilter::EventRemoved");
1749
1750     qctLogger().setShowLocation(false);
1751     QContactManager::Error error(QContactManager::UnspecifiedError);
1752     QList<QContactLocalId> actuallyRemoved = engine()->contactIds(filter, sorts, &error);
1753     qctLogger().setShowLocation(true);
1754
1755     QCOMPARE(error,  QContactManager::NotSupportedError);
1756     QVERIFY(actuallyRemoved.isEmpty());
1757 }
1758 /*
1759 void ut_qtcontacts_trackerplugin::testGroupsAddedSince()
1760 {
1761     qDebug() << "Not implemented";
1762     QVERIFY(false);
1763 }
1764
1765 void ut_qtcontacts_trackerplugin::testGroupsModifiedSince()
1766 {
1767     qDebug() << "Not implemented";
1768     QVERIFY(false);
1769 }
1770
1771 void ut_qtcontacts_trackerplugin::testGroupsRemovedSince()
1772 {
1773     qDebug() << "Not implemented";
1774     QVERIFY(false);
1775 }
1776 */
1777
1778 void ut_qtcontacts_trackerplugin::cleanupTestCase()
1779 {
1780     qctLogger().setShowLocation(true);
1781     resetEngine();
1782 }
1783
1784 void ut_qtcontacts_trackerplugin::cleanup()
1785 {
1786     QContactManager::Error error;
1787
1788     foreach (QContactLocalId id, addedContacts) {
1789         engine()->removeContact(id, &error);
1790     }
1791
1792     addedContacts.clear();
1793 }
1794
1795
1796 void ut_qtcontacts_trackerplugin::testNcoTypes()
1797 {
1798     using namespace SopranoLive;
1799
1800     QList<QContactLocalId> ids;
1801     RDFVariable RDFContact = RDFVariable::fromType<nco::PersonContact>();
1802     RDFSelect query;
1803
1804     query.addColumn("contact_uri", RDFContact);
1805     query.addColumn("contactId", RDFContact.property<nco::contactLocalUID>());
1806     LiveNodes ncoContacts = ::BackEnds::Tracker::tracker()->modelQuery(query);
1807     foreach( Live<nco::PersonContact> p, ncoContacts ) {
1808         QVERIFY(p.hasType<nco::Contact>());
1809         QVERIFY(p.hasType<nco::Role>());
1810         QVERIFY(p.hasType<nco::PersonContact>());
1811     }
1812 }
1813
1814 void ut_qtcontacts_trackerplugin::testClassHierarchy()
1815 {
1816     const QTrackerClassHierarchy &classes(engine()->classes());
1817
1818     // test isSubClassOf()
1819     QVERIFY(classes.isSubClassOf(nco::PersonContact::iri(), nco::Contact::iri()));
1820     QVERIFY(classes.isSubClassOf(nco::VoicePhoneNumber::iri(), nco::PhoneNumber::iri()));
1821     QVERIFY(classes.isSubClassOf(nco::CellPhoneNumber::iri(), nco::VoicePhoneNumber::iri()));
1822
1823     // test inheritedClasses()
1824     QCOMPARE(classes.inheritedClasses(nco::PersonContact::iri(), QStringList()),
1825              QSet<QUrl>() << nco::PersonContact::iri() <<
1826              nco::Contact::iri() << nco::Role::iri() <<
1827              nie::InformationElement::iri() <<
1828              rdfs::Resource::iri());
1829
1830     QCOMPARE(classes.inheritedClasses(nco::PersonContact::iri(),
1831                                       QStringList(QLatin1String(nco_namespace_prefix))),
1832              QSet<QUrl>() << nco::PersonContact::iri() <<
1833              nco::Contact::iri() << nco::Role::iri());
1834
1835     // test enum values
1836     int carPhoneNumberId = classes.getId(nco::CarPhoneNumber::iri());
1837     int genderFemaleId = classes.getId(nco::gender_female::iri());
1838
1839     QVERIFY(0 != carPhoneNumberId);
1840     QCOMPARE(classes.getIri(carPhoneNumberId), nco::CarPhoneNumber::iri());
1841
1842     QVERIFY(0 != genderFemaleId);
1843     QCOMPARE(classes.getIri(genderFemaleId), nco::gender_female::iri());
1844 }
1845
1846 void ut_qtcontacts_trackerplugin::testAsyncReadContacts()
1847 {
1848     addedContacts.clear();
1849     // Add at least one contact to be sure that this doesn't fail because tracker is clean
1850
1851     QStringList firstNames, lastNames;
1852     firstNames << "aa" << "ab" << "ac" << "dd" << "fe";
1853     lastNames << "fe" << "ab" << "dd" << "dd" << "aa";
1854     for (int i = 0; i < firstNames.count(); i++) {
1855         QContact c;
1856         QContactName name;
1857         name.setFirstName(firstNames.at(i));
1858         name.setLastName(lastNames.at(i));
1859         QContactAvatar avatar;
1860         avatar.setImageUrl(QUrl("default_avatar.png"));
1861         QVERIFY(c.saveDetail(&name));
1862         QVERIFY(c.saveDetail(&avatar));
1863         QContactManager::Error error(QContactManager::UnspecifiedError);
1864         QVERIFY(engine()->saveContact(&c, &error));
1865         QCOMPARE(error,  QContactManager::NoError);
1866         addedContacts.append(c.localId());
1867     }
1868     
1869     // Prepare the filter for the request - we really should test only the contact we add here.
1870     QContactLocalIdFilter filter;
1871     filter.setIds(addedContacts);
1872
1873     // this one will get complete contacts
1874
1875     Slots slot;
1876     QContactFetchRequest request;
1877     QList<QContactSortOrder> sorting;
1878     QContactSortOrder sort, sort1;
1879     sort.setDetailDefinitionName(QContactName::DefinitionName, QContactName::FieldLastName);
1880     sort1.setDetailDefinitionName(QContactName::DefinitionName, QContactName::FieldFirstName);
1881     sorting << sort << sort1;
1882     QStringList details; details << QContactName::DefinitionName << QContactAvatar::DefinitionName;
1883     QContactFetchHint fetchHint;
1884     fetchHint.setDetailDefinitionsHint(details);
1885     request.setFetchHint(fetchHint);
1886     request.setSorting(sorting);
1887     request.setFilter(filter);
1888
1889     QObject::connect(&request, SIGNAL(resultsAvailable()),
1890             &slot, SLOT(resultsAvailable()));
1891
1892     // this one only ids
1893     QContactLocalIdFetchRequest request1;
1894     request1.setFilter(filter);
1895     QObject::connect(&request1, SIGNAL(resultsAvailable()),
1896             &slot, SLOT(idResultsAvailable()));
1897
1898     // the purpose is to compare if all contacts are loaded, and
1899     // if optional fields are defined properly in request
1900
1901     // start both at once
1902     engine()->startRequest(&request);
1903     engine()->startRequest(&request1);
1904     engine()->waitForRequestFinished(&request, 10000);
1905     engine()->waitForRequestFinished(&request1, 10000);
1906
1907
1908     // if it takes more, then something is wrong
1909     QVERIFY(request.isFinished());
1910     QVERIFY(request1.isFinished());
1911
1912     // there need1 to be something added to be verified
1913     QVERIFY(!request.contacts().isEmpty());
1914     // now ask for one contact
1915     QVERIFY(!slot.contacts.isEmpty());
1916     // there need to be something added to be verified
1917     QVERIFY(!request1.ids().isEmpty());
1918     // now ask for one contact
1919     QVERIFY(!slot.ids.isEmpty());
1920
1921     QCOMPARE(slot.contacts.count(), slot.ids.count());
1922     QVERIFY(slot.contacts.count() >= firstNames.count());
1923     for( int i = 0; i < slot.contacts.size() -1 ; i++)
1924     {
1925         QContact contact = slot.contacts[i];
1926         QContact contact1 = slot.contacts[i+1];
1927         QString last0 = contact.detail<QContactName>().lastName();
1928         QString first0 = contact.detail<QContactName>().firstName();
1929         QString last1 = contact1.detail<QContactName>().lastName();
1930         QString first1 = contact1.detail<QContactName>().firstName();
1931         // sorting
1932         qDebug() << "contacts:" << contact.localId() << first0 << last0;
1933         bool test = last0 < last1 || (last0 == last1 && first0 <= first1);
1934         if (!test) {
1935             qDebug() << "contacts sort failed. First: " << contact1.localId() << first0 << last1 << "lasts: " << last0 << last1;
1936         }
1937         QVERIFY2(test, "Sorting failed.");
1938     }
1939
1940 }
1941
1942 void ut_qtcontacts_trackerplugin::testSortContacts()
1943 {
1944     // create sample contacts
1945     const QStringList samples = QStringList() << "Adelhoefer" << "Lose" << "Zumwinkel";
1946     QList<QContact> contacts;
1947
1948     foreach(const QString &lastName, samples) {
1949         QContactName name;
1950         name.setLastName(lastName);
1951         contacts.append(QContact());
1952         QVERIFY(contacts.last().saveDetail(&name));
1953     }
1954
1955     // save sample contacts
1956     QContactManager::Error error = QContactManager::UnspecifiedError;
1957     bool success = engine()->saveContacts(&contacts, 0, &error);
1958     QCOMPARE(error, QContactManager::NoError);
1959     QVERIFY(success);
1960
1961     // build local id list
1962     QContactLocalIdList localIds;
1963
1964     for(int i = 0; i < contacts.count(); ++i) {
1965         QVERIFY2(0 != contacts[i].localId(), qPrintable(QString::number(i)));
1966         localIds.append(contacts[i].localId());
1967     }
1968
1969     QCOMPARE(localIds.count(), samples.count());
1970
1971     // prepare fetch request
1972     QContactLocalIdFilter filter;
1973     filter.setIds(localIds);
1974
1975     QContactFetchHint fetchHint;
1976     fetchHint.setDetailDefinitionsHint(QStringList() << QContactName::DefinitionName);
1977
1978     QList<QContactSortOrder> sortOrders;
1979     sortOrders.append(QContactSortOrder());
1980
1981     sortOrders.last().setDetailDefinitionName(QContactName::DefinitionName,
1982                                               QContactName::FieldLastName);
1983
1984     // fetch contacts in ascending order
1985     error = QContactManager::UnspecifiedError;
1986     sortOrders.last().setDirection(Qt::AscendingOrder);
1987     contacts = engine()->contacts(filter, sortOrders, fetchHint, &error);
1988     QCOMPARE(error, QContactManager::NoError);
1989     QCOMPARE(contacts.count(), localIds.count());
1990
1991     // verify fetched contacts are in ascending order
1992     QContactLocalIdList fetchedIds;
1993     QStringList fetchedNames;
1994
1995     foreach(const QContact &c, contacts) {
1996         fetchedNames.append(c.detail<QContactName>().lastName());
1997         fetchedIds.append(c.localId());
1998     }
1999
2000     QCOMPARE(fetchedNames, samples);
2001     QCOMPARE(fetchedIds, localIds);
2002
2003     // fetch contacts in descending order
2004     error = QContactManager::UnspecifiedError;
2005     sortOrders.last().setDirection(Qt::DescendingOrder);
2006     contacts = engine()->contacts(filter, sortOrders, fetchHint, &error);
2007     QCOMPARE(error, QContactManager::NoError);
2008     QCOMPARE(contacts.count(), localIds.count());
2009
2010     // verify fetched contacts are in descending order
2011     fetchedNames.clear();
2012     fetchedIds.clear();
2013
2014     foreach(const QContact &c, contacts) {
2015         fetchedNames.prepend(c.detail<QContactName>().lastName());
2016         fetchedIds.prepend(c.localId());
2017     }
2018
2019     QCOMPARE(fetchedNames, samples);
2020     QCOMPARE(fetchedIds, localIds);
2021 }
2022
2023 void ut_qtcontacts_trackerplugin::testFilterContacts()
2024 {
2025     // this one will get complete contacts
2026     QContact c;
2027     QContactName name;
2028     name.setFirstName("Zuba");
2029     name.setLastName("Zub");
2030     c.saveDetail(&name);
2031     QContactPhoneNumber phone;
2032
2033     phone.setNumber("4872444");
2034     c.saveDetail(&phone);
2035
2036     QContactBirthday birthday;
2037     birthday.setDate(QDate(2010, 2, 14));
2038     c.saveDetail(&birthday);
2039
2040     QContactManager::Error error(QContactManager::UnspecifiedError);
2041     engine()->saveContact(&c, &error);
2042     QCOMPARE(error,  QContactManager::NoError);
2043
2044     QStringList details;
2045     details << QContactName::DefinitionName << QContactAvatar::DefinitionName
2046             << QContactPhoneNumber::DefinitionName;
2047
2048     QContactFetchRequest request;
2049     QContactDetailFilter filter;
2050     filter.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
2051
2052     Slots slot;
2053     QObject::connect(&request, SIGNAL(resultsAvailable()),
2054             &slot, SLOT(resultsAvailable()));
2055     filter.setValue(QString("4872444"));
2056     filter.setMatchFlags(QContactFilter::MatchEndsWith);
2057
2058     QContactFetchHint fetchHint;
2059     fetchHint.setDetailDefinitionsHint(details);
2060     request.setFetchHint(fetchHint);
2061     request.setFilter(filter);
2062
2063     engine()->startRequest(&request);
2064     engine()->waitForRequestFinished(&request, 1000);
2065
2066     // if it takes more, then something is wrong
2067     QVERIFY(request.isFinished());
2068     QVERIFY(!request.contacts().isEmpty());
2069
2070     QVERIFY(!slot.contacts.isEmpty());
2071
2072     bool containsThisId = false;
2073     foreach(const QContact &contact, slot.contacts)
2074     {
2075         if( contact.localId() == c.localId())
2076             containsThisId = true;
2077         bool containsPhone = false;
2078         foreach(const QContactDetail &detail, contact.details(QContactPhoneNumber::DefinitionName))
2079         {
2080             if(detail.value(QContactPhoneNumber::FieldNumber).contains("4872444"))
2081             {
2082                 containsPhone = true;
2083                 break;
2084             }
2085         }
2086         QVERIFY(containsPhone);
2087     }
2088     QVERIFY(containsThisId);
2089
2090     // filter by birthday range
2091     QContactDetailRangeFilter rangeFilter;
2092     rangeFilter.setDetailDefinitionName(QContactBirthday::DefinitionName,
2093                                         QContactBirthday::FieldBirthday);
2094     // include lower & exclude upper by default
2095     rangeFilter.setRange(QDate(2010, 2, 14), QDate(2010, 2, 15));
2096     QContactFetchHint fetchHintBirthday;
2097     fetchHintBirthday.setDetailDefinitionsHint(QStringList()<< QContactBirthday::DefinitionName);
2098     error = QContactManager::UnspecifiedError;
2099     QList<QContact> contacts(engine()->contacts(rangeFilter, QList<QContactSortOrder>(),
2100                                                 fetchHintBirthday, &error));
2101     QCOMPARE(error,  QContactManager::NoError);
2102     QVERIFY(!contacts.isEmpty());
2103     bool containsOurContact(false);
2104     foreach(const QContact &cont, contacts) {
2105         QCOMPARE(cont.detail<QContactBirthday>().date(), QDate(2010, 2, 14));
2106
2107         if(c.id() == cont.id()) {
2108             containsOurContact = true;
2109         }
2110     }
2111     QVERIFY(containsOurContact);
2112 }
2113
2114 void ut_qtcontacts_trackerplugin::testFilterContactsEndsWithAndPhoneNumber()
2115 {
2116     QTrackerContactSettings settings;
2117     QContactLocalId initialMatchCount = settings.localPhoneNumberLength();
2118
2119     QContact matchingContact;
2120     QContactName name;
2121     name.setFirstName("Zuba");
2122     name.setLastName("Zub");
2123     matchingContact.saveDetail(&name);
2124     QContactPhoneNumber phone;
2125     phone.setContexts(QContactPhoneNumber::ContextWork);
2126     phone.setNumber("3210987654321");
2127     matchingContact.saveDetail(&phone);
2128     QContactPhoneNumber phone1;
2129     phone1.setNumber("98653");
2130     matchingContact.saveDetail(&phone1);
2131     QContactManager::Error error(QContactManager::UnspecifiedError);
2132     QVERIFY(engine()->saveContact(&matchingContact, &error));
2133     QCOMPARE(error,  QContactManager::NoError);
2134
2135     QStringList details;
2136     details << QContactName::DefinitionName << QContactAvatar::DefinitionName
2137             << QContactPhoneNumber::DefinitionName;
2138
2139     QContactFetchRequest request;
2140     QContactDetailFilter filter;
2141     filter.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
2142     QContactFetchHint fetchHint;
2143     fetchHint.setDetailDefinitionsHint(details);
2144     // test matching of 7 last digits
2145     int matchCount = 7;
2146     qDebug() << "Test matching of" << matchCount << "last digits.";
2147     settings.setLocalPhoneNumberLength(matchCount);
2148
2149     // shorter phone numbers do exact match
2150     {
2151         filter.setValue("98653");
2152         filter.setMatchFlags(QContactFilter::MatchPhoneNumber);
2153         error = QContactManager::UnspecifiedError;
2154         QList<QContact> conts = engine()->contacts(filter, QList<QContactSortOrder>(), fetchHint, &error);
2155         QCOMPARE(error,  QContactManager::NoError);
2156         QVERIFY(!conts.isEmpty());
2157         bool containsMatchingC(false);
2158         foreach(const QContact &contact, conts) {
2159             if (contact.localId() == matchingContact.localId())
2160                 containsMatchingC = true;
2161             bool containsPhone = false;
2162             foreach(const QContactDetail &detail, contact.details(QContactPhoneNumber::DefinitionName)) {
2163                 // only exact match for shorter phone numbers
2164                 if (detail.value(QContactPhoneNumber::FieldNumber)=="98653") {
2165                     containsPhone = true;
2166                     break;
2167                 }
2168             }
2169             QVERIFY(containsPhone);
2170         }
2171         QVERIFY(containsMatchingC);
2172     }
2173
2174     // settings phone number match
2175     {
2176         filter.setValue("37654321");
2177         filter.setMatchFlags(QContactFilter::MatchPhoneNumber);
2178         error = QContactManager::UnspecifiedError;
2179         QList<QContact> conts = engine()->contacts(filter, QList<QContactSortOrder>(), fetchHint, &error);
2180         QCOMPARE(error,  QContactManager::NoError);
2181         QVERIFY(!conts.isEmpty());
2182         bool containsMatchingC(false);
2183         foreach(const QContact &contact, conts) {
2184             if (contact.localId() == matchingContact.localId())
2185                 containsMatchingC = true;
2186             bool containsPhone = false;
2187             foreach(const QContactDetail &detail, contact.details(QContactPhoneNumber::DefinitionName)) {
2188                 // only exact match for shorter phone numbers
2189                 if (normalizePhoneNumber(detail.value(QContactPhoneNumber::FieldNumber)).right(matchCount)=="7654321") {
2190                     containsPhone = true;
2191                     break;
2192                 }
2193             }
2194             QVERIFY(containsPhone);
2195         }
2196         QVERIFY(containsMatchingC);
2197     }
2198
2199     Slots slot;
2200     QObject::connect(&request, SIGNAL(resultsAvailable()),
2201             &slot, SLOT(resultsAvailable()));
2202
2203     {
2204
2205         QString matchValue = "3000007654321";
2206         QContact nonMatchingContact;
2207         nonMatchingContact.saveDetail(&name);
2208         phone.setNumber("3210980654321");
2209         nonMatchingContact.saveDetail(&phone);
2210         error = QContactManager::UnspecifiedError;
2211         QVERIFY(engine()->saveContact(&nonMatchingContact, &error));
2212         QCOMPARE(error,  QContactManager::NoError);
2213
2214         filter.setValue(matchValue);
2215         filter.setMatchFlags(QContactFilter::MatchEndsWith);
2216
2217         request.setFetchHint(fetchHint);
2218         request.setFilter(filter);
2219
2220         engine()->startRequest(&request);
2221         engine()->waitForRequestFinished(&request, 1000);
2222         QVERIFY(request.isFinished());
2223         QVERIFY(!slot.contacts.isEmpty());
2224
2225         bool containsMatchingId = false;
2226         bool containsNonMatchingId = false;
2227         foreach(const QContact &contact, slot.contacts) {
2228             if (contact.localId() == nonMatchingContact.localId())
2229                 containsNonMatchingId = true;
2230             if (contact.localId() == matchingContact.localId())
2231                 containsMatchingId = true;
2232             bool containsPhone = false;
2233             foreach(const QContactDetail &detail, contact.details(QContactPhoneNumber::DefinitionName)) {
2234                 if (detail.value(QContactPhoneNumber::FieldNumber).endsWith(matchValue.right(matchCount))) {
2235                     containsPhone = true;
2236                     break;
2237                 }
2238             }
2239             QVERIFY(containsPhone);
2240         }
2241         QVERIFY(containsMatchingId);
2242         QVERIFY(!containsNonMatchingId);
2243     }
2244
2245     {
2246         // test matching of 11 last digits
2247         matchCount = 11;
2248         qDebug() << "Test matching of" << matchCount << "last digits.";
2249         settings.setLocalPhoneNumberLength(matchCount);
2250         QString matchValue = "3010987654321";
2251         QContact nonMatchingContact;
2252         nonMatchingContact.saveDetail(&name);
2253         phone.setNumber("3200987654321");
2254         nonMatchingContact.saveDetail(&phone);
2255         error = QContactManager::UnspecifiedError;
2256         engine()->saveContact(&nonMatchingContact, &error);
2257         QCOMPARE(error,  QContactManager::NoError);
2258
2259         QContact matchingContactWithShorterNumber;
2260         QContactName name1;
2261         name1.setFirstName("ShortNumber");
2262         name1.setLastName("Zub1");
2263         matchingContactWithShorterNumber.saveDetail(&name1);
2264         QContactPhoneNumber phone1;