summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-16 08:25:14 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-16 08:25:14 +0100
commited7fdbcf6beb40960cf91555ae1d506278b852d8 (patch)
tree466b262e1d9c2efd63acb44170bdd1add1b98469 /client
parent52787e9469cc1892e9e221b877d0ffd81d7a817a (diff)
downloadsink-ed7fdbcf6beb40960cf91555ae1d506278b852d8.tar.gz
sink-ed7fdbcf6beb40960cf91555ae1d506278b852d8.zip
move client classes into akonadi2common and add the base class for resource plugins
we can divide up libakonadi2common later once we have a full collection of classes this makes writing code a bit simpler now as we don't have to figuer out which libraries to link against or how class dependencies should look. when we have more infrastructure in place this will mostly become self-evident
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}