summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/CMakeLists.txt18
-rw-r--r--client/clientapi.h419
-rw-r--r--client/main.cpp2
-rw-r--r--client/resourceaccess.cpp181
-rw-r--r--client/resourceaccess.h68
-rw-r--r--client/test/CMakeLists.txt15
-rw-r--r--client/test/clientapitest.cpp48
-rw-r--r--client/threadboundary.cpp29
-rw-r--r--client/threadboundary.h47
9 files changed, 3 insertions, 824 deletions
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index f85839d..c4f857b 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -2,20 +2,6 @@ project(akonadi2_client)
2 2
3include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 3include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
4 4
5set(akonadi2client_SRCS 5add_executable(${PROJECT_NAME} main.cpp)
6 resourceaccess.cpp
7 threadboundary.cpp
8)
9
10add_library(${PROJECT_NAME} SHARED ${akonadi2client_SRCS})
11target_link_libraries(${PROJECT_NAME} akonadi2common) 6target_link_libraries(${PROJECT_NAME} akonadi2common)
12qt5_use_modules(${PROJECT_NAME} Widgets Network) 7install(TARGETS ${PROJECT_NAME} DESTINATION bin)
13install(TARGETS ${PROJECT_NAME} DESTINATION lib)
14
15add_executable(${PROJECT_NAME}_bin main.cpp)
16set_target_properties(${PROJECT_NAME}_bin PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
17target_link_libraries(${PROJECT_NAME}_bin ${PROJECT_NAME} akonadi2common)
18qt5_use_modules(${PROJECT_NAME}_bin Widgets Network)
19install(TARGETS ${PROJECT_NAME}_bin DESTINATION bin)
20
21add_subdirectory(test)
diff --git a/client/clientapi.h b/client/clientapi.h
deleted file mode 100644
index f74e76a..0000000
--- a/client/clientapi.h
+++ /dev/null
@@ -1,419 +0,0 @@
1#pragma once
2
3#include <QString>
4#include <QSet>
5#include <QSharedPointer>
6#include <QStandardPaths>
7#include <QTimer>
8#include <QDebug>
9#include <QEventLoop>
10#include <QtConcurrent/QtConcurrentRun>
11#include <functional>
12#include "threadboundary.h"
13
14namespace async {
15 //This should abstract if we execute from eventloop or in thread.
16 //It supposed to allow the caller to finish the current method before executing the runner.
17 void run(const std::function<void()> &runner) {
18 QtConcurrent::run(runner);
19
20 // //FIXME we should be using a Job instead of a timer
21 // auto timer = new QTimer;
22 // timer->setSingleShot(true);
23 // QObject::connect(timer, &QTimer::timeout, runner);
24 // QObject::connect(timer, &QTimer::timeout, timer, &QObject::deleteLater);
25 // timer->start(0);
26 };
27
28 /**
29 * Query result set
30 */
31
32 template<class T>
33 class ResultEmitter;
34
35 /*
36 * The promise side for the result emitter
37 */
38 template<class T>
39 class ResultProvider {
40 public:
41 //Called from worker thread
42 void add(const T &value)
43 {
44 //We use the eventloop to call the addHandler directly from the main eventloop.
45 //That way the result emitter implementation doesn't have to care about threadsafety at all.
46 //The alternative would be to make all handlers of the emitter threadsafe.
47 auto emitter = mResultEmitter;
48 mResultEmitter->mThreadBoundary.callInMainThread([emitter, value]() {
49 if (emitter) {
50 emitter->addHandler(value);
51 }
52 });
53 }
54
55 //Called from worker thread
56 void complete()
57 {
58 auto emitter = mResultEmitter;
59 mResultEmitter->mThreadBoundary.callInMainThread([emitter]() {
60 if (emitter) {
61 emitter->completeHandler();
62 }
63 });
64 }
65
66 QSharedPointer<ResultEmitter<T> > emitter()
67 {
68 mResultEmitter = QSharedPointer<ResultEmitter<T> >(new ResultEmitter<T>());
69 return mResultEmitter;
70 }
71
72 private:
73 QSharedPointer<ResultEmitter<T> > mResultEmitter;
74 };
75
76 /*
77 * The future side for the client.
78 *
79 * It does not directly hold the state.
80 *
81 * The advantage of this is that we can specialize it to:
82 * * do inline transformations to the data
83 * * directly store the state in a suitable datastructure: QList, QSet, std::list, QVector, ...
84 * * build async interfaces with signals
85 * * build sync interfaces that block when accessing the value
86 *
87 * TODO: This should probably be merged with daniels futurebase used in Async
88 */
89 template<class DomainType>
90 class ResultEmitter {
91 public:
92 void onAdded(const std::function<void(const DomainType&)> &handler)
93 {
94 addHandler = handler;
95 }
96 // void onRemoved(const std::function<void(const T&)> &handler);
97 void onComplete(const std::function<void(void)> &handler)
98 {
99 completeHandler = handler;
100 }
101
102 private:
103 friend class ResultProvider<DomainType>;
104 std::function<void(const DomainType&)> addHandler;
105 // std::function<void(const T&)> removeHandler;
106 std::function<void(void)> completeHandler;
107 ThreadBoundary mThreadBoundary;
108 };
109
110
111 /*
112 * A result set specialization that provides a syncronous list
113 */
114 template<class T>
115 class SyncListResult : public QList<T> {
116 public:
117 SyncListResult(const QSharedPointer<ResultEmitter<T> > &emitter)
118 :QList<T>(),
119 mComplete(false),
120 mEmitter(emitter)
121 {
122 emitter->onAdded([this](const T &value) {
123 this->append(value);
124 });
125 emitter->onComplete([this]() {
126 mComplete = true;
127 auto loop = mWaitLoop.toStrongRef();
128 if (loop) {
129 loop->quit();
130 }
131 });
132 }
133
134 void exec()
135 {
136 auto loop = QSharedPointer<QEventLoop>::create();
137 mWaitLoop = loop;
138 loop->exec(QEventLoop::ExcludeUserInputEvents);
139 }
140
141 private:
142 bool mComplete;
143 QWeakPointer<QEventLoop> mWaitLoop;
144 QSharedPointer<ResultEmitter<T> > mEmitter;
145 };
146}
147
148namespace Akonadi2 {
149
150/**
151 * Standardized Domain Types
152 *
153 * The don't adhere to any standard and can be freely extended
154 * Their sole purpose is providing a standardized interface to access data.
155 *
156 * This is necessary to decouple resource-backends from application domain containers (otherwise each resource would have to provide a faceade for each application domain container).
157 *
158 * These types will be frequently modified (for every new feature that should be exposed to the any client)
159 */
160namespace Domain {
161
162class AkonadiDomainType {
163public:
164 AkonadiDomainType(const QString &resourceName, const QString &identifier, qint64 revision)
165 : mResourceName(resourceName),
166 mIdentifier(identifier),
167 mRevision(revision)
168 {
169 }
170
171 virtual QVariant getProperty(const QString &key){ return QVariant(); }
172
173private:
174 /*
175 * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location.
176 */
177 QString mResourceName;
178 QString mIdentifier;
179 qint64 mRevision;
180};
181
182class Event : public AkonadiDomainType {
183public:
184 typedef QSharedPointer<Event> Ptr;
185 Event(const QString &resource, const QString &identifier, qint64 revision):AkonadiDomainType(resource, identifier, revision){};
186
187};
188
189class Todo : public AkonadiDomainType {
190public:
191 typedef QSharedPointer<Todo> Ptr;
192};
193
194class Calendar : public AkonadiDomainType {
195public:
196 typedef QSharedPointer<Calendar> Ptr;
197};
198
199class Mail : public AkonadiDomainType {
200};
201
202class Folder : public AkonadiDomainType {
203};
204
205/**
206 * All types need to be registered here an MUST return a different name.
207 *
208 * Do not store these types to disk, they may change over time.
209 */
210
211template<class DomainType>
212QString getTypeName();
213
214template<>
215QString getTypeName<Event>()
216{
217 return "event";
218}
219
220template<>
221QString getTypeName<Todo>()
222{
223 return "todo";
224}
225
226}
227
228using namespace async;
229
230/**
231 * A query that matches a set of objects
232 *
233 * The query will have to be updated regularly similary to the domain objects.
234 * It probably also makes sense to have a domain specific part of the query,
235 * such as what properties we're interested in (necessary information for on-demand
236 * loading of data).
237 *
238 * The query defines:
239 * * what resources to search
240 * * filters on various properties (parent collection, startDate range, ....)
241 * * properties we need (for on-demand querying)
242 */
243class Query
244{
245public:
246 //Could also be a propertyFilter
247 QStringList resources;
248 //Could also be a propertyFilter
249 QStringList ids;
250 //Filters to apply
251 QHash<QString, QVariant> propertyFilter;
252 //Properties to retrieve
253 QSet<QString> requestedProperties;
254};
255
256
257/**
258 * Interface for the store facade.
259 *
260 * All methods are synchronous.
261 * Facades are stateful (they hold connections to resources and database).
262 *
263 * TODO: would it make sense to split the write, read and notification parts? (we could potentially save some connections)
264 */
265template<class DomainType>
266class StoreFacade {
267public:
268 virtual ~StoreFacade(){};
269 virtual void create(const DomainType &domainObject) = 0;
270 virtual void modify(const DomainType &domainObject) = 0;
271 virtual void remove(const DomainType &domainObject) = 0;
272 virtual void load(const Query &query, const std::function<void(const typename DomainType::Ptr &)> &resultCallback) = 0;
273};
274
275
276/**
277 * Facade factory that returns a store facade implementation, by loading a plugin and providing the relevant implementation.
278 *
279 * If we were to provide default implementations for certain capabilities. Here would be the place to do so.
280 *
281 * TODO: pluginmechansims for resources to provide their implementations.
282 * * We may want a way to recycle facades to avoid recreating socket connections all the time?
283 */
284
285class FacadeFactory {
286public:
287 //FIXME: proper singleton implementation
288 static FacadeFactory &instance()
289 {
290 static FacadeFactory factory;
291 return factory;
292 }
293
294 static QString key(const QString &resource, const QString &type)
295 {
296 return resource + type;
297 }
298
299 template<class DomainType, class Facade>
300 void registerFacade(const QString &resource)
301 {
302 const QString typeName = Domain::getTypeName<DomainType>();
303 mFacadeRegistry.insert(key(resource, typeName), [](){ return new Facade; });
304 }
305
306 /*
307 * Allows the registrar to register a specific instance.
308 *
309 * Primarily for testing.
310 * The facade factory takes ovnership of the poniter and typically deletes the instance via shared pointer.
311 * Supplied factory functions should therefore always return a new pointer (i.e. via clone())
312 *
313 * FIXME the factory function should really be returning QSharedPointer<void>, which doesn't work (std::shared_pointer<void> would though). That way i.e. a test could keep the object alive until it's done.
314 */
315 template<class DomainType, class Facade>
316 void registerFacade(const QString &resource, const std::function<void*(void)> &customFactoryFunction)
317 {
318 const QString typeName = Domain::getTypeName<DomainType>();
319 mFacadeRegistry.insert(key(resource, typeName), customFactoryFunction);
320 }
321
322 template<class DomainType>
323 QSharedPointer<StoreFacade<DomainType> > getFacade(const QString &resource)
324 {
325 const QString typeName = Domain::getTypeName<DomainType>();
326 auto factoryFunction = mFacadeRegistry.value(key(resource, typeName));
327 if (factoryFunction) {
328 return QSharedPointer<StoreFacade<DomainType> >(static_cast<StoreFacade<DomainType>* >(factoryFunction()));
329 }
330 qWarning() << "Failed to find facade for resource: " << resource << " and type: " << typeName;
331 return QSharedPointer<StoreFacade<DomainType> >();
332 }
333
334private:
335 QHash<QString, std::function<void*(void)> > mFacadeRegistry;
336};
337
338/**
339 * Store interface used in the client API.
340 *
341 * TODO: For testing we need to be able to inject dummy StoreFacades. Should we work with a store instance, or a singleton factory?
342 */
343class Store {
344public:
345 static QString storageLocation()
346 {
347 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2";
348 }
349
350 /**
351 * Asynchronusly load a dataset
352 */
353 template <class DomainType>
354 static QSharedPointer<ResultEmitter<typename DomainType::Ptr> > load(Query query)
355 {
356 QSharedPointer<ResultProvider<typename DomainType::Ptr> > resultSet(new ResultProvider<typename DomainType::Ptr>);
357
358 //Execute the search in a thread.
359 //We must guarantee that the emitter is returned before the first result is emitted.
360 //The result provider must be threadsafe.
361 async::run([resultSet, query](){
362 // Query all resources and aggregate results
363 // query tells us in which resources we're interested
364 // TODO: queries to individual resources could be parallelized
365 for(const QString &resource : query.resources) {
366 auto facade = FacadeFactory::instance().getFacade<DomainType>(resource);
367 //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.
368 std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1);
369 facade->load(query, addCallback);
370 }
371 resultSet->complete();
372 });
373 return resultSet->emitter();
374 }
375
376 /**
377 * Asynchronusly load a dataset with tree structure information
378 */
379 // template <class DomainType>
380 // static TreeSet<DomainType> loadTree(Query)
381 // {
382
383 // }
384
385 /**
386 * Create a new entity.
387 */
388 template <class DomainType>
389 static void create(const DomainType &domainObject, const QString &resourceIdentifier) {
390 //Potentially move to separate thread as well
391 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier);
392 facade.create(domainObject);
393 }
394
395 /**
396 * Modify an entity.
397 *
398 * This includes moving etc. since these are also simple settings on a property.
399 */
400 template <class DomainType>
401 static void modify(const DomainType &domainObject, const QString &resourceIdentifier) {
402 //Potentially move to separate thread as well
403 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier);
404 facade.modify(domainObject);
405 }
406
407 /**
408 * Remove an entity.
409 */
410 template <class DomainType>
411 static void remove(const DomainType &domainObject, const QString &resourceIdentifier) {
412 //Potentially move to separate thread as well
413 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier);
414 facade.remove(domainObject);
415 }
416};
417
418}
419
diff --git a/client/main.cpp b/client/main.cpp
index 77c93d2..c219f47 100644
--- a/client/main.cpp
+++ b/client/main.cpp
@@ -21,7 +21,7 @@
21#include <QCommandLineParser> 21#include <QCommandLineParser>
22 22
23#include "common/console.h" 23#include "common/console.h"
24#include "resourceaccess.h" 24#include "common/resourceaccess.h"
25 25
26int main(int argc, char *argv[]) 26int main(int argc, char *argv[])
27{ 27{
diff --git a/client/resourceaccess.cpp b/client/resourceaccess.cpp
deleted file mode 100644
index 2b58545..0000000
--- a/client/resourceaccess.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "resourceaccess.h"
22
23#include "common/console.h"
24#include "common/commands.h"
25#include "common/handshake_generated.h"
26#include "common/revisionupdate_generated.h"
27
28#include <QDebug>
29#include <QProcess>
30
31namespace Akonadi2
32{
33
34ResourceAccess::ResourceAccess(const QString &resourceName, QObject *parent)
35 : QObject(parent),
36 m_resourceName(resourceName),
37 m_socket(new QLocalSocket(this)),
38 m_tryOpenTimer(new QTimer(this)),
39 m_startingProcess(false)
40{
41 m_tryOpenTimer->setInterval(50);
42 m_tryOpenTimer->setSingleShot(true);
43 connect(m_tryOpenTimer, &QTimer::timeout,
44 this, &ResourceAccess::open);
45
46 log("Starting access");
47 connect(m_socket, &QLocalSocket::connected,
48 this, &ResourceAccess::connected);
49 connect(m_socket, &QLocalSocket::disconnected,
50 this, &ResourceAccess::disconnected);
51 connect(m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
52 this, SLOT(connectionError(QLocalSocket::LocalSocketError)));
53 connect(m_socket, &QIODevice::readyRead,
54 this, &ResourceAccess::readResourceMessage);
55
56}
57
58ResourceAccess::~ResourceAccess()
59{
60
61}
62
63QString ResourceAccess::resourceName() const
64{
65 return m_resourceName;
66}
67
68bool ResourceAccess::isReady() const
69{
70 return m_socket->isValid();
71}
72
73void ResourceAccess::open()
74{
75 if (m_socket->isValid()) {
76 log("Socket valid, so aborting the open");
77 return;
78 }
79
80 m_socket->setServerName(m_resourceName);
81 log(QString("Opening %1").arg(m_socket->serverName()));
82 //FIXME: race between starting the exec and opening the socket?
83 m_socket->open();
84}
85
86void ResourceAccess::close()
87{
88 log(QString("Closing %1").arg(m_socket->fullServerName()));
89 m_socket->close();
90}
91
92void ResourceAccess::connected()
93{
94 m_startingProcess = false;
95 log(QString("Connected: ").arg(m_socket->fullServerName()));
96
97 {
98 auto name = m_fbb.CreateString(QString::number((long long)this).toLatin1());
99 auto command = Akonadi2::CreateHandshake(m_fbb, name);
100 Akonadi2::FinishHandshakeBuffer(m_fbb, command);
101 Commands::write(m_socket, Commands::HandshakeCommand, m_fbb);
102 m_fbb.Clear();
103 }
104
105 emit ready(true);
106}
107
108void ResourceAccess::disconnected()
109{
110 m_socket->close();
111 log(QString("Disconnected from %1").arg(m_socket->fullServerName()));
112 emit ready(false);
113 open();
114}
115
116void ResourceAccess::connectionError(QLocalSocket::LocalSocketError error)
117{
118 log(QString("Connection error: %2").arg(error));
119 if (m_startingProcess) {
120 if (!m_tryOpenTimer->isActive()) {
121 m_tryOpenTimer->start();
122 }
123 return;
124 }
125
126 m_startingProcess = true;
127 log(QString("Attempting to start resource ") + m_resourceName);
128 QStringList args;
129 args << m_resourceName;
130 if (QProcess::startDetached("akonadi2_synchronizer", args)) {
131 m_socket->open();
132 }
133}
134
135void ResourceAccess::readResourceMessage()
136{
137 if (!m_socket->isValid()) {
138 return;
139 }
140
141 m_partialMessageBuffer += m_socket->readAll();
142
143 // should be scheduled rather than processed all at once
144 while (processMessageBuffer()) {}
145}
146
147bool ResourceAccess::processMessageBuffer()
148{
149 static const int headerSize = (sizeof(int) * 2);
150 if (m_partialMessageBuffer.size() < headerSize) {
151 return false;
152 }
153
154 const int commandId = *(int*)m_partialMessageBuffer.constData();
155 const int size = *(int*)(m_partialMessageBuffer.constData() + sizeof(int));
156
157 if (size > m_partialMessageBuffer.size() - headerSize) {
158 return false;
159 }
160
161 switch (commandId) {
162 case Commands::RevisionUpdateCommand: {
163 auto buffer = Akonadi2::GetRevisionUpdate(m_partialMessageBuffer.constData() + headerSize);
164 log(QString("Revision updated to: %1").arg(buffer->revision()));
165 emit revisionChanged(buffer->revision());
166 break;
167 }
168 default:
169 break;
170 }
171
172 m_partialMessageBuffer.remove(0, headerSize + size);
173 return m_partialMessageBuffer.size() >= headerSize;
174}
175
176void ResourceAccess::log(const QString &message)
177{
178 Console::main()->log(m_resourceName + ": " + message);
179}
180
181}
diff --git a/client/resourceaccess.h b/client/resourceaccess.h
deleted file mode 100644
index f381af1..0000000
--- a/client/resourceaccess.h
+++ /dev/null
@@ -1,68 +0,0 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include <QLocalSocket>
24#include <QObject>
25#include <QTimer>
26
27#include <flatbuffers/flatbuffers.h>
28
29namespace Akonadi2
30{
31
32class ResourceAccess : public QObject
33{
34 Q_OBJECT
35
36public:
37 ResourceAccess(const QString &resourceName, QObject *parent = 0);
38 ~ResourceAccess();
39
40 QString resourceName() const;
41 bool isReady() const;
42
43public Q_SLOTS:
44 void open();
45 void close();
46
47Q_SIGNALS:
48 void ready(bool isReady);
49 void revisionChanged(unsigned long long revision);
50
51private Q_SLOTS:
52 void connected();
53 void disconnected();
54 void connectionError(QLocalSocket::LocalSocketError error);
55 void readResourceMessage();
56 bool processMessageBuffer();
57
58private:
59 void log(const QString &message);
60 QString m_resourceName;
61 QLocalSocket *m_socket;
62 QTimer *m_tryOpenTimer;
63 bool m_startingProcess;
64 QByteArray m_partialMessageBuffer;
65 flatbuffers::FlatBufferBuilder m_fbb;
66};
67
68}
diff --git a/client/test/CMakeLists.txt b/client/test/CMakeLists.txt
deleted file mode 100644
index 601899a..0000000
--- a/client/test/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
1set(CMAKE_AUTOMOC ON)
2include_directories(${CMAKE_CURRENT_BINARY_DIR})
3
4macro(auto_tests)
5 foreach(_testname ${ARGN})
6 add_executable(${_testname} ${_testname}.cpp ${store_SRCS})
7 qt5_use_modules(${_testname} Core Test)
8 target_link_libraries(${_testname} lmdb akonadi2_client)
9 add_test(NAME ${_testname} COMMAND ${_testname})
10 endforeach(_testname)
11endmacro(auto_tests)
12
13auto_tests (
14 clientapitest
15)
diff --git a/client/test/clientapitest.cpp b/client/test/clientapitest.cpp
deleted file mode 100644
index 2d1c238..0000000
--- a/client/test/clientapitest.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
1#include <QtTest>
2#include <QDebug>
3#include <functional>
4
5#include "../clientapi.h"
6
7class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event>
8{
9public:
10 ~DummyResourceFacade(){};
11 virtual void create(const Akonadi2::Domain::Event &domainObject){};
12 virtual void modify(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)
15 {
16 qDebug() << "load called";
17 for(const auto &result : results) {
18 resultCallback(result);
19 }
20 }
21
22 QList<Akonadi2::Domain::Event::Ptr> results;
23};
24
25class ClientAPITest : public QObject
26{
27 Q_OBJECT
28private Q_SLOTS:
29
30 void testLoad()
31 {
32 DummyResourceFacade facade;
33 facade.results << QSharedPointer<Akonadi2::Domain::Event>::create("resource", "id", 0);
34
35 Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); });
36
37 Akonadi2::Query query;
38 query.resources << "dummyresource";
39
40 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query));
41 result.exec();
42 QCOMPARE(result.size(), 1);
43 }
44
45};
46
47QTEST_MAIN(ClientAPITest)
48#include "clientapitest.moc"
diff --git a/client/threadboundary.cpp b/client/threadboundary.cpp
deleted file mode 100644
index 47ec508..0000000
--- a/client/threadboundary.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "threadboundary.h"
22
23Q_DECLARE_METATYPE(std::function<void()>);
24
25namespace async {
26ThreadBoundary::ThreadBoundary(): QObject() { qRegisterMetaType<std::function<void()> >("std::function<void()>"); }
27ThreadBoundary:: ~ThreadBoundary() {}
28}
29
diff --git a/client/threadboundary.h b/client/threadboundary.h
deleted file mode 100644
index 9881afa..0000000
--- a/client/threadboundary.h
+++ /dev/null
@@ -1,47 +0,0 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include <QObject>
24#include <functional>
25
26namespace async {
27 /*
28 * A helper class to invoke a method in a different thread using the event loop.
29 * The ThreadBoundary object must live in the thread where the function should be called.
30 */
31 class ThreadBoundary : public QObject {
32 Q_OBJECT
33 public:
34 ThreadBoundary();
35 virtual ~ThreadBoundary();
36
37 //Call in worker thread
38 void callInMainThread(std::function<void()> f) {
39 QMetaObject::invokeMethod(this, "addValueInMainThread", Qt::QueuedConnection, QGenericReturnArgument(), Q_ARG(std::function<void()>, f));
40 }
41 public slots:
42 //Get's called in main thread by it's eventloop
43 void addValueInMainThread(std::function<void()> f) {
44 f();
45 }
46 };
47}