Branch 'kolab/integration/4.13.0' - 10 commits - akonadi/CMakeLists.txt akonadi/monitor_p.cpp akonadi/relationsync.cpp akonadi/relationsync.h akonadi/resourcebase.cpp akonadi/resourcebase.h akonadi/resourcescheduler.cpp akonadi/resourcescheduler_p.h akonadi/tests kimap/getmetadatajob.cpp kimap/tests

Christian Mollekopf mollekopf at kolabsys.com
Wed Nov 12 14:05:16 CET 2014


 akonadi/CMakeLists.txt             |    1 
 akonadi/monitor_p.cpp              |    2 
 akonadi/relationsync.cpp           |  127 +++++++++++++++++++++++++++++++++++++
 akonadi/relationsync.h             |   54 +++++++++++++++
 akonadi/resourcebase.cpp           |   56 ++++++++++++++++
 akonadi/resourcebase.h             |   14 ++++
 akonadi/resourcescheduler.cpp      |   16 ++++
 akonadi/resourcescheduler_p.h      |    3 
 akonadi/tests/tagtest.cpp          |   69 ++++++++++++++++++--
 kimap/getmetadatajob.cpp           |   12 +--
 kimap/tests/getmetadatajobtest.cpp |   37 ++++++++++
 11 files changed, 380 insertions(+), 11 deletions(-)

New commits:
commit 5d2c80defece6c16839e57b00de5d092a05c26e4
Merge: 3e1e8db c58e880
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Tue Nov 11 15:18:26 2014 +0100

    Merge remote-tracking branch 'remotes/kolab/dev/fix_annotaion' into kolab/integration/4.13.0



commit 3e1e8db8b31fad440bf94e68e5e0634c20ce76cc
Merge: 479ce23 f046246
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Tue Nov 11 15:02:21 2014 +0100

    Merge remote-tracking branch 'kolab/feature/remoterelationsync' into kolab/integration/4.13.0



commit f04624638951df75dbb2c571a0fa7db8d0d8f940
Author: Aaron Seigo <aseigo at kde.org>
Date:   Tue Nov 11 12:31:26 2014 +0100

    split tag and relation syncing
    
    this simplifies the queuing and task completion notification, even
    if it is less efficient for kolab

diff --git a/akonadi/resourcebase.cpp b/akonadi/resourcebase.cpp
index 0de70f3..ba0919d 100644
--- a/akonadi/resourcebase.cpp
+++ b/akonadi/resourcebase.cpp
@@ -149,6 +149,7 @@ public:
     void slotCollectionListForAttributesDone(KJob *job);
     void slotCollectionAttributesSyncDone(KJob *job);
     void slotSynchronizeTags();
+    void slotSynchronizeRelations();
     void slotAttributeRetrievalCollectionFetchDone(KJob *job);
 
     void slotItemSyncDone(KJob *job);
@@ -491,6 +492,8 @@ ResourceBase::ResourceBase(const QString &id)
             SLOT(slotSynchronizeCollectionAttributes(Akonadi::Collection)));
     connect(d->scheduler, SIGNAL(executeTagSync()),
             SLOT(slotSynchronizeTags()));
+    connect(d->scheduler, SIGNAL(executeRelationSync()),
+            SLOT(slotSynchronizeRelations()));
     connect(d->scheduler, SIGNAL(executeItemFetch(Akonadi::Item,QSet<QByteArray>)),
             SLOT(slotPrepareItemRetrieval(Akonadi::Item)));
     connect(d->scheduler, SIGNAL(executeResourceCollectionDeletion()),
@@ -987,6 +990,12 @@ void ResourceBasePrivate::slotSynchronizeTags()
     QMetaObject::invokeMethod(q, "retrieveTags");
 }
 
+void ResourceBasePrivate::slotSynchronizeRelations()
+{
+    Q_Q(ResourceBase);
+    QMetaObject::invokeMethod(q, "retrieveRelations");
+}
+
 void ResourceBasePrivate::slotPrepareItemRetrieval(const Akonadi::Item &item)
 {
     Q_Q(ResourceBase);
@@ -1100,6 +1109,11 @@ void ResourceBase::synchronizeTags()
     d_func()->scheduler->scheduleTagSync();
 }
 
+void ResourceBase::synchronizeRelations()
+{
+    d_func()->scheduler->scheduleRelationSync();
+}
+
 void ResourceBase::cancelTask()
 {
     Q_D(ResourceBase);
@@ -1329,6 +1343,12 @@ void ResourceBase::retrieveTags()
     d->scheduler->taskDone();
 }
 
+void ResourceBase::retrieveRelations()
+{
+    Q_D(ResourceBase);
+    d->scheduler->taskDone();
+}
+
 void Akonadi::ResourceBase::abortActivity()
 {
 }
@@ -1405,12 +1425,14 @@ void ResourceBasePrivate::slotTagSyncDone(KJob *job)
             emit q->error(job->errorString());
         }
     }
+
+    scheduler->taskDone();
 }
 
 void ResourceBase::relationsRetrieved(const Relation::List &relations)
 {
     Q_D(ResourceBase);
-    Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncTags ||
+    Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncRelations ||
                d->scheduler->currentTask().type == ResourceScheduler::SyncAll ||
                d->scheduler->currentTask().type == ResourceScheduler::Custom,
                "ResourceBase::relationsRetrieved()",
@@ -1433,6 +1455,7 @@ void ResourceBasePrivate::slotRelationSyncDone(KJob *job)
             emit q->error(job->errorString());
         }
     }
+
     scheduler->taskDone();
 }
 
diff --git a/akonadi/resourcebase.h b/akonadi/resourcebase.h
index cb59938..a18e2ae 100644
--- a/akonadi/resourcebase.h
+++ b/akonadi/resourcebase.h
@@ -277,6 +277,12 @@ protected Q_SLOTS:
     virtual void retrieveTags();
 
     /**
+     * Retreive all relations from the backend
+     * @see relationsRetrieved()
+     */
+    virtual void retrieveRelations();
+
+    /**
      * Retrieve the attributes of a single collection from the backend. The
      * collection to retrieve attributes for is provided as @p collection.
      * Add the attributes parts and call collectionAttributesRetrieved()
@@ -661,6 +667,11 @@ protected:
     void synchronizeTags();
 
     /**
+     * Refetches Relations.
+     */
+    void synchronizeRelations();
+
+    /**
      * Stops the execution of the current task and continues with the next one.
      */
     void cancelTask();
@@ -814,6 +825,7 @@ private:
     Q_PRIVATE_SLOT(d_func(), void slotTagSyncDone(KJob *))
     Q_PRIVATE_SLOT(d_func(), void slotRelationSyncDone(KJob *job))
     Q_PRIVATE_SLOT(d_func(), void slotSynchronizeTags())
+    Q_PRIVATE_SLOT(d_func(), void slotSynchronizeRelations())
     Q_PRIVATE_SLOT(d_func(), void slotItemRetrievalCollectionFetchDone(KJob *));
     Q_PRIVATE_SLOT(d_func(), void slotAttributeRetrievalCollectionFetchDone(KJob *));
 };
diff --git a/akonadi/resourcescheduler.cpp b/akonadi/resourcescheduler.cpp
index b46d23d..93078e5 100644
--- a/akonadi/resourcescheduler.cpp
+++ b/akonadi/resourcescheduler.cpp
@@ -80,6 +80,18 @@ void ResourceScheduler::scheduleTagSync()
   scheduleNext();
 }
 
+void ResourceScheduler::scheduleRelationSync()
+{
+  Task t;
+  t.type = SyncRelations;
+  TaskList& queue = queueForTaskType( t.type );
+  if ( queue.contains( t ) || mCurrentTask == t )
+    return;
+  queue << t;
+  signalTaskToTracker( t, "SyncRelations" );
+  scheduleNext();
+}
+
 void ResourceScheduler::scheduleSync(const Collection & col)
 {
   Task t;
@@ -354,6 +366,9 @@ void ResourceScheduler::executeNext()
     case SyncCollectionTreeDone:
       emit collectionTreeSyncComplete();
       break;
+    case SyncRelations:
+      emit executeRelationSync();
+      break;
     case Custom:
     {
       const QByteArray methodSig = mCurrentTask.methodName + "(QVariant)";
@@ -560,6 +575,7 @@ static const char s_taskTypes[][27] = {
       "InvalideCacheForCollection",
       "SyncAllDone",
       "SyncCollectionTreeDone",
+      "SyncRelations",
       "Custom"
 };
 
diff --git a/akonadi/resourcescheduler_p.h b/akonadi/resourcescheduler_p.h
index 676a7f4..f17d9ea 100644
--- a/akonadi/resourcescheduler_p.h
+++ b/akonadi/resourcescheduler_p.h
@@ -62,6 +62,7 @@ public:
         InvalideCacheForCollection,
         SyncAllDone,
         SyncCollectionTreeDone,
+        SyncRelations,
         Custom
     };
 
@@ -124,6 +125,7 @@ public:
     void scheduleAttributesSync(const Collection &collection);
 
     void scheduleTagSync();
+    void scheduleRelationSync();
 
     /**
       Schedules fetching of a single PIM item.
@@ -231,6 +233,7 @@ Q_SIGNALS:
     void executeCollectionSync(const Akonadi::Collection &col);
     void executeCollectionTreeSync();
     void executeTagSync();
+    void executeRelationSync();
     void executeItemFetch(const Akonadi::Item &item, const QSet<QByteArray> &parts);
     void executeResourceCollectionDeletion();
     void executeCacheInvalidation(const Akonadi::Collection &collection);


commit 82c75a940aa6d970b72d3618fe11f46f334d2a72
Author: Aaron Seigo <aseigo at kde.org>
Date:   Tue Nov 11 12:00:56 2014 +0100

    this piggy-backs on the tags sync

diff --git a/akonadi/resourcebase.cpp b/akonadi/resourcebase.cpp
index 190648e..0de70f3 100644
--- a/akonadi/resourcebase.cpp
+++ b/akonadi/resourcebase.cpp
@@ -1410,7 +1410,7 @@ void ResourceBasePrivate::slotTagSyncDone(KJob *job)
 void ResourceBase::relationsRetrieved(const Relation::List &relations)
 {
     Q_D(ResourceBase);
-    Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncRelations ||
+    Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncTags ||
                d->scheduler->currentTask().type == ResourceScheduler::SyncAll ||
                d->scheduler->currentTask().type == ResourceScheduler::Custom,
                "ResourceBase::relationsRetrieved()",


commit 479ce235cc6f9564ccf757062fa2230f7b44cc54
Merge: 482b63b ccc8c3c
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Tue Nov 11 10:28:20 2014 +0100

    Merge remote-tracking branch 'remotes/kolab/feature/remoterelationsync' into kolab/integration/4.13.0



commit c58e880d9470b1abe23496e3f34f8be23a1fb0bc
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Mon Nov 10 16:36:50 2014 +0100

    KIMAP: Do not request the same attribute or entries multiple times.
    
    If we request metatdata for /shared/comment and /shared/motd, the
    corresponding command should merge same attributes and entries.

diff --git a/kimap/getmetadatajob.cpp b/kimap/getmetadatajob.cpp
index 5d6d417..eb63c53 100644
--- a/kimap/getmetadatajob.cpp
+++ b/kimap/getmetadatajob.cpp
@@ -37,8 +37,8 @@ namespace KIMAP
 
        qint64 maxSize;
        QByteArray depth;
-       QList<QByteArray> entries;
-       QList<QByteArray> attributes;
+       QSet<QByteArray> entries;
+       QSet<QByteArray> attributes;
        QMap<QString, QMap<QByteArray, QMap<QByteArray, QByteArray> > > metadata;
        //    ^ mailbox        ^ entry          ^attribute  ^ value
   };
@@ -167,15 +167,15 @@ void GetMetaDataJob::addEntry(const QByteArray &entry, const QByteArray &attribu
   if ( d->serverCapability == Annotatemore && attribute.isNull() ) {
     qWarning() << "In ANNOTATEMORE mode an attribute must be specified with addEntry!";
   }
-  d->entries.append( entry );
-  d->attributes.append( attribute );
+  d->entries.insert( entry );
+  d->attributes.insert( attribute );
 }
 
 void GetMetaDataJob::addRequestedEntry(const QByteArray &entry)
 {
   Q_D( GetMetaDataJob );
-  d->entries.append( d->removePrefix(entry) );
-  d->attributes.append( d->getAttribute( entry ) );
+  d->entries.insert( d->removePrefix(entry) );
+  d->attributes.insert( d->getAttribute( entry ) );
 }
 
 void GetMetaDataJob::setMaximumSize(qint64 size)
diff --git a/kimap/tests/getmetadatajobtest.cpp b/kimap/tests/getmetadatajobtest.cpp
index 8abd464..7aa848a 100644
--- a/kimap/tests/getmetadatajobtest.cpp
+++ b/kimap/tests/getmetadatajobtest.cpp
@@ -330,6 +330,43 @@ void testAnnotateEntires()
   fakeServer.quit();
 }
 
+void testAnnotateMutiple()
+{
+  //Do not double same parts of the request
+  FakeServer fakeServer;
+  QList<QByteArray> scenario;
+
+  scenario << FakeServer::preauth()
+    << "C: A000001 GETANNOTATION \"Folder1\" (\"/comment\" \"/motd\") \"value.shared\""
+    << "S: A000001 OK annotations retrieved"
+    << "C: A000002 GETANNOTATION \"Folder1\" \"/comment\" (\"value.shared\" \"value.priv\")"
+    << "S: A000002 OK annotations retrieved";
+  fakeServer.setScenario( scenario );
+  fakeServer.startAndWait();
+
+  KIMAP::Session session(  "127.0.0.1", 5989 );
+
+  //C: A000001 GETANNOTATION "Folder1" ("/comment" "/motd") "value.shared"
+  KIMAP::GetMetaDataJob *getMetadataJob = new KIMAP::GetMetaDataJob(  &session );
+  getMetadataJob->setServerCapability( KIMAP::MetaDataJobBase::Annotatemore );
+  getMetadataJob->setMailBox( "Folder1" );
+  getMetadataJob->addRequestedEntry( "/shared/comment" );
+  getMetadataJob->addRequestedEntry( "/shared/motd" );
+  QVERIFY( getMetadataJob->exec() );
+
+  //C: A000002 GETANNOTATION "Folder1" ("/comment") ("value.shared" "value.priv")
+  getMetadataJob = new KIMAP::GetMetaDataJob( &session );
+  getMetadataJob->setServerCapability( KIMAP::MetaDataJobBase::Annotatemore );
+  getMetadataJob->setMailBox("Folder1");
+  getMetadataJob->addRequestedEntry("/shared/comment");
+  getMetadataJob->addRequestedEntry("/private/comment");
+  QVERIFY( getMetadataJob->exec() );
+
+  QVERIFY(fakeServer.isAllScenarioDone());
+  fakeServer.quit();
+}
+
+
 };
 
 QTEST_KDEMAIN_CORE( GetMetadataJobTest )


commit ccc8c3c3d328354e36df6f6e8a4b980599f37343
Author: Aaron Seigo <aseigo at kde.org>
Date:   Mon Nov 10 12:16:33 2014 +0100

    use the relations sync helper in ResourceBase

diff --git a/akonadi/resourcebase.cpp b/akonadi/resourcebase.cpp
index 042275e..190648e 100644
--- a/akonadi/resourcebase.cpp
+++ b/akonadi/resourcebase.cpp
@@ -27,6 +27,7 @@
 #include "dbusconnectionpool.h"
 #include "itemsync.h"
 #include "tagsync.h"
+#include "relationsync.h"
 #include "kdepimlibs-version.h"
 #include "resourcescheduler_p.h"
 #include "tracerinterface.h"
@@ -78,6 +79,7 @@ public:
         , mItemTransactionMode(ItemSync::SingleTransaction)
         , mCollectionSyncer(0)
         , mTagSyncer(0)
+        , mRelationSyncer(0)
         , mHierarchicalRid(false)
         , mUnemittedProgress(0)
         , mAutomaticProgressReporting(true)
@@ -168,6 +170,8 @@ public:
     void slotRecursiveMoveReplayResult(KJob *job);
 
     void slotTagSyncDone(KJob *job);
+    void slotRelationSyncDone(KJob *job);
+
     void slotSessionReconnected()
     {
         Q_Q(ResourceBase);
@@ -446,6 +450,7 @@ public:
     ItemSync::TransactionMode mItemTransactionMode;
     CollectionSync *mCollectionSyncer;
     TagSync *mTagSyncer;
+    RelationSync *mRelationSyncer;
     bool mHierarchicalRid;
     QTimer mProgressEmissionCompressor;
     int mUnemittedProgress;
@@ -1400,6 +1405,34 @@ void ResourceBasePrivate::slotTagSyncDone(KJob *job)
             emit q->error(job->errorString());
         }
     }
+}
+
+void ResourceBase::relationsRetrieved(const Relation::List &relations)
+{
+    Q_D(ResourceBase);
+    Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncRelations ||
+               d->scheduler->currentTask().type == ResourceScheduler::SyncAll ||
+               d->scheduler->currentTask().type == ResourceScheduler::Custom,
+               "ResourceBase::relationsRetrieved()",
+               "Calling relationsRetrieved() although no relation retrieval is in progress");
+    if (!d->mRelationSyncer) {
+        d->mRelationSyncer = new RelationSync(this);
+        connect(d->mRelationSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)));
+        connect(d->mRelationSyncer, SIGNAL(result(KJob*)), SLOT(slotRelationSyncDone(KJob*)));
+    }
+    d->mRelationSyncer->setRemoteRelations(relations);
+}
+
+void ResourceBasePrivate::slotRelationSyncDone(KJob *job)
+{
+    Q_Q(ResourceBase);
+    mRelationSyncer = 0;
+    if (job->error()) {
+        if (job->error() != Job::UserCanceled) {
+            kWarning() << "RelationSync failed: " << job->errorString();
+            emit q->error(job->errorString());
+        }
+    }
     scheduler->taskDone();
 }
 
diff --git a/akonadi/resourcebase.h b/akonadi/resourcebase.h
index 40bda13..cb59938 100644
--- a/akonadi/resourcebase.h
+++ b/akonadi/resourcebase.h
@@ -442,6 +442,7 @@ protected:
     void collectionsRetrieved(const Collection::List &collections);
 
     void tagsRetrieved(const Tag::List &tags, const QHash<QString, Item::List> &tagMembers);
+    void relationsRetrieved(const Relation::List &relations);
 
     /**
      * Call this to supply incrementally retrieved collections from the remote server.
@@ -811,6 +812,7 @@ private:
     Q_PRIVATE_SLOT(d_func(), void slotRecursiveMoveReplay(RecursiveMover *))
     Q_PRIVATE_SLOT(d_func(), void slotRecursiveMoveReplayResult(KJob *))
     Q_PRIVATE_SLOT(d_func(), void slotTagSyncDone(KJob *))
+    Q_PRIVATE_SLOT(d_func(), void slotRelationSyncDone(KJob *job))
     Q_PRIVATE_SLOT(d_func(), void slotSynchronizeTags())
     Q_PRIVATE_SLOT(d_func(), void slotItemRetrievalCollectionFetchDone(KJob *));
     Q_PRIVATE_SLOT(d_func(), void slotAttributeRetrievalCollectionFetchDone(KJob *));


commit 40125eae243dacf61230d3ed3531cafeb87a9bf3
Author: Aaron Seigo <aseigo at kde.org>
Date:   Mon Nov 10 12:11:00 2014 +0100

    add a relation sync helper

diff --git a/akonadi/CMakeLists.txt b/akonadi/CMakeLists.txt
index 106f762..ffce7a6 100644
--- a/akonadi/CMakeLists.txt
+++ b/akonadi/CMakeLists.txt
@@ -195,6 +195,7 @@ set( akonadikde_LIB_SRC
   relationcreatejob.cpp
   relationdeletejob.cpp
   relationfetchjob.cpp
+  relationsync.cpp
   tag.cpp
   tagmodel.cpp
   tagmodel_p.cpp
diff --git a/akonadi/relationsync.cpp b/akonadi/relationsync.cpp
new file mode 100644
index 0000000..2fdc595
--- /dev/null
+++ b/akonadi/relationsync.cpp
@@ -0,0 +1,127 @@
+/*
+    Copyright (c) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+    This library is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Library General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or (at your
+    option) any later version.
+
+    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 Library General Public
+    License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to the
+    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301, USA.
+*/
+namespace Akonadi {
+    class Item;
+}
+
+#include "relationsync.h"
+
+#include <akonadi/itemfetchjob.h>
+#include <akonadi/itemfetchscope.h>
+
+#include <akonadi/relationfetchjob.h>
+#include <akonadi/relationcreatejob.h>
+#include <akonadi/relationdeletejob.h>
+
+#include <QStringList>
+
+using namespace Akonadi;
+
+RelationSync::RelationSync(QObject *parent)
+    : Job(parent),
+      mRemoteRelationsSet(false),
+      mLocalRelationsFetched(false)
+{
+
+}
+
+RelationSync::~RelationSync()
+{
+
+}
+
+void RelationSync::setRemoteRelations(const Akonadi::Relation::List &relations)
+{
+    mRemoteRelations = relations;
+    mRemoteRelationsSet = true;
+    diffRelations();
+}
+
+void RelationSync::doStart()
+{
+    QStringList types;
+    types << QString::fromAscii(Akonadi::Relation::GENERIC);
+    Akonadi::RelationFetchJob *fetch = new Akonadi::RelationFetchJob(types, this);
+    connect(fetch, SIGNAL(result(KJob*)), this, SLOT(onLocalFetchDone(KJob*)));
+}
+
+void RelationSync::onLocalFetchDone(KJob *job)
+{
+    // kDebug();
+    Akonadi::RelationFetchJob *fetch = static_cast<Akonadi::RelationFetchJob *>(job);
+    mLocalRelations = fetch->relations();
+    mLocalRelationsFetched = true;
+    diffRelations();
+}
+
+void RelationSync::diffRelations()
+{
+    if (!mRemoteRelationsSet || !mLocalRelationsFetched) {
+        kDebug() << "waiting for delivery: " << mRemoteRelationsSet << mLocalRelationsFetched;
+        return;
+    }
+
+    // kDebug() << "diffing";
+    QHash<QByteArray, Akonadi::Relation> relationByRid;
+    Q_FOREACH (const Akonadi::Relation &localRelation, mLocalRelations) {
+        if (!localRelation.remoteId().isEmpty()) {
+            relationByRid.insert(localRelation.remoteId(), localRelation);
+        }
+    }
+
+    Q_FOREACH (const Akonadi::Relation &remoteRelation, mRemoteRelations) {
+        if (relationByRid.contains(remoteRelation.remoteId())) {
+            relationByRid.remove(remoteRelation.remoteId());
+        } else {
+            //New relation or had its GID updated, so create one now
+            RelationCreateJob *createJob = new RelationCreateJob(remoteRelation, this);
+            connect(createJob, SIGNAL(result(KJob*)), this, SLOT(checkDone()));
+        }
+    }
+
+    Q_FOREACH (const Akonadi::Relation &removedRelation, relationByRid) {
+        //Removed remotely, remove locally
+        RelationDeleteJob *removeJob = new RelationDeleteJob(removedRelation, this);
+        connect(removeJob, SIGNAL(result(KJob*)), this, SLOT(checkDone()));
+    }
+    checkDone();
+}
+
+void RelationSync::slotResult(KJob *job)
+{
+    if (job->error()) {
+        kWarning() << "Error during CollectionSync: " << job->errorString() << job->metaObject()->className();
+        // pretend there were no errors
+        Akonadi::Job::removeSubjob(job);
+    } else {
+        Akonadi::Job::slotResult(job);
+    }
+}
+
+void RelationSync::checkDone()
+{
+    if (hasSubjobs()) {
+        kDebug() << "Still going";
+        return;
+    }
+    kDebug() << "done";
+    emitResult();
+}
+
+#include "relationsync.moc"
diff --git a/akonadi/relationsync.h b/akonadi/relationsync.h
new file mode 100644
index 0000000..972ad4a
--- /dev/null
+++ b/akonadi/relationsync.h
@@ -0,0 +1,54 @@
+/*
+    Copyright (c) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+    This library is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Library General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or (at your
+    option) any later version.
+
+    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 Library General Public
+    License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to the
+    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301, USA.
+*/
+#ifndef RELATIONSYNC_H
+#define RELATIONSYNC_H
+
+#include "akonadi_export.h"
+
+#include <akonadi/job.h>
+#include <akonadi/relation.h>
+
+class AKONADI_EXPORT RelationSync : public Akonadi::Job
+{
+    Q_OBJECT
+public:
+    RelationSync(QObject *parent = 0);
+    virtual ~RelationSync();
+
+    void setRemoteRelations(const Akonadi::Relation::List &relations);
+
+protected:
+    void doStart();
+
+private Q_SLOTS:
+    void onLocalFetchDone(KJob *job);
+    void slotResult(KJob *job);
+
+private:
+    void diffRelations();
+    void checkDone();
+
+private:
+    Akonadi::Relation::List mRemoteRelations;
+    Akonadi::Relation::List mLocalRelations;
+    bool mRemoteRelationsSet;
+    bool mLocalRelationsFetched;
+};
+
+#endif


commit 482b63b8b65b95813a20f8950059282a439a2c80
Author: Dan Vrátil <dvratil at redhat.com>
Date:   Wed Nov 5 16:01:40 2014 +0100

    Monitor: set RID of a removed Tag when it's available
    
    When a Tag is removed the change notification from server now includes
    RID of the Tag. The RID is resource specific, so each Resource will get
    notification with its own RID, client applications won't get any RIDs.

diff --git a/akonadi/monitor_p.cpp b/akonadi/monitor_p.cpp
index ee207ba..cd35dd6 100644
--- a/akonadi/monitor_p.cpp
+++ b/akonadi/monitor_p.cpp
@@ -1079,6 +1079,8 @@ bool MonitorPrivate::emitTagsNotification(const NotificationMessageV3 &msg, cons
     if (msg.operation() == NotificationMessageV2::Remove) {
         //In case of a removed signal the cache entry was already invalidated, and we therefore received an empty list of tags
         Q_FOREACH (const Entity::Id &uid, msg.uids()) {
+            Tag tag(uid);
+            tag.setRemoteId(msg.entity(uid).remoteId.toLatin1());
             validTags << Tag(uid);
         }
     } else {
diff --git a/akonadi/tests/tagtest.cpp b/akonadi/tests/tagtest.cpp
index 75143b3..7e577ce 100644
--- a/akonadi/tests/tagtest.cpp
+++ b/akonadi/tests/tagtest.cpp
@@ -37,6 +37,7 @@
 #include <akonadi/itemfetchscope.h>
 #include <akonadi/monitor.h>
 #include <akonadi/attributefactory.h>
+#include <akonadi/session.h>
 
 using namespace Akonadi;
 
@@ -50,6 +51,7 @@ private Q_SLOTS:
     void testCreateFetch();
     void testRID();
     void testDelete();
+    void testDeleteRIDIsolation();
     void testModify();
     void testModifyFromResource();
     void testCreateMerge();
@@ -202,6 +204,10 @@ void TagTest::testRIDIsolation()
 
 void TagTest::testDelete()
 {
+    Akonadi::Monitor monitor;
+    monitor.setTypeMonitored(Monitor::Tags);
+    QSignalSpy spy(&monitor, SIGNAL(tagRemoved(Akonadi::Tag)));
+
     Tag tag1;
     {
       tag1.setGid("tag1");
@@ -233,8 +239,63 @@ void TagTest::testDelete()
       TagDeleteJob *deleteJob = new TagDeleteJob(tag2, this);
       AKVERIFYEXEC(deleteJob);
     }
+
+    // Collect Remove notification, so that they don't interfere with testDeleteRIDIsolation
+    QTRY_VERIFY(!spy.isEmpty());
 }
 
+void TagTest::testDeleteRIDIsolation()
+{
+    Tag tag;
+    tag.setGid("gid");
+    tag.setType("mytype");
+    tag.setRemoteId("rid_0");
+
+    {
+        ResourceSelectJob *select = new ResourceSelectJob("akonadi_knut_resource_0");
+        AKVERIFYEXEC(select);
+
+        TagCreateJob *createJob = new TagCreateJob(tag, this);
+        AKVERIFYEXEC(createJob);
+        QVERIFY(createJob->tag().isValid());
+        tag.setId(createJob->tag().id());
+    }
+
+    tag.setRemoteId("rid_1");
+    {
+        ResourceSelectJob *select = new ResourceSelectJob("akonadi_knut_resource_1");
+        AKVERIFYEXEC(select);
+
+        TagCreateJob *createJob = new TagCreateJob(tag, this);
+        createJob->setMergeIfExisting(true);
+        AKVERIFYEXEC(createJob);
+        QVERIFY(createJob->tag().isValid());
+    }
+
+    Akonadi::Monitor monitor;
+    monitor.setTypeMonitored(Akonadi::Monitor::Tags);
+    QSignalSpy signalSpy(&monitor, SIGNAL(tagRemoved(Akonadi::Tag)));
+
+    TagDeleteJob *deleteJob = new TagDeleteJob(tag, this);
+    AKVERIFYEXEC(deleteJob);
+
+    // Other tests notifications might interfere due to notification compression on server
+    QTRY_VERIFY(signalSpy.count() >= 1);
+
+    Tag removedTag;
+    while (!signalSpy.isEmpty()) {
+        const Tag t = signalSpy.takeFirst().takeFirst().value<Akonadi::Tag>();
+        if (t.id() == tag.id()) {
+            removedTag = t;
+            break;
+        }
+    }
+
+    QVERIFY(removedTag.isValid());
+    QVERIFY(removedTag.remoteId().isEmpty());
+}
+
+
 void TagTest::testModify()
 {
     Tag tag;


commit ca4702fa58e4d3fa0b4925a22979a99b70441776
Author: Dan Vrátil <dvratil at redhat.com>
Date:   Wed Nov 5 16:39:12 2014 +0100

    TagTest: Adapt test to behavior change on server
    
    When last RID is removed from a tag, the server will now delete the tag.

diff --git a/akonadi/tests/tagtest.cpp b/akonadi/tests/tagtest.cpp
index 1ddf340..75143b3 100644
--- a/akonadi/tests/tagtest.cpp
+++ b/akonadi/tests/tagtest.cpp
@@ -313,14 +313,12 @@ void TagTest::testModifyFromResource()
         TagModifyJob *modJob = new TagModifyJob(tag, this);
         AKVERIFYEXEC(modJob);
 
+        // The tag is removed on the server, because we just removed the last
+        // RemoteID
         TagFetchJob *fetchJob = new TagFetchJob(this);
         AKVERIFYEXEC(fetchJob);
-        QCOMPARE(fetchJob->tags().size(), 1);
-        QVERIFY(fetchJob->tags().first().remoteId().isEmpty());
+        QCOMPARE(fetchJob->tags().size(), 0);
     }
-
-    TagDeleteJob *deleteJob = new TagDeleteJob(tag, this);
-    AKVERIFYEXEC(deleteJob);
 }
 
 void TagTest::testCreateMerge()




More information about the commits mailing list