summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-10-06 16:19:51 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-10-10 10:40:01 +0200
commitf689ad1021a7805f6f8b6a81f534b4cb9ca91f51 (patch)
treec18d746b775279f143c8d8052924bb4d83fbb91f
parentc3f6e72c2d46906a4699127b558ca248729ce577 (diff)
downloadsink-f689ad1021a7805f6f8b6a81f534b4cb9ca91f51.tar.gz
sink-f689ad1021a7805f6f8b6a81f534b4cb9ca91f51.zip
Change replay
So far only includes modifications and additions, removals are not yet stored as separate revisions.
-rw-r--r--common/entitystorage.cpp75
-rw-r--r--common/entitystorage.h38
-rw-r--r--common/facade.h4
-rw-r--r--common/genericresource.cpp3
-rw-r--r--common/pipeline.cpp3
-rw-r--r--common/resourceaccess.h2
-rw-r--r--common/resultset.h12
-rw-r--r--common/storage_common.cpp19
-rw-r--r--examples/client/main.cpp4
-rw-r--r--tests/dummyresourcetest.cpp4
-rw-r--r--tests/genericfacadetest.cpp5
-rw-r--r--tests/storagetest.cpp9
12 files changed, 131 insertions, 47 deletions
diff --git a/common/entitystorage.cpp b/common/entitystorage.cpp
index 0eb2763..60d58ad 100644
--- a/common/entitystorage.cpp
+++ b/common/entitystorage.cpp
@@ -44,18 +44,21 @@ static void scan(const Akonadi2::Storage::Transaction &transaction, const QByteA
44 }); 44 });
45} 45}
46 46
47void EntityStorageBase::readValue(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)> &resultCallback) 47void EntityStorageBase::readEntity(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback)
48{ 48{
49 //This only works for a 1:1 mapping of resource to domain types.
50 //Not i.e. for tags that are stored as flags in each entity of an imap store.
51 //additional properties that don't have a 1:1 mapping (such as separately stored tags),
52 //could be added to the adaptor.
53 //TODO: resource implementations should be able to customize the retrieval function for non 1:1 entity-buffer mapping cases
49 scan(transaction, key, [=](const QByteArray &key, const Akonadi2::Entity &entity) { 54 scan(transaction, key, [=](const QByteArray &key, const Akonadi2::Entity &entity) {
50 const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(entity.metadata()); 55 const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(entity.metadata());
56 Q_ASSERT(metadataBuffer);
51 qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; 57 qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
52 //This only works for a 1:1 mapping of resource to domain types. 58 auto operation = metadataBuffer->operation();
53 //Not i.e. for tags that are stored as flags in each entity of an imap store.
54 //additional properties that don't have a 1:1 mapping (such as separately stored tags),
55 //could be added to the adaptor.
56 59
57 auto domainObject = create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)); 60 auto domainObject = create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity));
58 resultCallback(domainObject); 61 resultCallback(domainObject, operation);
59 return false; 62 return false;
60 }, mBufferType); 63 }, mBufferType);
61} 64}
@@ -80,16 +83,22 @@ static ResultSet fullScan(const Akonadi2::Storage::Transaction &transaction, con
80 return ResultSet(keys); 83 return ResultSet(keys);
81} 84}
82 85
83ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, qint64 baseRevision, qint64 topRevision) 86ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool initialQuery)
84{ 87{
85 auto resultSetPtr = QSharedPointer<ResultSet>::create(resultSet); 88 auto resultSetPtr = QSharedPointer<ResultSet>::create(resultSet);
86 89
87 //Read through the source values and return whatever matches the filter 90 //Read through the source values and return whatever matches the filter
88 std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)>)> generator = [this, resultSetPtr, &transaction, filter](std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)> callback) -> bool { 91 std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)>)> generator = [this, resultSetPtr, &transaction, filter, initialQuery](std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> callback) -> bool {
89 while (resultSetPtr->next()) { 92 while (resultSetPtr->next()) {
90 readValue(transaction, resultSetPtr->id(), [this, filter, callback](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) { 93 //TODO. every read value is actually a revision that contains one of three operations. Reflect that so the result set can be updated appropriately.
94 //TODO while getting the initial set everything is adding
95 readEntity(transaction, resultSetPtr->id(), [this, filter, callback, initialQuery](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject, Akonadi2::Operation operation) {
91 if (filter(domainObject)) { 96 if (filter(domainObject)) {
92 callback(domainObject); 97 if (initialQuery) {
98 callback(domainObject, Akonadi2::Operation_Creation);
99 } else {
100 callback(domainObject, operation);
101 }
93 } 102 }
94 }); 103 });
95 } 104 }
@@ -98,15 +107,51 @@ ResultSet EntityStorageBase::filteredSet(const ResultSet &resultSet, const std::
98 return ResultSet(generator); 107 return ResultSet(generator);
99} 108}
100 109
101ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision, qint64 topRevision) 110ResultSet EntityStorageBase::loadInitialResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters)
102{ 111{
103 QSet<QByteArray> appliedFilters; 112 QSet<QByteArray> appliedFilters;
104 ResultSet resultSet = queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction); 113 auto resultSet = queryIndexes(query, mResourceInstanceIdentifier, appliedFilters, transaction);
105 const auto remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; 114 remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters;
106 115
107 //We do a full scan if there were no indexes available to create the initial set. 116 //We do a full scan if there were no indexes available to create the initial set.
108 if (appliedFilters.isEmpty()) { 117 if (appliedFilters.isEmpty()) {
109 resultSet = fullScan(transaction, mBufferType); 118 //TODO this should be replaced by an index lookup as well
119 return fullScan(transaction, mBufferType);
120 }
121 return resultSet;
122}
123
124ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision, qint64 topRevision)
125{
126 QSet<QByteArray> remainingFilters = query.propertyFilter.keys().toSet();
127 ResultSet resultSet;
128 const bool initialQuery = (baseRevision == 0);
129 if (initialQuery) {
130 Trace() << "Initial result set update";
131 resultSet = loadInitialResultSet(query, transaction, remainingFilters);
132 } else {
133 //TODO fallback in case the old revision is no longer available to clear + redo complete initial scan
134 Trace() << "Incremental result set update" << baseRevision << topRevision;
135 auto revisionCounter = QSharedPointer<qint64>::create(baseRevision);
136 resultSet = ResultSet([revisionCounter, topRevision, &transaction, this]() -> QByteArray {
137 //Spit out the revision keys one by one.
138 while (*revisionCounter <= topRevision) {
139 const auto uid = Akonadi2::Storage::getUidFromRevision(transaction, *revisionCounter);
140 const auto type = Akonadi2::Storage::getTypeFromRevision(transaction, *revisionCounter);
141 Trace() << "Revision" << *revisionCounter << type << uid;
142 if (type != mBufferType) {
143 //Skip revision
144 *revisionCounter += 1;
145 continue;
146 }
147 const auto key = Akonadi2::Storage::assembleKey(uid, *revisionCounter);
148 *revisionCounter += 1;
149 return key;
150 }
151 //We're done
152 //FIXME make sure result set understands that this means we're done
153 return QByteArray();
154 });
110 } 155 }
111 156
112 auto filter = [remainingFilters, query, baseRevision, topRevision](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) -> bool { 157 auto filter = [remainingFilters, query, baseRevision, topRevision](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) -> bool {
@@ -125,5 +170,5 @@ ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2
125 return true; 170 return true;
126 }; 171 };
127 172
128 return filteredSet(resultSet, filter, transaction, baseRevision, topRevision); 173 return filteredSet(resultSet, filter, transaction, initialQuery);
129} 174}
diff --git a/common/entitystorage.h b/common/entitystorage.h
index 9d928b8..f1d7f84 100644
--- a/common/entitystorage.h
+++ b/common/entitystorage.h
@@ -31,6 +31,8 @@
31 31
32/** 32/**
33 * Wraps storage, entity adaptor factory and indexes into one. 33 * Wraps storage, entity adaptor factory and indexes into one.
34 *
35 * TODO: customize with readEntity instead of adaptor factory
34 */ 36 */
35class EntityStorageBase 37class EntityStorageBase
36{ 38{
@@ -46,14 +48,26 @@ protected:
46 virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr copy(const Akonadi2::ApplicationDomain::ApplicationDomainType &) = 0; 48 virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr copy(const Akonadi2::ApplicationDomain::ApplicationDomainType &) = 0;
47 virtual ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Akonadi2::Storage::Transaction &transaction) = 0; 49 virtual ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Akonadi2::Storage::Transaction &transaction) = 0;
48 50
49 void readValue(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)> &resultCallback); 51 /**
50 ResultSet filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, qint64 baseRevision, qint64 topRevision); 52 * Loads a single entity by uid from storage.
53 *
54 * TODO: Resources should be able to customize this for cases where an entity is not the same as a single buffer.
55 */
56 void readEntity(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)> &resultCallback);
51 ResultSet getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision, qint64 topRevision); 57 ResultSet getResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, qint64 baseRevision, qint64 topRevision);
52 58
53protected: 59protected:
54 QByteArray mResourceInstanceIdentifier; 60 QByteArray mResourceInstanceIdentifier;
55 QByteArray mBufferType; 61 QByteArray mBufferType;
56 DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory; 62 DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory;
63private:
64 /**
65 * Returns the initial result set that still needs to be filtered.
66 *
67 * To make this efficient indexes should be chosen that are as selective as possible.
68 */
69 ResultSet loadInitialResultSet(const Akonadi2::Query &query, Akonadi2::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters);
70 ResultSet filteredSet(const ResultSet &resultSet, const std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject)> &filter, const Akonadi2::Storage::Transaction &transaction, bool isInitialQuery);
57}; 71};
58 72
59template<typename DomainType> 73template<typename DomainType>
@@ -95,13 +109,25 @@ public:
95 auto transaction = storage.createTransaction(Akonadi2::Storage::ReadOnly); 109 auto transaction = storage.createTransaction(Akonadi2::Storage::ReadOnly);
96 110
97 Log() << "Querying" << revisionRange.first << revisionRange.second; 111 Log() << "Querying" << revisionRange.first << revisionRange.second;
112 //TODO fallback in case the old revision is no longer available to clear + redo complete initial scan
113 //
98 auto resultSet = getResultSet(query, transaction, revisionRange.first, revisionRange.second); 114 auto resultSet = getResultSet(query, transaction, revisionRange.first, revisionRange.second);
99 while(resultSet.next([this, resultProvider](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value) -> bool { 115 while(resultSet.next([this, resultProvider](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value, Akonadi2::Operation operation) -> bool {
100 auto cloned = copy(*value); 116 switch (operation) {
101 resultProvider->add(cloned.template staticCast<DomainType>()); 117 case Akonadi2::Operation_Creation:
118 Trace() << "Got creation";
119 resultProvider->add(copy(*value).template staticCast<DomainType>());
120 break;
121 case Akonadi2::Operation_Modification:
122 Trace() << "Got modification";
123 resultProvider->add(copy(*value).template staticCast<DomainType>());
124 break;
125 case Akonadi2::Operation_Removal:
126 Trace() << "Got removal";
127 break;
128 }
102 return true; 129 return true;
103 })){}; 130 })){};
104 //TODO replay removals and modifications
105 } 131 }
106 132
107}; 133};
diff --git a/common/facade.h b/common/facade.h
index d53ec4a..dab1578 100644
--- a/common/facade.h
+++ b/common/facade.h
@@ -56,8 +56,8 @@ public:
56 if (mLatestRevision == newRevision && mLatestRevision > 0) { 56 if (mLatestRevision == newRevision && mLatestRevision > 0) {
57 return KAsync::null<void>(); 57 return KAsync::null<void>();
58 } 58 }
59 return queryFunction(mLatestRevision + 1, newRevision).then<void, qint64>([this](qint64 revision) { 59 return queryFunction(mLatestRevision, newRevision).then<void, qint64>([this](qint64 revision) {
60 mLatestRevision = revision; 60 mLatestRevision = revision + 1;
61 }).then<void>([](){}); 61 }).then<void>([](){});
62 } 62 }
63 63
diff --git a/common/genericresource.cpp b/common/genericresource.cpp
index 4abcecd..acf84c4 100644
--- a/common/genericresource.cpp
+++ b/common/genericresource.cpp
@@ -204,10 +204,11 @@ void GenericResource::enqueueCommand(MessageQueue &mq, int commandId, const QByt
204void GenericResource::processCommand(int commandId, const QByteArray &data) 204void GenericResource::processCommand(int commandId, const QByteArray &data)
205{ 205{
206 static int modifications = 0; 206 static int modifications = 0;
207 const int batchSize = 100;
207 mUserQueue.startTransaction(); 208 mUserQueue.startTransaction();
208 enqueueCommand(mUserQueue, commandId, data); 209 enqueueCommand(mUserQueue, commandId, data);
209 modifications++; 210 modifications++;
210 if (modifications >= 100) { 211 if (modifications >= batchSize) {
211 mUserQueue.commit(); 212 mUserQueue.commit();
212 modifications = 0; 213 modifications = 0;
213 mCommitQueueTimer.stop(); 214 mCommitQueueTimer.stop();
diff --git a/common/pipeline.cpp b/common/pipeline.cpp
index c108540..6c75bde 100644
--- a/common/pipeline.cpp
+++ b/common/pipeline.cpp
@@ -220,6 +220,7 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
220 } 220 }
221 } 221 }
222 222
223 //TODO use only readPropertyMapper and writePropertyMapper
223 auto adaptorFactory = d->adaptorFactory.value(bufferType); 224 auto adaptorFactory = d->adaptorFactory.value(bufferType);
224 if (!adaptorFactory) { 225 if (!adaptorFactory) {
225 Warning() << "no adaptor factory for type " << bufferType; 226 Warning() << "no adaptor factory for type " << bufferType;
@@ -255,6 +256,7 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
255 256
256 //Apply diff 257 //Apply diff
257 //FIXME only apply the properties that are available in the buffer 258 //FIXME only apply the properties that are available in the buffer
259 Trace() << "Applying changed properties: " << diff->availableProperties();
258 for (const auto &property : diff->availableProperties()) { 260 for (const auto &property : diff->availableProperties()) {
259 newObject->setProperty(property, diff->getProperty(property)); 261 newObject->setProperty(property, diff->getProperty(property));
260 } 262 }
@@ -277,7 +279,6 @@ KAsync::Job<qint64> Pipeline::modifiedEntity(void const *command, size_t size)
277 flatbuffers::FlatBufferBuilder fbb; 279 flatbuffers::FlatBufferBuilder fbb;
278 adaptorFactory->createBuffer(*newObject, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize()); 280 adaptorFactory->createBuffer(*newObject, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize());
279 281
280 //TODO don't overwrite the old entry, but instead store a new revision
281 d->transaction.openDatabase(bufferType + ".main").write(Akonadi2::Storage::assembleKey(key, newRevision), QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize())); 282 d->transaction.openDatabase(bufferType + ".main").write(Akonadi2::Storage::assembleKey(key, newRevision), QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
282 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision); 283 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision);
283 Akonadi2::Storage::recordRevision(d->transaction, newRevision, key, bufferType); 284 Akonadi2::Storage::recordRevision(d->transaction, newRevision, key, bufferType);
diff --git a/common/resourceaccess.h b/common/resourceaccess.h
index e6b9d91..1ff9ca6 100644
--- a/common/resourceaccess.h
+++ b/common/resourceaccess.h
@@ -49,7 +49,7 @@ public:
49 49
50Q_SIGNALS: 50Q_SIGNALS:
51 void ready(bool isReady); 51 void ready(bool isReady);
52 void revisionChanged(unsigned long long revision); 52 void revisionChanged(qint64 revision);
53 53
54public Q_SLOTS: 54public Q_SLOTS:
55 virtual void open() = 0; 55 virtual void open() = 0;
diff --git a/common/resultset.h b/common/resultset.h
index 1a19100..a888177 100644
--- a/common/resultset.h
+++ b/common/resultset.h
@@ -21,6 +21,7 @@
21#include <QVector> 21#include <QVector>
22#include <functional> 22#include <functional>
23#include "domain/applicationdomaintype.h" 23#include "domain/applicationdomaintype.h"
24#include "metadata_generated.h"
24 25
25/* 26/*
26 * An iterator to a result set. 27 * An iterator to a result set.
@@ -30,8 +31,13 @@
30class ResultSet { 31class ResultSet {
31 public: 32 public:
32 33
34 ResultSet()
35 : mIt(nullptr)
36 {
37
38 }
33 39
34 ResultSet(const std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)>)> &generator) 40 ResultSet(const std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)>)> &generator)
35 : mIt(nullptr), 41 : mIt(nullptr),
36 mValueGenerator(generator) 42 mValueGenerator(generator)
37 { 43 {
@@ -67,7 +73,7 @@ class ResultSet {
67 return false; 73 return false;
68 } 74 }
69 75
70 bool next(std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value)> callback) 76 bool next(std::function<bool(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &value, Akonadi2::Operation)> callback)
71 { 77 {
72 Q_ASSERT(mValueGenerator); 78 Q_ASSERT(mValueGenerator);
73 return mValueGenerator(callback); 79 return mValueGenerator(callback);
@@ -107,6 +113,6 @@ class ResultSet {
107 QVector<QByteArray>::ConstIterator mIt; 113 QVector<QByteArray>::ConstIterator mIt;
108 QByteArray mCurrentValue; 114 QByteArray mCurrentValue;
109 std::function<QByteArray()> mGenerator; 115 std::function<QByteArray()> mGenerator;
110 std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &)>)> mValueGenerator; 116 std::function<bool(std::function<void(const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &, Akonadi2::Operation)>)> mValueGenerator;
111}; 117};
112 118
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index dc02aec..512a13f 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -66,10 +66,7 @@ qint64 Storage::maxRevision(const Akonadi2::Storage::Transaction &transaction)
66 r = revision.toLongLong(); 66 r = revision.toLongLong();
67 return false; 67 return false;
68 }, [](const Error &error){ 68 }, [](const Error &error){
69 if (error.code != ErrorCodes::NotFound) { 69 std::cout << "Coultn'd find uid for revision ";
70 //FIXME
71 // defaultErrorHandler()(error);
72 }
73 }); 70 });
74 return r; 71 return r;
75} 72}
@@ -80,11 +77,8 @@ QByteArray Storage::getUidFromRevision(const Akonadi2::Storage::Transaction &tra
80 transaction.openDatabase("revisions").scan(QByteArray::number(revision), [&](const QByteArray &, const QByteArray &value) -> bool { 77 transaction.openDatabase("revisions").scan(QByteArray::number(revision), [&](const QByteArray &, const QByteArray &value) -> bool {
81 uid = value; 78 uid = value;
82 return false; 79 return false;
83 }, [](const Error &error){ 80 }, [revision](const Error &error){
84 if (error.code != ErrorCodes::NotFound) { 81 std::cout << "Coultn'd find uid for revision " << revision;
85 //FIXME
86 // defaultErrorHandler()(error);
87 }
88 }); 82 });
89 return uid; 83 return uid;
90} 84}
@@ -95,11 +89,8 @@ QByteArray Storage::getTypeFromRevision(const Akonadi2::Storage::Transaction &tr
95 transaction.openDatabase("revisionType").scan(QByteArray::number(revision), [&](const QByteArray &, const QByteArray &value) -> bool { 89 transaction.openDatabase("revisionType").scan(QByteArray::number(revision), [&](const QByteArray &, const QByteArray &value) -> bool {
96 type = value; 90 type = value;
97 return false; 91 return false;
98 }, [](const Error &error){ 92 }, [revision](const Error &error){
99 if (error.code != ErrorCodes::NotFound) { 93 std::cout << "Coultn'd find type for revision " << revision;
100 //FIXME
101 // defaultErrorHandler()(error);
102 }
103 }); 94 });
104 return type; 95 return type;
105} 96}
diff --git a/examples/client/main.cpp b/examples/client/main.cpp
index a0ca51b..ead9dd6 100644
--- a/examples/client/main.cpp
+++ b/examples/client/main.cpp
@@ -128,6 +128,7 @@ int main(int argc, char *argv[])
128 cliOptions.addPositionalArgument(QObject::tr("[resource]"), 128 cliOptions.addPositionalArgument(QObject::tr("[resource]"),
129 QObject::tr("A resource to connect to")); 129 QObject::tr("A resource to connect to"));
130 cliOptions.addOption(QCommandLineOption("clear")); 130 cliOptions.addOption(QCommandLineOption("clear"));
131 cliOptions.addOption(QCommandLineOption("debuglevel"));
131 cliOptions.addHelpOption(); 132 cliOptions.addHelpOption();
132 cliOptions.process(app); 133 cliOptions.process(app);
133 QStringList resources = cliOptions.positionalArguments(); 134 QStringList resources = cliOptions.positionalArguments();
@@ -143,6 +144,9 @@ int main(int argc, char *argv[])
143 } 144 }
144 return 0; 145 return 0;
145 } 146 }
147 if (cliOptions.isSet("debuglevel")) {
148 Akonadi2::Log::setDebugOutputLevel(static_cast<Akonadi2::Log::DebugLevel>(cliOptions.value("debuglevel").toInt()));
149 }
146 150
147 //Ensure resource is ready 151 //Ensure resource is ready
148 for (const auto &resource : resources) { 152 for (const auto &resource : resources) {
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp
index a28e071..caf808a 100644
--- a/tests/dummyresourcetest.cpp
+++ b/tests/dummyresourcetest.cpp
@@ -2,10 +2,6 @@
2 2
3#include <QString> 3#include <QString>
4 4
5#include "event_generated.h"
6#include "entity_generated.h"
7#include "metadata_generated.h"
8#include "createentity_generated.h"
9#include "dummyresource/resourcefactory.h" 5#include "dummyresource/resourcefactory.h"
10#include "clientapi.h" 6#include "clientapi.h"
11#include "synclistresult.h" 7#include "synclistresult.h"
diff --git a/tests/genericfacadetest.cpp b/tests/genericfacadetest.cpp
index 45ca54d..4c58b91 100644
--- a/tests/genericfacadetest.cpp
+++ b/tests/genericfacadetest.cpp
@@ -68,6 +68,11 @@ class GenericFacadeTest : public QObject
68 Q_OBJECT 68 Q_OBJECT
69private Q_SLOTS: 69private Q_SLOTS:
70 70
71 void init()
72 {
73 Akonadi2::Log::setDebugOutputLevel(Akonadi2::Log::Trace);
74 }
75
71 void testLoad() 76 void testLoad()
72 { 77 {
73 Akonadi2::Query query; 78 Akonadi2::Query query;
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp
index 8d5ee00..8e841cb 100644
--- a/tests/storagetest.cpp
+++ b/tests/storagetest.cpp
@@ -381,6 +381,15 @@ private Q_SLOTS:
381 381
382 QCOMPARE(result, QByteArray("value2")); 382 QCOMPARE(result, QByteArray("value2"));
383 } 383 }
384
385 void testRecordRevision()
386 {
387 Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite);
388 auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite);
389 Akonadi2::Storage::recordRevision(transaction, 1, "uid", "type");
390 QCOMPARE(Akonadi2::Storage::getTypeFromRevision(transaction, 1), QByteArray("type"));
391 QCOMPARE(Akonadi2::Storage::getUidFromRevision(transaction, 1), QByteArray("uid"));
392 }
384}; 393};
385 394
386QTEST_MAIN(StorageTest) 395QTEST_MAIN(StorageTest)