diff --git a/src/akonadi/akonadicachingstorage.cpp b/src/akonadi/akonadicachingstorage.cpp --- a/src/akonadi/akonadicachingstorage.cpp +++ b/src/akonadi/akonadicachingstorage.cpp @@ -28,6 +28,7 @@ #include "akonadicollectionfetchjobinterface.h" #include "akonadiitemfetchjobinterface.h" +#include "akonaditagfetchjobinterface.h" #include @@ -302,6 +303,67 @@ Item::List m_items; }; +class CachingTagFetchJob : public KCompositeJob, public TagFetchJobInterface +{ + Q_OBJECT +public: + CachingTagFetchJob(const StorageInterface::Ptr &storage, + const Cache::Ptr &cache, + QObject *parent = nullptr) + : KCompositeJob(parent), + m_started(false), + m_storage(storage), + m_cache(cache) + { + QTimer::singleShot(0, this, &CachingTagFetchJob::start); + } + + void start() override + { + if (m_started) + return; + + if (m_cache->isTagListPopulated()) { + QTimer::singleShot(0, this, &CachingTagFetchJob::retrieveFromCache); + } else { + auto job = m_storage->fetchTags(); + addSubjob(job->kjob()); + } + + m_started = true; + } + + Tag::List tags() const override + { + return m_tags; + } + +private: + void slotResult(KJob *kjob) override + { + if (kjob->error()) { + KCompositeJob::slotResult(kjob); + return; + } + + auto job = dynamic_cast(kjob); + Q_ASSERT(job); + m_tags = job->tags(); + m_cache->setTags(m_tags); + emitResult(); + } + + void retrieveFromCache() + { + m_tags = m_cache->tags(); + emitResult(); + } + + bool m_started; + StorageInterface::Ptr m_storage; + Cache::Ptr m_cache; + Tag::List m_tags; +}; CachingStorage::CachingStorage(const Cache::Ptr &cache, const StorageInterface::Ptr &storage) : m_cache(cache), @@ -410,7 +472,7 @@ TagFetchJobInterface *CachingStorage::fetchTags() { - return m_storage->fetchTags(); + return new CachingTagFetchJob(m_storage, m_cache); } #include "akonadicachingstorage.moc" diff --git a/tests/units/akonadi/akonadicachingstoragetest.cpp b/tests/units/akonadi/akonadicachingstoragetest.cpp --- a/tests/units/akonadi/akonadicachingstoragetest.cpp +++ b/tests/units/akonadi/akonadicachingstoragetest.cpp @@ -372,6 +372,68 @@ QVERIFY(cache->item(44).isValid()); } } + + void shouldCacheTags() + { + // GIVEN + AkonadiFakeData data; + + data.createTag(GenTag().withId(42).withName(QStringLiteral("42Plain")).asPlain()); + data.createTag(GenTag().withId(43).withName(QStringLiteral("43Context")).asContext()); + data.createTag(GenTag().withId(44).withName(QStringLiteral("44Plain")).asPlain()); + + auto cache = Akonadi::Cache::Ptr::create(Akonadi::SerializerInterface::Ptr(new Akonadi::Serializer), + Akonadi::MonitorInterface::Ptr(data.createMonitor())); + Akonadi::CachingStorage storage(cache, Akonadi::StorageInterface::Ptr(data.createStorage())); + + // WHEN + auto job = storage.fetchTags(); + QVERIFY2(job->kjob()->exec(), job->kjob()->errorString().toUtf8().constData()); + + // THEN + const auto toTagNames = [](const Akonadi::Tag::List &tags) { + auto res = QStringList(); + std::transform(tags.cbegin(), tags.cend(), + std::back_inserter(res), + std::mem_fn(&Akonadi::Tag::name)); + res.sort(); + return res; + }; + + auto expectedNames = QStringList() << "42Plain" << "43Context" << "44Plain"; + + { + const auto tagFetchNames = [job, toTagNames]{ + return toTagNames(job->tags()); + }(); + QCOMPARE(tagFetchNames, expectedNames); + + const auto tagCachedNames = [cache, toTagNames]{ + const auto tags = cache->tags(); + return toTagNames(tags); + }(); + QCOMPARE(tagCachedNames, expectedNames); + } + + // WHEN (second time shouldn't hit the original storage) + data.storageBehavior().setFetchTagsBehavior(AkonadiFakeStorageBehavior::EmptyFetch); + data.storageBehavior().setFetchTagsErrorCode(128); + job = storage.fetchTags(); + QVERIFY2(job->kjob()->exec(), job->kjob()->errorString().toUtf8().constData()); + + { + const auto tagFetchNames = [job, toTagNames]{ + return toTagNames(job->tags()); + }(); + QCOMPARE(tagFetchNames, expectedNames); + + const auto tagCachedNames = [cache, toTagNames]{ + const auto tags = cache->tags(); + return toTagNames(tags); + }(); + QCOMPARE(tagCachedNames, expectedNames); + } + } }; ZANSHIN_TEST_MAIN(AkonadiCachingStorageTest)