From 8a3f039c50447a3d46dd75d14ad8c59e449e8ca7 Mon Sep 17 00:00:00 2001
From: Christophe Dumez <christophe.dumez@intel.com>
Date: Sat, 7 May 2011 16:41:21 +0300
Subject: [PATCH 2/6] Engine: support for contact change timestamp

Bring support for QContactTimestamp::FieldModificationTimestamp.
Field is correctly updated when saving a contact and filtering
can be done based on last modification timestamp.

Update the unit tests to check both functionalities.
---
 src/qcontactebook.cpp                |   26 ++++++++-
 test/main.cpp                        |    7 +++
 test/test.pro                        |    1 +
 test/ut_basic/basictest.cpp          |   97 ++++++++++++++++++++++++++++++++++
 test/ut_basic/basictest.h            |   46 ++++++++++++++++
 test/ut_basic/ut_basic.pri           |    6 ++
 test/ut_filtering/filteringtest.cpp  |   14 +++++
 test/ut_filtering/qcontact_helpers.h |    3 +
 8 files changed, 198 insertions(+), 2 deletions(-)
 create mode 100644 test/ut_basic/basictest.cpp
 create mode 100644 test/ut_basic/basictest.h
 create mode 100644 test/ut_basic/ut_basic.pri

diff --git a/src/qcontactebook.cpp b/src/qcontactebook.cpp
index b8c6766..b3b7f21 100644
--- a/src/qcontactebook.cpp
+++ b/src/qcontactebook.cpp
@@ -47,6 +47,7 @@
 #include <QVersitContactExporter>
 #include <QVersitContactImporter>
 #include <QVersitContactHandler>
+#include <QContactTimestamp>
 #include <QVersitDocument>
 #include <QVersitWriter>
 #include <QVersitReader>
@@ -666,7 +667,7 @@ bool QContactEBook::saveContact(QContact* contact, QContactManager::Error* error
 
   EContact *eContact = NULL;
 
-  // Convert QContact to AContact;
+  // Convert QContact to EContact;
   // must include right UID if we have one or remove it otherwise
   QString pasid;
   if (contact->localId()) {
@@ -674,9 +675,20 @@ bool QContactEBook::saveContact(QContact* contact, QContactManager::Error* error
     pasid = QContactLocalId2UID(contact->localId());
     guid.setGuid(pasid);
     contact->saveDetail(&guid);
+    // update lastModified timestamp
+    QContactTimestamp ts = contact->detail(QContactTimestamp::DefinitionName);
+    ts.setLastModified(QDateTime::currentDateTimeUtc());
+    QContactManagerEngine::setDetailAccessConstraints(&ts, QContactDetail::ReadOnly | QContactDetail::Irremovable);
+    contact->saveDetail(&ts);
   } else {
     QContactGuid guid;
     contact->removeDetail(&guid);
+    // set lastModified timestamp
+    QContactTimestamp ts = contact->detail(QContactTimestamp::DefinitionName);
+    ts.setLastModified(QDateTime::currentDateTimeUtc());
+    // XXX: QContactTimestamp::FieldCreationTimestamp is not supported due to vCard limitation
+    QContactManagerEngine::setDetailAccessConstraints(&ts, QContactDetail::ReadOnly | QContactDetail::Irremovable);
+    contact->saveDetail(&ts);
   }
   eContact = convert(contact, error);
   if (!eContact){
@@ -830,6 +842,9 @@ QStringList QContactEBook::getDetailMapping(const QString &detailDefinitionName,
     mapping[QContactEmailAddress::DefinitionName][""] << "email";
     // Tags / Categories
     mapping[QContactTag::DefinitionName][""] << "category_list";
+    // Timestamp
+    mapping[QContactTimestamp::DefinitionName][""] << "Rev";
+    mapping[QContactTimestamp::DefinitionName][QContactTimestamp::FieldCreationTimestamp] = QStringList(); // Unsupported
     // Name
     mapping[QContactDisplayLabel::DefinitionName][""] << "full_name";
     mapping[QContactName::DefinitionName][""] << "full_name";
@@ -871,7 +886,14 @@ EBookQuery* QContactEBook::convertDetailFilter(const QContactDetailFilter& f) co
     return NULL;
   bool existence_check = f.detailFieldName().isEmpty();
 
-  QString valueStr = f.value().toString();
+  QString valueStr;
+  if (f.value().type() == QVariant::DateTime) {
+    valueStr = f.value().toDateTime().toString(Qt::ISODate);
+    // EDS adds a trailing "Z"
+    if (!valueStr.endsWith("Z")) valueStr += "Z";
+  } else {
+    valueStr = f.value().toString();
+  }
 
   if (existence_check) {
     // Note that this behavior is according to the documentation:
diff --git a/test/main.cpp b/test/main.cpp
index 052b561..cccc695 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -21,6 +21,7 @@
 #include <QCoreApplication>
 #include "ut_propertymatching/propertymatching.h"
 #include "ut_filtering/filteringtest.h"
+#include "ut_basic/basictest.h"
 #include "ut_signals/signalstest.h"
 
 int main(int argc, char *argv[]) {
@@ -32,6 +33,12 @@ int main(int argc, char *argv[]) {
   Q_ASSERT(ebook);
 
   int ret = 0;
+  // Basic testing
+  BasicTest testBasic;
+  ret = QTest::qExec(&testBasic, argc, argv);
+  if(ret)
+    return ret;
+
   // Test property matching
   PropertyMatching testPropMatching;
   ret = QTest::qExec(&testPropMatching, argc, argv);
diff --git a/test/test.pro b/test/test.pro
index 673bcdb..afcf4c4 100644
--- a/test/test.pro
+++ b/test/test.pro
@@ -14,6 +14,7 @@ check.depends = $$DESTDIR/$$TARGET
 check.commands = $$DESTDIR/$$TARGET
 
 include(ut_common/ut_common.pri)
+include(ut_basic/ut_basic.pri)
 include(ut_propertymatching/ut_propertymatching.pri)
 include(ut_filtering/ut_filtering.pri)
 include(ut_signals/ut_signals.pri)
diff --git a/test/ut_basic/basictest.cpp b/test/ut_basic/basictest.cpp
new file mode 100644
index 0000000..3933ece
--- /dev/null
+++ b/test/ut_basic/basictest.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) version 3.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301  USA
+ */
+
+#include <QContact>
+#include <QContactName>
+#include <QContactTimestamp>
+#include <QDateTime>
+#include <QDebug>
+#include "basictest.h"
+#include "ut_manager.h"
+
+void BasicTest::initTestCase()
+{
+  m_manager = testContactManagerEDS();
+  QVERIFY(m_manager);
+}
+
+void BasicTest::testAddContactUpdateTimeStamp() {
+  // Add a test contact
+  QContact c;
+  QContact compatible = m_manager->compatibleContact(c);
+  QDateTime before_dt = QDateTime::currentDateTime();
+  QVERIFY(m_manager->saveContact(&compatible));
+  QDateTime after_dt = QDateTime::currentDateTime();
+  // Check that the local id was updated
+  QVERIFY(compatible.localId() > 0);
+  m_testContactIds << compatible.localId();
+  // Check that the manager URI was updated
+  QVERIFY(compatible.id().managerUri() == m_manager->managerUri());
+  // Check update timestamp
+  QContactTimestamp timestamp = compatible.detail<QContactTimestamp>();
+  QVERIFY(!timestamp.isEmpty());
+  QVERIFY(timestamp.lastModified() >= before_dt);
+  QVERIFY(timestamp.lastModified() <= after_dt);
+
+  qDebug() << "sleeping 1 sec...";
+  QTest::qWait(1000); // Sleep 1 sec to make sure the date/time changes
+
+  // Update the contact
+  QContactName name;
+  name.setFirstName("test");
+  compatible.saveDetail(&name);
+
+  compatible = m_manager->compatibleContact(compatible);
+  before_dt = QDateTime::currentDateTime();
+  QVERIFY(m_manager->saveContact(&compatible));
+  after_dt = QDateTime::currentDateTime();
+  QContactTimestamp new_timestamp = compatible.detail<QContactTimestamp>();
+  QVERIFY(new_timestamp.lastModified() >= before_dt);
+  QVERIFY(new_timestamp.lastModified() <= after_dt);
+  QVERIFY(timestamp.lastModified() < new_timestamp.lastModified());
+
+  QContact saved = m_manager->contact(compatible.localId());
+  QCOMPARE(m_manager->error(), QContactManager::NoError);
+  QVERIFY(saved.id() == compatible.id());
+  QVERIFY(saved.detail<QContactName>().firstName() == compatible.detail<QContactName>().firstName());
+  QVERIFY(saved.detail<QContactTimestamp>().lastModified().toString(Qt::ISODate) ==
+          compatible.detail<QContactTimestamp>().lastModified().toString(Qt::ISODate));
+}
+
+void BasicTest::cleanup()
+{
+  foreach (const QContactLocalId &cid, m_testContactIds) {
+    QVERIFY(m_manager->removeContact(cid));
+  }
+  m_testContactIds.clear();
+}
+
+void BasicTest::testRemoveContact()
+{
+  QContact c;
+  QVERIFY(m_manager->saveContact(&c));
+  QVERIFY(c.localId() > 0);
+  QVERIFY(m_manager->removeContact(c.localId()));
+  QVERIFY(!m_manager->removeContact(c.localId()));
+}
+
+void BasicTest::cleanupTestCase()
+{
+  delete m_manager;
+}
diff --git a/test/ut_basic/basictest.h b/test/ut_basic/basictest.h
new file mode 100644
index 0000000..b35933e
--- /dev/null
+++ b/test/ut_basic/basictest.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) version 3.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301  USA
+ */
+
+#ifndef BASICTEST_H
+#define BASICTEST_H
+
+#include <QObject>
+#include <QTest>
+#include <QContactManager>
+
+QTM_USE_NAMESPACE
+
+class BasicTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+  void initTestCase();
+  void testAddContactUpdateTimeStamp();
+  void testRemoveContact();
+  void cleanupTestCase();
+  void cleanup();
+
+private:
+  QContactManager *m_manager;
+  QList<QContactLocalId> m_testContactIds;
+
+};
+
+#endif // BASICTEST_H
diff --git a/test/ut_basic/ut_basic.pri b/test/ut_basic/ut_basic.pri
new file mode 100644
index 0000000..48ce0cb
--- /dev/null
+++ b/test/ut_basic/ut_basic.pri
@@ -0,0 +1,6 @@
+
+HEADERS += \
+    ut_basic/basictest.h
+
+SOURCES += \
+    ut_basic/basictest.cpp
diff --git a/test/ut_filtering/filteringtest.cpp b/test/ut_filtering/filteringtest.cpp
index 2ba0eb0..892bded 100644
--- a/test/ut_filtering/filteringtest.cpp
+++ b/test/ut_filtering/filteringtest.cpp
@@ -336,6 +336,20 @@ void FilteringTest::contactFilteringTest() {
   QCOMPARE(ret.size(), 1);
   QCOMPARE(ret.first().localId(), test_contacts.first().localId());
 
+  // Timestamp existence
+  existFilter.setDetailDefinitionName(QContactTimestamp::DefinitionName);
+  ret = manager->contacts(existFilter);
+  QCOMPARE(ret.size(), 2);
+
+  // Timestamp filtering
+  filter.setDetailDefinitionName(QContactTimestamp::DefinitionName,
+                                 QContactTimestamp::FieldModificationTimestamp);
+  filter.setMatchFlags(QContactFilter::MatchExactly);
+  filter.setValue(test_contacts.first().detail<QContactTimestamp>().lastModified());
+  ret = manager->contacts(filter);
+  QCOMPARE(ret.size(), 1);
+  QCOMPARE(ret.first().localId(), test_contacts.first().localId());
+
   // test Asynchronous
   filter.setDetailDefinitionName(QContactName::DefinitionName, QContactName::FieldFirstName);
   filter.setMatchFlags(QContactFilter::MatchExactly);
diff --git a/test/ut_filtering/qcontact_helpers.h b/test/ut_filtering/qcontact_helpers.h
index 6b4848d..6ccbd56 100644
--- a/test/ut_filtering/qcontact_helpers.h
+++ b/test/ut_filtering/qcontact_helpers.h
@@ -38,6 +38,7 @@
 #include <QContactPhoneNumber>
 #include <QContactFetchRequest>
 #include <QContactOnlineAccount>
+#include <QContactTimestamp>
 #include <QEventLoop>
 
 QTM_USE_NAMESPACE
@@ -119,6 +120,8 @@ bool addTestContacts(QContactManager *manager)
     qWarning() << "Failed to save first contact, error code:" << manager->error();
     return false;
   }
+  qDebug() << "sleeping 1 sec...";
+  QTest::qWait(1000); // Sleep one second to make sure the contacts have different timestamps
   // Second contact
   name.setFirstName("Bobali");
   name.setLastName("Bobinski");
-- 
1.7.4.4

