summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/clientapi.h19
-rw-r--r--common/pipeline.cpp7
-rw-r--r--common/resourceaccess.cpp20
-rw-r--r--common/resourceaccess.h1
-rw-r--r--common/storage.h5
-rw-r--r--common/storage_common.cpp19
-rw-r--r--common/storage_lmdb.cpp16
-rw-r--r--common/test/clientapitest.cpp3
-rw-r--r--dummyresource/dummycalendar.fbs1
-rw-r--r--dummyresource/facade.cpp63
-rw-r--r--dummyresource/facade.h9
-rw-r--r--dummyresource/resourcefactory.cpp87
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/dummyresourcetest.cpp35
14 files changed, 244 insertions, 43 deletions
diff --git a/common/clientapi.h b/common/clientapi.h
index 6054130..d2757e7 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -278,7 +278,7 @@ public:
278 virtual void create(const DomainType &domainObject) = 0; 278 virtual void create(const DomainType &domainObject) = 0;
279 virtual void modify(const DomainType &domainObject) = 0; 279 virtual void modify(const DomainType &domainObject) = 0;
280 virtual void remove(const DomainType &domainObject) = 0; 280 virtual void remove(const DomainType &domainObject) = 0;
281 virtual void load(const Query &query, const std::function<void(const typename DomainType::Ptr &)> &resultCallback) = 0; 281 virtual void load(const Query &query, const std::function<void(const typename DomainType::Ptr &)> &resultCallback, const std::function<void()> &completeCallback) = 0;
282}; 282};
283 283
284 284
@@ -353,7 +353,7 @@ class Store {
353public: 353public:
354 static QString storageLocation() 354 static QString storageLocation()
355 { 355 {
356 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2"; 356 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage";
357 } 357 }
358 358
359 /** 359 /**
@@ -371,13 +371,24 @@ public:
371 // Query all resources and aggregate results 371 // Query all resources and aggregate results
372 // query tells us in which resources we're interested 372 // query tells us in which resources we're interested
373 // TODO: queries to individual resources could be parallelized 373 // TODO: queries to individual resources could be parallelized
374 auto eventloop = QSharedPointer<QEventLoop>::create();
375 int completeCounter = 0;
374 for(const QString &resource : query.resources) { 376 for(const QString &resource : query.resources) {
375 auto facade = FacadeFactory::instance().getFacade<DomainType>(resource); 377 auto facade = FacadeFactory::instance().getFacade<DomainType>(resource);
376 //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive. 378 //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive.
377 std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1); 379 std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1);
378 facade->load(query, addCallback); 380 //We copy the facade pointer to keep it alive
381 facade->load(query, addCallback, [&completeCounter, &query, resultSet, facade, eventloop]() {
382 //TODO use jobs instead of this counter
383 completeCounter++;
384 if (completeCounter == query.resources.size()) {
385 resultSet->complete();
386 eventloop->quit();
387 }
388 });
379 } 389 }
380 resultSet->complete(); 390 //The thread contains no eventloop, so execute one here
391 eventloop->exec(QEventLoop::ExcludeUserInputEvents);
381 }); 392 });
382 return resultSet->emitter(); 393 return resultSet->emitter();
383 } 394 }
diff --git a/common/pipeline.cpp b/common/pipeline.cpp
index cf508c5..739909d 100644
--- a/common/pipeline.cpp
+++ b/common/pipeline.cpp
@@ -31,7 +31,7 @@ class Pipeline::Private
31{ 31{
32public: 32public:
33 Private(const QString &resourceName) 33 Private(const QString &resourceName)
34 : storage(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", resourceName), 34 : storage(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", resourceName, Storage::ReadWrite),
35 stepScheduled(false) 35 stepScheduled(false)
36 { 36 {
37 } 37 }
@@ -71,6 +71,11 @@ void Pipeline::null()
71 71
72void Pipeline::newEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder &entity) 72void Pipeline::newEntity(const QByteArray &key, flatbuffers::FlatBufferBuilder &entity)
73{ 73{
74 const qint64 newRevision = storage().maxRevision() + 1;
75 //FIXME this should go into a preprocessor
76 storage().write(key, key.size(), reinterpret_cast<char*>(entity.GetBufferPointer()), entity.GetSize());
77 storage().setMaxRevision(newRevision);
78
74 PipelineState state(this, NewPipeline, key, d->newPipeline); 79 PipelineState state(this, NewPipeline, key, d->newPipeline);
75 d->activePipelines << state; 80 d->activePipelines << state;
76 state.step(); 81 state.step();
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
index a7e14f5..1706ac4 100644
--- a/common/resourceaccess.cpp
+++ b/common/resourceaccess.cpp
@@ -58,7 +58,7 @@ public:
58 58
59 void write(QIODevice *device, uint messageId) 59 void write(QIODevice *device, uint messageId)
60 { 60 {
61 Console::main()->log(QString("\tSending queued command %1").arg(m_commandId)); 61 // Console::main()->log(QString("\tSending queued command %1").arg(m_commandId));
62 Commands::write(device, messageId, m_commandId, m_buffer, m_bufferSize); 62 Commands::write(device, messageId, m_commandId, m_buffer, m_bufferSize);
63 } 63 }
64 64
@@ -82,6 +82,7 @@ public:
82 QByteArray partialMessageBuffer; 82 QByteArray partialMessageBuffer;
83 flatbuffers::FlatBufferBuilder fbb; 83 flatbuffers::FlatBufferBuilder fbb;
84 QVector<QueuedCommand *> commandQueue; 84 QVector<QueuedCommand *> commandQueue;
85 QVector<std::function<void()> > synchronizeResultHandler;
85 uint messageId; 86 uint messageId;
86}; 87};
87 88
@@ -149,6 +150,13 @@ void ResourceAccess::sendCommand(int commandId, flatbuffers::FlatBufferBuilder &
149 } 150 }
150} 151}
151 152
153void ResourceAccess::synchronizeResource(const std::function<void()> &resultHandler)
154{
155 sendCommand(Commands::SynchronizeCommand);
156 //TODO: this should be implemented as a job, so we don't need to store the result handler as member
157 d->synchronizeResultHandler << resultHandler;
158}
159
152void ResourceAccess::open() 160void ResourceAccess::open()
153{ 161{
154 if (d->socket->isValid()) { 162 if (d->socket->isValid()) {
@@ -262,6 +270,13 @@ bool ResourceAccess::processMessageBuffer()
262 auto buffer = GetRevisionUpdate(d->partialMessageBuffer.constData() + headerSize); 270 auto buffer = GetRevisionUpdate(d->partialMessageBuffer.constData() + headerSize);
263 log(QString("Revision updated to: %1").arg(buffer->revision())); 271 log(QString("Revision updated to: %1").arg(buffer->revision()));
264 emit revisionChanged(buffer->revision()); 272 emit revisionChanged(buffer->revision());
273
274 //FIXME: The result handler should be called on completion of the synchronize command, and not upon arbitrary revision updates.
275 for(auto handler : d->synchronizeResultHandler) {
276 //FIXME: we should associate the handler with a buffer->id() to avoid prematurely triggering the result handler from a delayed synchronized response (this is relevant for on-demand syncing).
277 handler();
278 }
279 d->synchronizeResultHandler.clear();
265 break; 280 break;
266 } 281 }
267 case Commands::CommandCompletion: { 282 case Commands::CommandCompletion: {
@@ -280,7 +295,8 @@ bool ResourceAccess::processMessageBuffer()
280 295
281void ResourceAccess::log(const QString &message) 296void ResourceAccess::log(const QString &message)
282{ 297{
283 Console::main()->log(d->resourceName + ": " + message); 298 qDebug() << d->resourceName + ": " + message;
299 // Console::main()->log(d->resourceName + ": " + message);
284} 300}
285 301
286} 302}
diff --git a/common/resourceaccess.h b/common/resourceaccess.h
index 3a35af6..7416b25 100644
--- a/common/resourceaccess.h
+++ b/common/resourceaccess.h
@@ -42,6 +42,7 @@ public:
42 42
43 void sendCommand(int commandId); 43 void sendCommand(int commandId);
44 void sendCommand(int commandId, flatbuffers::FlatBufferBuilder &fbb); 44 void sendCommand(int commandId, flatbuffers::FlatBufferBuilder &fbb);
45 void synchronizeResource(const std::function<void()> &resultHandler);
45 46
46public Q_SLOTS: 47public Q_SLOTS:
47 void open(); 48 void open();
diff --git a/common/storage.h b/common/storage.h
index 57ee56c..6cfa3d6 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -71,6 +71,11 @@ public:
71 static std::function<void(const Storage::Error &error)> basicErrorHandler(); 71 static std::function<void(const Storage::Error &error)> basicErrorHandler();
72 qint64 diskUsage() const; 72 qint64 diskUsage() const;
73 void removeFromDisk() const; 73 void removeFromDisk() const;
74
75 qint64 maxRevision();
76 void setMaxRevision(qint64 revision);
77
78 bool exists() const;
74private: 79private:
75 class Private; 80 class Private;
76 Private * const d; 81 Private * const d;
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index 8f465fc..bdae9dd 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -52,4 +52,23 @@ void Storage::scan(const std::string &sKey, const std::function<bool(void *keyPt
52 scan(sKey.data(), sKey.size(), resultHandler, &errorHandler); 52 scan(sKey.data(), sKey.size(), resultHandler, &errorHandler);
53} 53}
54 54
55void Storage::setMaxRevision(qint64 revision)
56{
57 write("__internal_maxRevision", QString::number(revision).toStdString());
58}
59
60qint64 Storage::maxRevision()
61{
62 qint64 r = 0;
63 read(std::string("__internal_maxRevision"), [&](const std::string &revision) -> bool {
64 r = QString::fromStdString(revision).toLongLong();
65 return false;
66 },
67 [](const Storage::Error &error) {
68 //Ignore the error in case we don't find the value
69 //TODO only ignore value not found errors
70 });
71 return r;
72}
73
55} // namespace Akonadi2 74} // namespace Akonadi2
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index a8ec378..eeb0045 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -59,6 +59,7 @@ QMutex Storage::Private::sMutex;
59Storage::Private::Private(const QString &s, const QString &n, AccessMode m) 59Storage::Private::Private(const QString &s, const QString &n, AccessMode m)
60 : storageRoot(s), 60 : storageRoot(s),
61 name(n), 61 name(n),
62 env(0),
62 transaction(0), 63 transaction(0),
63 mode(m), 64 mode(m),
64 readTransaction(false), 65 readTransaction(false),
@@ -66,7 +67,7 @@ Storage::Private::Private(const QString &s, const QString &n, AccessMode m)
66{ 67{
67 const QString fullPath(storageRoot + '/' + name); 68 const QString fullPath(storageRoot + '/' + name);
68 QDir dir; 69 QDir dir;
69 dir.mkdir(storageRoot); 70 dir.mkpath(storageRoot);
70 dir.mkdir(fullPath); 71 dir.mkdir(fullPath);
71 72
72 //This seems to resolve threading related issues, not sure why though 73 //This seems to resolve threading related issues, not sure why though
@@ -97,8 +98,10 @@ Storage::Private::~Private()
97 } 98 }
98 99
99 // it is still there and still unused, so we can shut it down 100 // it is still there and still unused, so we can shut it down
100 mdb_dbi_close(env, dbi); 101 if (env) {
101 mdb_env_close(env); 102 mdb_dbi_close(env, dbi);
103 mdb_env_close(env);
104 }
102} 105}
103 106
104Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode) 107Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode)
@@ -111,6 +114,10 @@ Storage::~Storage()
111 delete d; 114 delete d;
112} 115}
113 116
117bool Storage::exists() const
118{
119 return (d->env != 0);
120}
114bool Storage::isInTransaction() const 121bool Storage::isInTransaction() const
115{ 122{
116 return d->transaction; 123 return d->transaction;
@@ -313,12 +320,9 @@ void Storage::scan(const char *keyData, uint keySize,
313 errorHandler(error); 320 errorHandler(error);
314 } 321 }
315 322
316 /**
317 we don't abort the transaction since we need it for reading the values
318 if (implicitTransaction) { 323 if (implicitTransaction) {
319 abortTransaction(); 324 abortTransaction();
320 } 325 }
321 */
322} 326}
323 327
324qint64 Storage::diskUsage() const 328qint64 Storage::diskUsage() const
diff --git a/common/test/clientapitest.cpp b/common/test/clientapitest.cpp
index 2d1c238..dd634f1 100644
--- a/common/test/clientapitest.cpp
+++ b/common/test/clientapitest.cpp
@@ -11,12 +11,13 @@ public:
11 virtual void create(const Akonadi2::Domain::Event &domainObject){}; 11 virtual void create(const Akonadi2::Domain::Event &domainObject){};
12 virtual void modify(const Akonadi2::Domain::Event &domainObject){}; 12 virtual void modify(const Akonadi2::Domain::Event &domainObject){};
13 virtual void remove(const Akonadi2::Domain::Event &domainObject){}; 13 virtual void remove(const Akonadi2::Domain::Event &domainObject){};
14 virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) 14 virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback)
15 { 15 {
16 qDebug() << "load called"; 16 qDebug() << "load called";
17 for(const auto &result : results) { 17 for(const auto &result : results) {
18 resultCallback(result); 18 resultCallback(result);
19 } 19 }
20 completeCallback();
20 } 21 }
21 22
22 QList<Akonadi2::Domain::Event::Ptr> results; 23 QList<Akonadi2::Domain::Event::Ptr> results;
diff --git a/dummyresource/dummycalendar.fbs b/dummyresource/dummycalendar.fbs
index 5a217b5..643c9b2 100644
--- a/dummyresource/dummycalendar.fbs
+++ b/dummyresource/dummycalendar.fbs
@@ -6,6 +6,7 @@ table DummyEvent {
6 summary:string; 6 summary:string;
7 description:string; 7 description:string;
8 attachment:[ubyte]; 8 attachment:[ubyte];
9 remoteId:string;
9} 10}
10 11
11root_type DummyEvent; 12root_type DummyEvent;
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp
index 0d47010..c2871bb 100644
--- a/dummyresource/facade.cpp
+++ b/dummyresource/facade.cpp
@@ -23,6 +23,7 @@
23#include <functional> 23#include <functional>
24 24
25#include "common/resourceaccess.h" 25#include "common/resourceaccess.h"
26#include "common/commands.h"
26#include "dummycalendar_generated.h" 27#include "dummycalendar_generated.h"
27 28
28using namespace DummyCalendar; 29using namespace DummyCalendar;
@@ -30,7 +31,7 @@ using namespace flatbuffers;
30 31
31DummyResourceFacade::DummyResourceFacade() 32DummyResourceFacade::DummyResourceFacade()
32 : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(), 33 : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(),
33 mResourceAccess(/* new ResourceAccess("dummyresource") */) 34 mResourceAccess(new Akonadi2::ResourceAccess("org.kde.dummy"))
34{ 35{
35 // connect(mResourceAccess.data(), &ResourceAccess::ready, this, onReadyChanged); 36 // connect(mResourceAccess.data(), &ResourceAccess::ready, this, onReadyChanged);
36} 37}
@@ -95,11 +96,8 @@ public:
95 QSharedPointer<Akonadi2::Storage> storage; 96 QSharedPointer<Akonadi2::Storage> storage;
96}; 97};
97 98
98void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) 99static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query)
99{ 100{
100 qDebug() << "load called";
101 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "dummyresource");
102
103 //Compose some functions to make query matching fast. 101 //Compose some functions to make query matching fast.
104 //This way we can process the query once, and convert all values into something that can be compared quickly 102 //This way we can process the query once, and convert all values into something that can be compared quickly
105 std::function<bool(const std::string &key, DummyEvent const *buffer)> preparedQuery; 103 std::function<bool(const std::string &key, DummyEvent const *buffer)> preparedQuery;
@@ -125,21 +123,48 @@ void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function
125 return true; 123 return true;
126 }; 124 };
127 } 125 }
126 return preparedQuery;
127}
128 128
129 //Because we have no indexes yet, we always do a full scan 129void DummyResourceFacade::synchronizeResource(const std::function<void()> &continuation)
130 storage->scan("", [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { 130{
131 //TODO read second buffer as well 131 //TODO check if a sync is necessary
132 auto eventBuffer = GetDummyEvent(dataValue); 132 //TODO Only sync what was requested
133 if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), eventBuffer)) { 133 //TODO timeout
134 //TODO read the revision from the generic portion of the buffer 134 mResourceAccess->open();
135 auto event = QSharedPointer<DummyEventAdaptor>::create("dummyresource", QString::fromUtf8(static_cast<char*>(keyValue), keySize), 0); 135 mResourceAccess->synchronizeResource(continuation);
136 event->buffer = eventBuffer; 136}
137 event->storage = storage; 137
138 resultCallback(event); 138void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback)
139 } 139{
140 return true; 140 qDebug() << "load called";
141
142 synchronizeResource([=]() {
143 //Now that the sync is complete we can execute the query
144 const auto preparedQuery = prepareQuery(query);
145
146 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy");
147
148 qDebug() << "executing query";
149 //We start a transaction explicitly that we'll leave open so the values can be read.
150 //The transaction will be closed automatically once the storage object is destroyed.
151 storage->startTransaction(Akonadi2::Storage::ReadOnly);
152 //Because we have no indexes yet, we always do a full scan
153 storage->scan("", [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool {
154 //TODO read the three buffers
155 qDebug() << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize));
156 auto eventBuffer = GetDummyEvent(dataValue);
157 if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), eventBuffer)) {
158 //TODO set proper revision
159 qint64 revision = 0;
160 auto event = QSharedPointer<DummyEventAdaptor>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision);
161 event->buffer = eventBuffer;
162 event->storage = storage;
163 resultCallback(event);
164 }
165 return true;
166 });
167 completeCallback();
141 }); 168 });
142} 169}
143 170
144//TODO call in plugin loader
145// Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); });
diff --git a/dummyresource/facade.h b/dummyresource/facade.h
index f179c06..c76e62c 100644
--- a/dummyresource/facade.h
+++ b/dummyresource/facade.h
@@ -22,7 +22,9 @@
22#include "common/clientapi.h" 22#include "common/clientapi.h"
23#include "common/storage.h" 23#include "common/storage.h"
24 24
25class ResourceAccess; 25namespace Akonadi2 {
26 class ResourceAccess;
27}
26 28
27class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> 29class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event>
28{ 30{
@@ -32,8 +34,9 @@ public:
32 virtual void create(const Akonadi2::Domain::Event &domainObject); 34 virtual void create(const Akonadi2::Domain::Event &domainObject);
33 virtual void modify(const Akonadi2::Domain::Event &domainObject); 35 virtual void modify(const Akonadi2::Domain::Event &domainObject);
34 virtual void remove(const Akonadi2::Domain::Event &domainObject); 36 virtual void remove(const Akonadi2::Domain::Event &domainObject);
35 virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback); 37 virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback);
36 38
37private: 39private:
38 QSharedPointer<ResourceAccess> mResourceAccess; 40 void synchronizeResource(const std::function<void()> &continuation);
41 QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess;
39}; 42};
diff --git a/dummyresource/resourcefactory.cpp b/dummyresource/resourcefactory.cpp
index bd85b4f..2c43981 100644
--- a/dummyresource/resourcefactory.cpp
+++ b/dummyresource/resourcefactory.cpp
@@ -20,22 +20,95 @@
20#include "resourcefactory.h" 20#include "resourcefactory.h"
21#include "facade.h" 21#include "facade.h"
22#include "dummycalendar_generated.h" 22#include "dummycalendar_generated.h"
23#include <QUuid>
24
25static std::string createEvent()
26{
27 static const size_t attachmentSize = 1024*2; // 2KB
28 static uint8_t rawData[attachmentSize];
29 static flatbuffers::FlatBufferBuilder fbb;
30 fbb.Clear();
31 {
32 auto summary = fbb.CreateString("summary");
33 auto data = fbb.CreateUninitializedVector<uint8_t>(attachmentSize);
34 DummyCalendar::DummyEventBuilder eventBuilder(fbb);
35 eventBuilder.add_summary(summary);
36 eventBuilder.add_attachment(data);
37 auto eventLocation = eventBuilder.Finish();
38 DummyCalendar::FinishDummyEventBuffer(fbb, eventLocation);
39 memcpy((void*)DummyCalendar::GetDummyEvent(fbb.GetBufferPointer())->attachment()->Data(), rawData, attachmentSize);
40 }
41
42 return std::string(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize());
43}
44
45QMap<QString, QString> populate()
46{
47 QMap<QString, QString> content;
48 for (int i = 0; i < 2; i++) {
49 auto event = createEvent();
50 content.insert(QString("key%1").arg(i), QString::fromStdString(event));
51 }
52 return content;
53}
54
55static QMap<QString, QString> s_dataSource = populate();
23 56
24DummyResource::DummyResource() 57DummyResource::DummyResource()
25 : Akonadi2::Resource() 58 : Akonadi2::Resource()
26{ 59{
60}
27 61
62void findByRemoteId(QSharedPointer<Akonadi2::Storage> storage, const QString &rid, std::function<void(void *keyValue, int keySize, void *dataValue, int dataSize)> callback)
63{
64 //TODO lookup in rid index instead of doing a full scan
65 const std::string ridString = rid.toStdString();
66 storage->scan("", [&](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool {
67 auto eventBuffer = DummyCalendar::GetDummyEvent(dataValue);
68 if (std::string(eventBuffer->remoteId()->c_str(), eventBuffer->remoteId()->size()) == ridString) {
69 callback(keyValue, keySize, dataValue, dataSize);
70 }
71 return true;
72 });
28} 73}
29 74
30void DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeline) 75void DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeline)
31{ 76{
32 // TODO actually populate the storage with new items 77 //TODO use a read-only transaction during the complete sync to sync against a defined revision
33 auto builder = DummyCalendar::DummyEventBuilder(m_fbb); 78
34 builder .add_summary(m_fbb.CreateString("summary summary!")); 79 qDebug() << "synchronize with source";
35 auto buffer = builder.Finish(); 80
36 DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); 81 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy");
37 pipeline->newEntity("fakekey", m_fbb); 82 for (auto it = s_dataSource.constBegin(); it != s_dataSource.constEnd(); it++) {
38 m_fbb.Clear(); 83 bool isNew = true;
84 if (storage->exists()) {
85 findByRemoteId(storage, it.key(), [&](void *keyValue, int keySize, void *dataValue, int dataSize) {
86 isNew = false;
87 });
88 }
89
90 if (isNew) {
91 //TODO: perhaps it would be more convenient to populate the domain types?
92 //Resource specific parts are not accessible that way, but then we would only have to implement the property mapping in one place
93 const QByteArray data = it.value().toUtf8();
94 auto eventBuffer = DummyCalendar::GetDummyEvent(data.data());
95
96 //Map the source format to the buffer format (which happens to be an exact copy here)
97 auto builder = DummyCalendar::DummyEventBuilder(m_fbb);
98 builder.add_summary(m_fbb.CreateString(eventBuffer->summary()->c_str()));
99 auto buffer = builder.Finish();
100 DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer);
101
102 //TODO toRFC4122 would probably be more efficient, but results in non-printable keys.
103 const auto key = QUuid::createUuid().toString().toUtf8();
104 //TODO can we really just start populating the buffer and pass the buffer builder?
105 qDebug() << "new event";
106 pipeline->newEntity(key, m_fbb);
107 } else { //modification
108 //TODO diff and create modification if necessary
109 }
110 }
111 //TODO find items to remove
39} 112}
40 113
41void DummyResource::processCommand(int commandId, const QByteArray &data, uint size, Akonadi2::Pipeline *pipeline) 114void DummyResource::processCommand(int commandId, const QByteArray &data, uint size, Akonadi2::Pipeline *pipeline)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4c288e9..dcf2f21 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -17,7 +17,9 @@ manual_tests (
17 storagebenchmark 17 storagebenchmark
18 storagetest 18 storagetest
19 dummyresourcefacadetest 19 dummyresourcefacadetest
20 dummyresourcetest
20) 21)
21 22
22target_link_libraries(dummyresourcefacadetest akonadi2_resource_dummy) 23target_link_libraries(dummyresourcefacadetest akonadi2_resource_dummy)
24target_link_libraries(dummyresourcetest akonadi2_resource_dummy)
23 25
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp
new file mode 100644
index 0000000..75d29de
--- /dev/null
+++ b/tests/dummyresourcetest.cpp
@@ -0,0 +1,35 @@
1#include <QtTest>
2
3#include <QString>
4
5#include "common/resource.h"
6#include "clientapi.h"
7
8class DummyResourceTest : public QObject
9{
10 Q_OBJECT
11private Q_SLOTS:
12 void initTestCase()
13 {
14 auto factory = Akonadi2::ResourceFactory::load("org.kde.dummy");
15 QVERIFY(factory);
16 }
17
18 void cleanupTestCase()
19 {
20 }
21
22 void testSync()
23 {
24 Akonadi2::Query query;
25 query.resources << "org.kde.dummy";
26
27 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query));
28 result.exec();
29 QVERIFY(!result.isEmpty());
30 }
31
32};
33
34QTEST_MAIN(DummyResourceTest)
35#include "dummyresourcetest.moc"