Changes: Use QString instead of QUrl for Iris
[qtcontacts-tracker:qtcontacts-tracker.git] / tests / ut_qtcontacts_trackerplugin_common / ut_qtcontacts_trackerplugin_common.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reservbed.
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_common.h"
43 #include "resourcecleanser.h"
44
45 #include <dao/logger.h>
46 #include <dao/sparqlconnectionmanager.h>
47 #include <dao/sparqlresolver.h>
48 #include <dao/subject.h>
49 #include <dao/support.h>
50
51 #include <QContactFetchRequest>
52 #include <QContactLocalIdFilter>
53 #include <QContactRemoveRequest>
54 #include <QContactSaveRequest>
55
56 #include <QVersitReader>
57 #include <QVersitContactImporter>
58
59 const QList<QContactSortOrder> ut_qtcontacts_trackerplugin_common::NoSortOrders;
60 const QContactFetchHint ut_qtcontacts_trackerplugin_common::NoFetchHint;
61
62 ut_qtcontacts_trackerplugin_common::ut_qtcontacts_trackerplugin_common(const QDir &dataDir,
63                                                                        const QDir &srcDir,
64                                                                        QObject *parent)
65     : QObject(parent)
66     , m_engine(0)
67     , m_dataDir(dataDir)
68     , m_srcDir(srcDir)
69     , m_uuid(QUuid::createUuid())
70 {
71     QDir localPluginDir = QDir::current();
72     localPluginDir.cd (QLatin1String("../../src/plugin"));
73
74     QStringList libraryPaths = qApp->libraryPaths();
75     libraryPaths.prepend(localPluginDir.absolutePath());
76
77     qApp->setLibraryPaths(libraryPaths);
78
79     // parse DEBUG envvar
80     const QStringList debugFlags =
81             QProcessEnvironment::systemEnvironment().
82             value(QLatin1String("DEBUG")).trimmed().toLower().
83             split(QRegExp(QLatin1String("\\s*(,|\\s)\\s*")));
84
85     m_verbose = debugFlags.contains(QLatin1String("verbose"));
86 }
87
88 ut_qtcontacts_trackerplugin_common::~ut_qtcontacts_trackerplugin_common()
89 {
90     Q_ASSERT(0 == m_engine);
91
92     // Really delete any deferred-deleted objects, which QTest doesn't seem
93     // to delete otherwise. This helps valgrind's leak check.
94     while (QCoreApplication::hasPendingEvents()) {
95         QCoreApplication::sendPostedEvents();
96         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
97         QCoreApplication::processEvents();
98     }
99 }
100
101 QString
102 ut_qtcontacts_trackerplugin_common::uniqueTestId(const QString& id) const
103 {
104     QString result = QLatin1String(metaObject()->className()) % QLatin1Char(':') % m_uuid;
105     if (not id.isEmpty()) {
106         result += QLatin1Char(':') % id;
107     }
108     return result;
109 }
110
111 QString
112 ut_qtcontacts_trackerplugin_common::makeUniqueName(const QString &id) const
113 {
114     static const int counterDigits = 5; // 99999 is latest, sorting will fail with higher numbers
115     static int counter = 0;
116     return (uniqueTestId(id) %
117             QLatin1Char('_') % QString::fromLatin1("%1").arg(counter++, counterDigits, 10, QLatin1Char('0')));
118 }
119
120 void
121 ut_qtcontacts_trackerplugin_common::setTestNicknameToContact(QContact &contact,
122                                                              const QString &id) const
123 {
124     QContactNickname nicknameDetail;
125     const QString nickname = makeUniqueName(id);
126     nicknameDetail.setNickname(nickname);
127     QVERIFY(contact.saveDetail(&nicknameDetail));
128 }
129
130 QContactDetailFilter
131 ut_qtcontacts_trackerplugin_common::testNicknameFilter(const QString &id) const
132 {
133     QContactDetailFilter filter;
134     filter.setDetailDefinitionName(QContactNickname::DefinitionName, QContactNickname::FieldNickname);
135     filter.setMatchFlags(QContactDetailFilter::MatchStartsWith);
136     filter.setValue(uniqueTestId(id));
137     return filter;
138 }
139
140 QMap<QString, QString> ut_qtcontacts_trackerplugin_common::makeEngineParams() const
141 {
142     return QMap<QString, QString>();
143 }
144
145 QContactTrackerEngine *
146 ut_qtcontacts_trackerplugin_common::engine() const
147 {
148     // need lazy initialization because *_data() is called before init() -- QTBUG-11186
149     if (0 == m_engine) {
150         m_engine = new QContactTrackerEngine(makeEngineParams());
151     }
152
153     return m_engine;
154 }
155
156 void
157 ut_qtcontacts_trackerplugin_common::resetEngine()
158 {
159     delete m_engine;
160     m_engine = 0;
161 }
162
163 void ut_qtcontacts_trackerplugin_common::cleanup()
164 {
165     if (m_engine) {
166         QEventLoop eventLoop;
167         m_localIds.removeAll(0);
168         QctResourceIriResolver resolver(m_localIds);
169         eventLoop.connect(&resolver, SIGNAL(finished()), SLOT(quit()));
170
171         if (resolver.lookup()) {
172             eventLoop.quit();
173             ResourceCleanser(QSet<QString>::fromList(resolver.resourceIris())).run();
174         }
175
176         m_localIds.clear();
177     }
178
179     resetEngine();
180 }
181
182 // FIXME: remove again once QtMobility provides more verbose contact validation utilities
183 static bool validateContact(QContactTrackerEngine *manager, const QContact &contact,
184                             QContactManager::Error &error, QString &what)
185 {
186 #if 0
187     QList<QString> uniqueDefinitionIds;
188
189     // check that each detail conforms to its definition as supported by this manager.
190     for (int i=0; i < contact.details().count(); i++) {
191         const QContactDetail& d = contact.details().at(i);
192         QVariantMap values = d.variantValues();
193         QContactManager::Error detailError = QContactManager::NoError;
194         QContactDetailDefinition def = manager->detailDefinition(d.definitionName(), contact.type(), &detailError);
195
196         if (detailError != QContactManager::NoError) {
197             error = detailError;
198             what = "Detail Error: " + d.definitionName();
199             return false;
200         }
201
202         // check that the definition is supported
203         if (def.isEmpty()) {
204             error = QContactManager::InvalidDetailError;
205             what = "Unsupported definition: " + d.definitionName();
206             return false;
207         }
208
209         // check uniqueness
210         if (def.isUnique()) {
211             if (uniqueDefinitionIds.contains(def.name())) {
212                 error = QContactManager::AlreadyExistsError;
213                 what = "Detail must be unique: " + d.definitionName();
214                 return false;
215             }
216             uniqueDefinitionIds.append(def.name());
217         }
218
219         QList<QString> keys = values.keys();
220         for (int i=0; i < keys.count(); i++) {
221             const QString& key = keys.at(i);
222
223             if (key == QContactDetail::FieldDetailUri) {
224                 continue;
225             }
226
227             // check that no values exist for nonexistent fields.
228             if (!def.fields().contains(key)) {
229                 error = QContactManager::InvalidDetailError;
230                 what = "Value for nonexistent field: " + d.definitionName() + "::" + key;
231                 return false;
232             }
233
234             QContactDetailFieldDefinition field = def.fields().value(key);
235             // check that the type of each value corresponds to the allowable field type
236             if (static_cast<int>(field.dataType()) != values.value(key).userType()) {
237                 error = QContactManager::InvalidDetailError;
238                 what = "Type doesn't match: " + d.definitionName() + "::" + key;
239                 return false;
240             }
241
242             // check that the value is allowable
243             // if the allowable values is an empty list, any are allowed.
244             if (!field.allowableValues().isEmpty()) {
245                 // if the field datatype is a list, check that it contains only allowable values
246                 if (field.dataType() == QVariant::List || field.dataType() == QVariant::StringList) {
247                     QList<QVariant> innerValues = values.value(key).toList();
248                     for (int i = 0; i < innerValues.size(); i++) {
249                         if (!field.allowableValues().contains(innerValues.at(i))) {
250                             error = QContactManager::InvalidDetailError;
251                             what = QString::fromLatin1("Value not allowed: %1 (%2)").
252                                    arg(d.definitionName() + "::" + key,
253                                        innerValues.at(i).toString());
254                             return false;
255                         }
256                     }
257                 } else if (!field.allowableValues().contains(values.value(key))) {
258                     // the datatype is not a list; the value wasn't allowed.
259                     error = QContactManager::InvalidDetailError;
260                     what = QString::fromLatin1("Value not allowed: %1 (%2)").
261                            arg(d.definitionName() + "::" + key,
262                                values.value(key).toString());
263                     return false;
264                 }
265             }
266         }
267     }
268 #else
269     qWarning("%s: temporarly disabled", __func__);
270     Q_UNUSED(manager);
271     Q_UNUSED(contact);
272     Q_UNUSED(error);
273     Q_UNUSED(what);
274 #endif
275
276     return true;
277 }
278
279 // FIXME: remove again once QtMobility provides more verbose relationship validation utilities
280 static bool
281 validateRelationship(QContactTrackerEngine */*manager*/,
282                      const QContactRelationship &/*relationship*/,
283                      QContactManager::Error &/*error*/, QString &/*what*/)
284 {
285     //FIXME: do some useful validation here
286     return true;
287 }
288
289 void ut_qtcontacts_trackerplugin_common::saveContact(QContact &contact, int timeout)
290 {
291     QList<QContact> contactList;
292     contactList.append(contact);
293
294     saveContacts(contactList, timeout);
295
296     QCOMPARE(contactList.count(), 1);
297     contact = contactList[0];
298 }
299
300 void ut_qtcontacts_trackerplugin_common::saveContacts(QList<QContact> &contacts, int timeout)
301 {
302     QVERIFY(not contacts.isEmpty());
303
304     foreach(const QContact &contact, contacts) {
305         QContactManager::Error error;
306         QString what;
307
308         if (not validateContact(m_engine, contact, error, what)) {
309             foreach(const QContactDetail &d, contact.details()) {
310                 qDebug() << d.definitionName() << d.variantValues();
311             }
312
313             QFAIL(qPrintable(QString::fromLatin1("error %1: %2").arg(error).arg(what)));
314         }
315     }
316
317     // add the contact to database
318     QContactSaveRequest request;
319     request.setContacts(contacts);
320     QVERIFY(engine()->startRequest(&request));
321
322     if (m_verbose) {
323         qDebug() << "saving" << request.contacts().count() << "contact(s)";
324     }
325
326     QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
327
328     // verify the contact got saved
329     QVERIFY(request.isFinished());
330     QCOMPARE(request.error(), QContactManager::NoError);
331
332     // copy back the saved contacts
333     contacts = request.contacts();
334
335     // remember the local id so that we can remove the contact from database later
336     foreach(const QContact &contact, contacts) {
337         QVERIFY(contact.localId());
338         m_localIds.append(contact.localId());
339     }
340 }
341
342 void ut_qtcontacts_trackerplugin_common::fetchContact(const QContactLocalId &id,
343                                                       QContact &result, int timeout)
344 {
345     QList<QContact> contactList;
346     fetchContacts(QList<QContactLocalId>() << id, contactList, timeout);
347     QCOMPARE(contactList.count(), 1);
348     result = contactList[0];
349 }
350
351 void ut_qtcontacts_trackerplugin_common::fetchContact(const QContactFilter &filter,
352                                                       QContact &result, int timeout)
353 {
354     QList<QContact> contactList;
355     fetchContacts(filter, contactList, timeout);
356     QCOMPARE(contactList.count(), 1);
357     result = contactList[0];
358 }
359
360 void ut_qtcontacts_trackerplugin_common::fetchContacts(const QList<QContactLocalId> &ids,
361                                          QList<QContact> &result, int timeout)
362 {
363     QContactLocalIdFilter filter;
364
365     filter.setIds(ids);
366     fetchContacts(filter, result, timeout);
367     CHECK_CURRENT_TEST_FAILED;
368
369     QCOMPARE(result.count(), ids.count());
370 }
371
372 void ut_qtcontacts_trackerplugin_common::fetchContacts(const QContactFilter &filter,
373                                                        QList<QContact> &result, int timeout)
374 {
375     fetchContacts(filter, NoSortOrders, result, timeout);
376 }
377
378 void ut_qtcontacts_trackerplugin_common::fetchContacts(const QContactFilter &filter,
379                                                        const QList<QContactSortOrder> &sorting,
380                                                        QList<QContact> &result, int timeout)
381 {
382     QContactFetchRequest request;
383
384     if (QContactFilter::InvalidFilter != filter.type())
385         request.setFilter(filter);
386     request.setSorting(sorting);
387
388     QVERIFY(engine()->startRequest(&request));
389     QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
390
391     QVERIFY(request.isFinished());
392     result = request.contacts();
393 }
394
395 void ut_qtcontacts_trackerplugin_common::fetchContactLocalId(const QContactFilter &filter,
396                                                              QContactLocalId &result, int timeout)
397 {
398     QList<QContactLocalId> contactLocalIdList;
399     fetchContactLocalIds(filter, contactLocalIdList, timeout);
400     QCOMPARE(contactLocalIdList.count(), 1);
401     result = contactLocalIdList[0];
402 }
403
404 void ut_qtcontacts_trackerplugin_common::fetchContactLocalIds(const QContactFilter &filter,
405                                                               QList<QContactLocalId> &result, int timeout)
406 {
407     fetchContactLocalIds(filter, NoSortOrders, result, timeout);
408 }
409
410 void ut_qtcontacts_trackerplugin_common::fetchContactLocalIds(const QContactFilter &filter,
411                                                               const QList<QContactSortOrder> &sorting,
412                                                               QList<QContactLocalId> &result, int timeout)
413 {
414     QContactLocalIdFetchRequest request;
415
416     if (QContactFilter::InvalidFilter != filter.type())
417         request.setFilter(filter);
418     request.setSorting(sorting);
419
420     QVERIFY(engine()->startRequest(&request));
421     QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
422
423     QVERIFY(request.isFinished());
424     result = request.ids();
425 }
426
427 void ut_qtcontacts_trackerplugin_common::saveRelationship(const QContactRelationship &relationship, int timeout)
428 {
429     QList<QContactRelationship> relationshipList;
430     relationshipList.append(relationship);
431
432     saveRelationships(relationshipList, timeout);
433 }
434
435 void ut_qtcontacts_trackerplugin_common::saveRelationships(const QList<QContactRelationship> &relationships, int timeout)
436 {
437     QVERIFY(not relationships.isEmpty());
438
439     foreach(const QContactRelationship &relationship, relationships) {
440         QContactManager::Error error;
441         QString what;
442
443         if (not validateRelationship(m_engine, relationship, error, what)) {
444             qDebug() << relationship;
445
446             QFAIL(qPrintable(QString::fromLatin1("error %1: %2").arg(error).arg(what)));
447         }
448     }
449
450     // add the relationship to database
451     QContactRelationshipSaveRequest request;
452     request.setRelationships(relationships);
453     QVERIFY(engine()->startRequest(&request));
454
455     QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
456
457     // verify the relationship got saved
458     QVERIFY(request.isFinished());
459
460     QCOMPARE((int) request.error(),
461              (int) QContactManager::NoError);
462
463     // no need to note the relationships stored, should be removed automatically with the contacts in tracker
464 }
465
466 void ut_qtcontacts_trackerplugin_common::fetchRelationship(const QContactId &firstId, const QString &relationshipType, const QContactId &secondId, QContactRelationship &result, int timeout)
467 {
468     // fetch relationship from database
469     QContactRelationshipFetchRequest request;
470     request.setRelationshipType(relationshipType);
471     request.setFirst(firstId);
472     request.setSecond(secondId);
473
474     QVERIFY(engine()->startRequest(&request));
475     QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
476
477     QVERIFY(request.isFinished());
478
479     QCOMPARE((int) request.error(),
480              (int) QContactManager::NoError);
481
482     const QList<QContactRelationship> relationships = request.relationships();
483     QCOMPARE(relationships.count(), 1);
484     result = relationships[0];
485 }
486
487
488 void ut_qtcontacts_trackerplugin_common::fetchRelationships(const QString &relationshipType,
489                                                             const QContactId &participantId, QContactRelationship::Role role,
490                                                             QList<QContactRelationship> &result, int timeout)
491 {
492     QList<QContactRelationship> internalResult;
493
494     // fetch relationship from database
495     if (role != QContactRelationship::Second) {
496         QContactRelationshipFetchRequest request;
497         request.setRelationshipType(relationshipType);
498         request.setFirst(participantId);
499
500         QVERIFY(engine()->startRequest(&request));
501         QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
502
503         QVERIFY(request.isFinished());
504
505         QCOMPARE((int) request.error(),
506                  (int) QContactManager::NoError);
507
508         internalResult += request.relationships();
509     }
510     if (role != QContactRelationship::First) {
511         QContactRelationshipFetchRequest request;
512         request.setRelationshipType(relationshipType);
513         request.setSecond(participantId);
514
515         QVERIFY(engine()->startRequest(&request));
516         QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
517
518         QVERIFY(request.isFinished());
519
520         QCOMPARE((int) request.error(),
521                  (int) QContactManager::NoError);
522
523         internalResult += request.relationships();
524     }
525     result = internalResult;
526 }
527
528 void ut_qtcontacts_trackerplugin_common::removeRelationship(const QContactRelationship &relationship, int timeout)
529 {
530     QList<QContactRelationship> relationshipList;
531     relationshipList.append(relationship);
532
533     removeRelationships(relationshipList, timeout);
534 }
535
536 void ut_qtcontacts_trackerplugin_common::removeRelationships(const QList<QContactRelationship> &relationships, int timeout)
537 {
538     QVERIFY(not relationships.isEmpty());
539
540     foreach(const QContactRelationship &relationship, relationships) {
541         QContactManager::Error error;
542         QString what;
543
544         if (not validateRelationship(m_engine, relationship, error, what)) {
545             qDebug() << relationship;
546
547             QFAIL(qPrintable(QString::fromLatin1("error %1: %2").arg(error).arg(what)));
548         }
549     }
550
551     // remove the relationship from database
552     QContactRelationshipRemoveRequest request;
553     request.setRelationships(relationships);
554     QVERIFY(engine()->startRequest(&request));
555
556     QVERIFY(engine()->waitForRequestFinishedImpl(&request, timeout));
557
558     // verify the relationship got removed
559     QVERIFY(request.isFinished());
560
561     QCOMPARE((int) request.error(),
562              (int) QContactManager::NoError);
563 }
564
565 QSet<QString> ut_qtcontacts_trackerplugin_common::findTestSlotNames()
566 {
567     QSet<QString> testSlots;
568
569     for(int i = 0; i < metaObject()->methodCount(); ++i) {
570         const QMetaMethod &method = metaObject()->method(i);
571
572         if (QMetaMethod::Private != method.access() ||
573             QMetaMethod::Slot != method.methodType()) {
574             continue;
575         }
576
577         const char *signature = method.signature();
578         const char *parenthesis = strchr(signature, '(');
579
580         if (0 != qstrcmp(parenthesis, "()")) {
581             continue;
582         }
583
584         testSlots.insert(QString::fromLatin1(signature, parenthesis - signature));
585     }
586
587     return testSlots;
588 }
589
590 QList<QContact> ut_qtcontacts_trackerplugin_common::parseVCards(const QString &fileName, int limit)
591 {
592     QFile file(fileName);
593
594     if (not file.open(QFile::ReadOnly)) {
595         qWarning("Cannot open %s: %s",
596                  qPrintable(file.fileName()),
597                  qPrintable(file.errorString()));
598
599         return QList<QContact>();
600     }
601
602     return parseVCards(file.readAll(), limit);
603 }
604
605 QList<QContact> ut_qtcontacts_trackerplugin_common::parseVCards(const QByteArray &vcardData, int limit)
606 {
607     if (limit != INT_MAX) {
608         int offset = 0;
609
610         for(int i = 0; i < limit; ++i) {
611             static const char endToken[] = "END:VCARD";
612             int end = vcardData.indexOf(endToken, offset);
613
614             if (-1 == end) {
615                 break;
616             }
617
618             offset = end + sizeof(endToken) - 1;
619         }
620
621         return parseVCards(vcardData.left(offset));
622     }
623
624     QVersitReader reader(vcardData);
625
626     if (not reader.startReading()) {
627         qWarning() << "Starting to read vCards failed:" << reader.error();
628         return QList<QContact>();
629     }
630
631     if (not reader.waitForFinished() || QVersitReader::NoError != reader.error()) {
632         qWarning() << "Reading vCards failed:" << reader.error();
633         return QList<QContact>();
634     }
635
636     QList<QVersitDocument> documents = reader.results();
637
638     while(documents.count() > limit) {
639         documents.removeLast();
640     }
641
642     QVersitContactImporter importer;
643
644     if (not importer.importDocuments(documents)) {
645         qWarning() << "Importing vCards failed:" << importer.errors();
646         return QList<QContact>();
647     }
648
649     return importer.contacts();
650 }
651
652
653 QString ut_qtcontacts_trackerplugin_common::referenceFileName(const QString &fileName)
654 {
655     QString path(QDir(QLatin1String("data")).absoluteFilePath(fileName));
656
657     if (not QFile::exists(path)) {
658         path = QDir(m_srcDir.filePath(QLatin1String("data"))).filePath(fileName);
659     }
660
661     if (not QFile::exists(path)) {
662         path = m_dataDir.filePath(fileName);
663     }
664
665     return path;
666 }
667
668 QString ut_qtcontacts_trackerplugin_common::loadReferenceFile(const QString &fileName)
669 {
670     QFile referenceFile(referenceFileName(fileName));
671
672     if (not referenceFile.open(QFile::ReadOnly)) {
673         qWarning() << referenceFile.fileName() << ":" << referenceFile.errorString();
674         return QString();
675     }
676
677     return QString::fromLocal8Bit(referenceFile.readAll());
678 }
679
680 QDomDocument ut_qtcontacts_trackerplugin_common::loadReferenceContacts(const QString &fileName)
681 {
682     QFile file(referenceFileName(fileName));
683
684     if (not file.open(QFile::ReadOnly)) {
685         qWarning() << file.errorString();
686         return QDomDocument();
687     }
688
689     int errorLine, errorColumn;
690     QString documentError;
691     QDomDocument document;
692
693     if (not document.setContent(&file, &documentError, &errorLine, &errorColumn)) {
694         qWarning() << errorLine << errorColumn << ":" << documentError;
695         return QDomDocument();
696     }
697
698     return document;
699 }
700
701 QSparqlResult *
702 ut_qtcontacts_trackerplugin_common::executeQuery(const QString &queryString,
703                                                  QSparqlQuery::StatementType type) const
704 {
705     QSparqlConnection &connection = QctSparqlConnectionManager::defaultConnection();
706
707     if (not connection.isValid()) {
708         qctWarn("No QtSparql connection available.");
709         return false;
710     }
711
712     const QSparqlQuery query(queryString, type);
713     QScopedPointer<QSparqlResult> result(connection.exec(query));
714     result->waitForFinished();
715
716     if (result->hasError()) {
717         qctWarn(result->lastError().message());
718         return 0;
719     }
720
721     return result.take();
722 }
723
724 QStringList
725 ut_qtcontacts_trackerplugin_common::loadRawTuples(const QString &fileName)
726 {
727     QSparqlConnection &connection = QctSparqlConnectionManager::defaultConnection();
728
729     if (not connection.isValid()) {
730         qctWarn("No QtSparql connection available.");
731         return QStringList();
732     }
733
734     QString rawTuples = loadReferenceFile(fileName);
735
736     // extract resource IRIs
737     QRegExp iriPattern(QLatin1String("<([a-z]+:[^>]+[^#])>"));
738     QList<QString> resourceIris;
739
740     for(int i = 0;; i += iriPattern.matchedLength()) {
741         if (-1 == (i = rawTuples.indexOf(iriPattern, i))) {
742             break;
743         }
744
745         const QString iri = iriPattern.capturedTexts().at(1);
746
747         // simulate set, but preserve order
748         if (not resourceIris.contains(iri)) {
749             resourceIris.append(iri);
750         }
751     }
752
753     // process the turtle file's prefix directives
754     QRegExp prefixPattern(QLatin1String("@prefix\\s+(\\w+):\\s+<([^>]+)>\\s*\\."));
755
756     for(int i; -1 != (i = rawTuples.indexOf(prefixPattern)); ) {
757         connection.addPrefix(prefixPattern.cap(1), prefixPattern.cap(2));
758         rawTuples.remove(i, prefixPattern.matchedLength());
759     }
760
761     // cleaning resources found in the tuple file
762     ResourceCleanser(resourceIris.toSet()).run();
763
764     // run INSERT query to load the tuples
765     const QString queryString = QString::fromLatin1("INSERT { %1 }").arg(rawTuples);
766     const QSparqlQuery query(queryString, QSparqlQuery::InsertStatement);
767     const QScopedPointer<QSparqlResult> result(connection.exec(query));
768
769     result->waitForFinished();
770
771     if (result->hasError()) {
772         qctWarn(result->lastError().message());
773     }
774
775     return resourceIris;
776 }
777
778 static QHash<QString, QDomElement>
779 collectChildren(QDomElement parent, const QString &baseId = QString())
780 {
781     QHash<QString, QDomElement> result;
782
783     for(QDomElement child = parent.firstChildElement();
784         not child.isNull(); child = child.nextSiblingElement()) {
785         QString id(child.attribute(QLatin1String("id")));
786
787         if (id.isEmpty()) {
788             id = baseId + child.tagName();
789
790             if (result.contains(id)) {
791                 qWarning() << "anonymous detail must be unique:" << id;
792                 continue;
793             }
794         }
795
796         result.insert(id, child);
797     }
798
799     return result;
800 }
801
802 void
803 ut_qtcontacts_trackerplugin_common::verifyContacts(const QList<QContact> &candiates,
804                                                    const QDomDocument &reference,
805                                                    const QStringList &missingIds)
806 {
807     QHash<QString, QDomElement> referenceContactMap(collectChildren(reference.documentElement()));
808
809     foreach(const QContact &contact, candiates) {
810         const QString contactId(makeContactIri(contact.localId()).toString());
811         const QString detailBaseUri(contactId + QLatin1Char('#'));
812
813         QDomElement referenceContact(referenceContactMap.take(contactId));
814         QVERIFY2(not referenceContact.isNull(), qPrintable(contactId));
815
816         QHash<QString, QDomElement> referenceDetailMap =
817                 collectChildren(referenceContact, detailBaseUri);
818         QList<QContactDetail> candidateDetailList(contact.details());
819
820         while(not candidateDetailList.isEmpty()) {
821             const QContactDetail candiateDetail(candidateDetailList.takeFirst());
822             QString candiateDetailUri(candiateDetail.detailUri());
823
824             if (candiateDetailUri.isEmpty()) {
825                 candiateDetailUri = detailBaseUri + candiateDetail.definitionName();
826             }
827
828             const QDomElement referenceDetail(referenceDetailMap.take(candiateDetailUri));
829
830             QVERIFY2(not referenceDetail.isNull(),
831                      qPrintable(QString::fromLatin1("Unexpected detail for contact <%1>: <%2>").
832                                 arg(contactId, candiateDetailUri)));
833             QVERIFY2(candiateDetail.detailUri() == referenceDetail.attribute(QLatin1String("id")),
834                      qPrintable(QString::fromLatin1("Bad URI for detail of contact <%1>: <%2>").
835                                 arg(contactId, candiateDetailUri)));
836
837             QVariantMap candiateValueMap(candiateDetail.variantValues());
838             QHash<QString, QDomElement> referenceValueMap(collectChildren(referenceDetail));
839
840             if (referenceValueMap.isEmpty()) {
841                 referenceValueMap.insert(referenceDetail.tagName(), referenceDetail);
842             }
843
844             while (not candiateValueMap.isEmpty()) {
845                 const QString fieldName(candiateValueMap.keys().first());
846                 const QVariant candidateValue(candiateValueMap.take(fieldName));
847
848                 if (fieldName == QContactDetail::FieldDetailUri) {
849                     continue;
850                 }
851
852                 const QString candiateValueUri(candiateDetailUri + QLatin1Char('/') + fieldName);
853                 const QDomElement referenceValue(referenceValueMap.take(fieldName));
854
855                 QVERIFY2(not referenceValue.isNull(),
856                          qPrintable(contactId + QLatin1String(" - ") + candiateValueUri));
857
858                 if (QVariant::StringList == candidateValue.type()) {
859                     QCOMPARE(candidateValue.toStringList().toSet(),
860                              referenceValue.text().split(QLatin1String(";")).toSet());
861                 } else {
862                     QCOMPARE(candidateValue, QVariant(referenceValue.text()));
863                 }
864             }
865
866             QVERIFY2(referenceValueMap.isEmpty(),
867                      qPrintable(QString::fromLatin1("Detail <%3> of contact <%2> is missing values: %1").
868                                 arg(QStringList(referenceValueMap.keys()).join(QLatin1String(", ")),
869                                     contactId, candiateDetailUri)));
870         }
871
872         QVERIFY2(referenceDetailMap.isEmpty(),
873                  qPrintable(QString::fromLatin1("Contact <%2> is missing expected details: <%1>").
874                             arg(QStringList(referenceDetailMap.keys()).join(QLatin1String(">, <")),
875                                 contactId)));
876     }
877
878     QCOMPARE(QStringList(referenceContactMap.keys()), missingIds);
879 }
880
881 void
882 ut_qtcontacts_trackerplugin_common::verifyContacts(const QList<QContact> &candiates,
883                                                    const QString &fileName,
884                                                    const QStringList &missingIds)
885 {
886     QDomDocument reference(loadReferenceContacts(fileName));
887     QVERIFY2(not reference.isNull(), qPrintable(fileName));
888     verifyContacts(candiates, reference, missingIds);
889 }
890
891 QContactLocalId
892 parseContactIri(const QString &/*iri*/, bool *ok)
893 {
894     QTest::qFail("parseContactIri() is crap, get over it", __FILE__, __LINE__);
895     qctPropagate(false, ok);
896     return 0;
897 }
898
899 QUrl
900 makeContactIri(const QContactLocalId &/*localId*/)
901 {
902     QTest::qFail("makeContactIri() is crap, get over it", __FILE__, __LINE__);
903     return QUrl();
904 }
905
906 QContactLocalId
907 parseContactGroupIri(const QString &/*iri*/, bool *ok)
908 {
909     QTest::qFail("parseContactGroupIri() is crap, get over it", __FILE__, __LINE__);
910     qctPropagate(false, ok);
911     return 0;
912 }
913
914 QUrl
915 makeContactGroupIri(const QContactLocalId &/*localId*/)
916 {
917     QTest::qFail("makeContactGroupIri() is crap, get over it", __FILE__, __LINE__);
918     return QUrl();
919 }