Tests: Try to release libqttracker singletons when finishing.
[qtcontacts-tracker:murrayc-qtcontacts-tracker-for-merge-requests2.git] / tests / ut_qtcontacts_common / ut_qtcontacts_common.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 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_common.h"
43
44 #include <dao/subject.h>
45
46 #include <QContactFetchRequest>
47 #include <QContactLocalIdFilter>
48 #include <QContactRemoveRequest>
49 #include <QContactSaveRequest>
50
51 #include <QVersitReader>
52 #include <QVersitContactImporter>
53
54 #include "resourcecleanser.h"
55
56 ut_qtcontacts_common::ut_qtcontacts_common(const QDir &testDir, QObject *parent)
57     : QObject(parent), mEngine(0), mTestDir(testDir)
58 {
59 }
60
61 ut_qtcontacts_common::~ut_qtcontacts_common()
62 {
63     QVERIFY(0 == mEngine);
64
65     // Really delete any deferred-deleted objects, which QTest doesn't seem
66     // to delete otherwise. This helps valgrind's leak check.
67     while (QCoreApplication::hasPendingEvents()) {
68         QCoreApplication::sendPostedEvents();
69         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
70         QCoreApplication::processEvents();
71     }
72
73     // Delete libqttracker singletons.
74     // This helps with memory leak detection.
75     // We can't just do this in the ~QContactTrackerEngine destructor,
76     // because tracker can't be used again afterwards without crashing.
77     SopranoLive::BackEnds::Tracker::trackerRelease();
78 }
79
80 QMap<QString, QString> ut_qtcontacts_common::makeEngineParams()
81 {
82     return QMap<QString, QString>();
83 }
84
85 QContactTrackerEngine *
86 ut_qtcontacts_common::engine()
87 {
88     // need lazy initialization because *_data() is called before init() -- QTBUG-11186
89     if (0 == mEngine) {
90         mEngine = new QContactTrackerEngine(makeEngineParams());
91     }
92
93     return mEngine;
94 }
95
96 void
97 ut_qtcontacts_common::resetEngine()
98 {
99     delete mEngine;
100     mEngine = 0;
101 }
102
103 void ut_qtcontacts_common::cleanup()
104 {
105     if (mEngine) {
106         QSet<QUrl> subjects;
107
108         foreach(const QContactLocalId &localId, mLocalIds) {
109             subjects.insert(makeContactIri(localId));
110         }
111
112         if (not subjects.isEmpty()) {
113             ResourceCleanser(subjects).run();
114         }
115
116         mLocalIds.clear();
117     }
118
119     resetEngine();
120 }
121
122 // FIXME: remove again once QtMobility provides more verbose contact validation utilities
123 static bool validateContact(QContactTrackerEngine *manager, const QContact &contact, QContactManager::Error &error_, QString &what)
124 {
125     QList<QString> uniqueDefinitionIds;
126
127     // check that each detail conforms to its definition as supported by this manager.
128     for (int i=0; i < contact.details().count(); i++) {
129         const QContactDetail& d = contact.details().at(i);
130         QVariantMap values = d.variantValues();
131         QContactManager::Error detailError = QContactManager::NoError;
132         QContactDetailDefinition def = manager->detailDefinition(d.definitionName(), contact.type(), &detailError);
133
134         if (detailError != QContactManager::NoError) {
135             error_ = detailError;
136             what = "Detail Error:" + d.definitionName();
137             return false;
138         }
139
140         // check that the definition is supported
141         if (def.isEmpty()) {
142             error_ = QContactManager::InvalidDetailError;
143             what = "Unsupported definition: " + d.definitionName();
144             return false;
145         }
146
147         // check uniqueness
148         if (def.isUnique()) {
149             if (uniqueDefinitionIds.contains(def.name())) {
150                 error_ = QContactManager::AlreadyExistsError;
151                 what = "Detail must be unique: " + d.definitionName();
152                 return false;
153             }
154             uniqueDefinitionIds.append(def.name());
155         }
156
157         QList<QString> keys = values.keys();
158         for (int i=0; i < keys.count(); i++) {
159             const QString& key = keys.at(i);
160
161             if (key == QContactDetail::FieldDetailUri) {
162                 continue;
163             }
164
165             // check that no values exist for nonexistent fields.
166             if (!def.fields().contains(key)) {
167                 error_ = QContactManager::InvalidDetailError;
168                 what = "Value for nonexistent field: " + d.definitionName() + "::" + key;
169                 return false;
170             }
171
172             QContactDetailFieldDefinition field = def.fields().value(key);
173             // check that the type of each value corresponds to the allowable field type
174             if (static_cast<int>(field.dataType()) != values.value(key).userType()) {
175                 error_ = QContactManager::InvalidDetailError;
176                 what = "Type doesn't match: " + d.definitionName() + "::" + key;
177                 return false;
178             }
179
180             // check that the value is allowable
181             // if the allowable values is an empty list, any are allowed.
182             if (!field.allowableValues().isEmpty()) {
183                 // if the field datatype is a list, check that it contains only allowable values
184                 if (field.dataType() == QVariant::List || field.dataType() == QVariant::StringList) {
185                     QList<QVariant> innerValues = values.value(key).toList();
186                     for (int i = 0; i < innerValues.size(); i++) {
187                         if (!field.allowableValues().contains(innerValues.at(i))) {
188                             error_ = QContactManager::InvalidDetailError;
189                             what = QString("Value not allowed: %1 (%2)").
190                                    arg(d.definitionName() + "::" + key,
191                                        innerValues.at(i).toString());
192                             return false;
193                         }
194                     }
195                 } else if (!field.allowableValues().contains(values.value(key))) {
196                     // the datatype is not a list; the value wasn't allowed.
197                     error_ = QContactManager::InvalidDetailError;
198                     what = QString("Value not allowed: %1 (%2)").
199                            arg(d.definitionName() + "::" + key,
200                                values.value(key).toString());
201                     return false;
202                 }
203             }
204         }
205     }
206
207     return true;
208 }
209
210 void ut_qtcontacts_common::saveContact(QContact &contact, int timeout)
211 {
212     QList<QContact> contactList;
213     contactList.append(contact);
214
215     saveContacts(contactList, timeout);
216
217     QCOMPARE(contactList.count(), 1);
218     contact = contactList[0];
219 }
220
221 void ut_qtcontacts_common::saveContacts(QList<QContact> &contacts, int timeout)
222 {
223     QVERIFY(not contacts.isEmpty());
224
225     foreach(const QContact &contact, contacts) {
226         QContactManager::Error error;
227         QString what;
228
229         if (not validateContact(mEngine, contact, error, what)) {
230             foreach(const QContactDetail &d, contact.details()) {
231                 qDebug() << d.definitionName() << d.variantValues();
232             }
233
234             QFAIL(qPrintable(QString("error %1: %2").arg(error).arg(what)));
235         }
236     }
237
238     // add the contact to database
239     QContactSaveRequest request;
240     request.setContacts(contacts);
241     QVERIFY(engine()->startRequest(&request));
242
243     qDebug() << "saving" << request.contacts().count() << "contact(s)";
244     QVERIFY(engine()->waitForRequestFinished(&request, timeout));
245
246     // verify the contact got saved
247     QVERIFY(request.isFinished());
248
249     QCOMPARE((int) request.error(),
250              (int) QContactManager::NoError);
251
252     // copy back the saved contacts
253     contacts = request.contacts();
254
255     // remember the local id so that we can remove the contact from database later
256     foreach(const QContact &contact, contacts) {
257         QVERIFY(contact.localId());
258         mLocalIds.append(contact.localId());
259     }
260 }
261
262 void ut_qtcontacts_common::fetchContact(const QContactLocalId &id,
263                                         QContact &result, int timeout)
264 {
265     QList<QContact> contactList;
266     fetchContacts(QList<QContactLocalId>() << id, contactList, timeout);
267     QCOMPARE(contactList.count(), 1);
268     result = contactList[0];
269 }
270
271 void ut_qtcontacts_common::fetchContact(const QContactFilter &filter,
272                                         QContact &result, int timeout)
273 {
274     QList<QContact> contactList;
275     fetchContacts(filter, contactList, timeout);
276     QCOMPARE(contactList.count(), 1);
277     result = contactList[0];
278 }
279
280 void ut_qtcontacts_common::fetchContacts(const QList<QContactLocalId> &ids,
281                                          QList<QContact> &result, int timeout)
282 {
283     QContactLocalIdFilter filter;
284
285     filter.setIds(ids);
286     fetchContacts(filter, result, timeout);
287     CHECK_CURRENT_TEST_FAILED;
288
289     QCOMPARE(result.count(), ids.count());
290 }
291
292 void ut_qtcontacts_common::fetchContacts(const QContactFilter &filter,
293                                          QList<QContact> &result, int timeout)
294 {
295     QContactFetchRequest request;
296
297     if (QContactFilter::InvalidFilter != filter.type())
298         request.setFilter(filter);
299
300     QVERIFY(engine()->startRequest(&request));
301
302     qDebug() << "fetching contacts";
303     QVERIFY(engine()->waitForRequestFinished(&request, timeout));
304
305     QVERIFY(request.isFinished());
306     result = request.contacts();
307 }
308
309 QSet<QString> ut_qtcontacts_common::findTestSlotNames()
310 {
311     QSet<QString> testSlots;
312
313     for(int i = 0; i < metaObject()->methodCount(); ++i) {
314         const QMetaMethod &method = metaObject()->method(i);
315
316         if (QMetaMethod::Private != method.access() ||
317             QMetaMethod::Slot != method.methodType()) {
318             continue;
319         }
320
321         const char *signature = method.signature();
322         const char *parenthesis = strchr(signature, '(');
323
324         if (0 != qstrcmp(parenthesis, "()")) {
325             continue;
326         }
327
328         testSlots.insert(QString::fromLatin1(signature, parenthesis - signature));
329     }
330
331     return testSlots;
332 }
333
334 QList<QContact> ut_qtcontacts_common::parseVCards(const QByteArray &vcardData, int limit)
335 {
336     if (limit != INT_MAX) {
337         int offset = 0;
338
339         for(int i = 0; i < limit; ++i) {
340             static const char endToken[] = "END:VCARD";
341             int end = vcardData.indexOf(endToken, offset);
342
343             if (-1 == end) {
344                 break;
345             }
346
347             offset = end + sizeof(endToken) - 1;
348         }
349
350         qDebug() << offset;
351         return parseVCards(vcardData.left(offset));
352     }
353
354     QVersitReader reader(vcardData);
355
356     if (not reader.startReading()) {
357         qWarning() << "Starting to read vCards failed:" << reader.error();
358         return QList<QContact>();
359     }
360
361     if (not reader.waitForFinished()) {
362         qWarning() << "Reading vCards failed:" << reader.error();
363         return QList<QContact>();
364     }
365
366     Q_ASSERT(reader.error() == QVersitReader::NoError);
367     QList<QVersitDocument> documents = reader.results();
368
369     while(documents.count() > limit) {
370         documents.removeLast();
371     }
372
373     QVersitContactImporter importer;
374
375     if (not importer.importDocuments(documents)) {
376         qWarning() << "Importing vCards failed:" << importer.errors();
377         return QList<QContact>();
378     }
379
380     return importer.contacts();
381 }
382
383
384 QString ut_qtcontacts_common::referenceFileName(const QString &fileName)
385 {
386     QString path(QDir(QLatin1String("data")).absoluteFilePath(fileName));
387
388     if (not QFile::exists(path)) {
389         path = mTestDir.filePath(fileName);
390     }
391
392     return path;
393 }
394
395 QUrl ut_qtcontacts_common::referenceFileUrl(const QString &fileName)
396 {
397     return QUrl::fromLocalFile(referenceFileName(fileName));
398 }
399
400 QString ut_qtcontacts_common::loadReferenceFile(const QString &fileName)
401 {
402     QFile referenceFile(referenceFileName(fileName));
403
404     if (not referenceFile.open(QFile::ReadOnly)) {
405         qWarning() << referenceFile.fileName() << ":" << referenceFile.errorString();
406         return QString();
407     }
408
409     return referenceFile.readAll();
410 }
411
412 QDomDocument ut_qtcontacts_common::loadReferenceContacts(const QString &fileName)
413 {
414     QFile file(referenceFileName(fileName));
415
416     if (not file.open(QFile::ReadOnly)) {
417         qWarning() << file.errorString();
418         return QDomDocument();
419     }
420
421     int errorLine, errorColumn;
422     QString documentError;
423     QDomDocument document;
424
425     if (not document.setContent(&file, &documentError, &errorLine, &errorColumn)) {
426         qWarning() << errorLine << errorColumn << ":" << documentError;
427         return QDomDocument();
428     }
429
430     return document;
431 }
432
433 void ut_qtcontacts_common::fillAddressbook(const QString &fileName)
434 {
435     QRegExp iriPattern("<([a-z]+:[^>]+)>");
436     QSet<QUrl> subjects;
437
438     for(int i = 0;; i+= iriPattern.matchedLength()) {
439         if (-1 == (i = loadReferenceFile(fileName).indexOf(iriPattern, i))) {
440             break;
441         }
442
443         subjects.insert(QUrl(iriPattern.capturedTexts().at(1)));
444     }
445
446     ResourceCleanser(subjects).run();
447     ::tracker()->rawLoad(referenceFileUrl(fileName));
448 }