summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-18 15:17:30 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-18 15:17:30 +0100
commitd8cd2d6585507a4e40881092a633ec1a80b14dd9 (patch)
treee710026c9191fe42643f7996a55b460e3ff39622
parent0d55900241772fa5cc5028934358cbcb28702c46 (diff)
downloadsink-d8cd2d6585507a4e40881092a633ec1a80b14dd9.tar.gz
sink-d8cd2d6585507a4e40881092a633ec1a80b14dd9.zip
Draft of inspection API
-rw-r--r--common/CMakeLists.txt1
-rw-r--r--common/clientapi.cpp17
-rw-r--r--common/clientapi.h25
-rw-r--r--common/commands.cpp2
-rw-r--r--common/commands.h1
-rw-r--r--common/commands/inspection.fbs12
-rw-r--r--common/commands/notification.fbs5
-rw-r--r--common/genericresource.cpp39
-rw-r--r--common/genericresource.h1
-rw-r--r--common/listener.cpp3
-rw-r--r--common/notification.h40
-rw-r--r--common/resource.h2
-rw-r--r--common/resourceaccess.cpp22
-rw-r--r--common/resourceaccess.h4
-rw-r--r--docs/resource.md7
-rw-r--r--examples/dummyresource/resourcefactory.cpp20
-rw-r--r--examples/dummyresource/resourcefactory.h1
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/inspectiontest.cpp64
19 files changed, 267 insertions, 1 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index f07772a..7e142df 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -51,6 +51,7 @@ generate_flatbuffers(
51 commands/synchronize 51 commands/synchronize
52 commands/notification 52 commands/notification
53 commands/revisionreplayed 53 commands/revisionreplayed
54 commands/inspection
54 domain/event 55 domain/event
55 domain/mail 56 domain/mail
56 domain/folder 57 domain/folder
diff --git a/common/clientapi.cpp b/common/clientapi.cpp
index 5c0bcb8..deab962 100644
--- a/common/clientapi.cpp
+++ b/common/clientapi.cpp
@@ -25,6 +25,7 @@
25#include <QEventLoop> 25#include <QEventLoop>
26#include <QAbstractItemModel> 26#include <QAbstractItemModel>
27#include <QDir> 27#include <QDir>
28#include <QUuid>
28#include <functional> 29#include <functional>
29#include <memory> 30#include <memory>
30 31
@@ -289,10 +290,26 @@ KAsync::Job<QList<typename DomainType::Ptr> > Store::fetch(const Akonadi2::Query
289 }); 290 });
290} 291}
291 292
293template <class DomainType>
294KAsync::Job<void> Resources::inspect(const Inspection &inspectionCommand)
295{
296 auto resource = inspectionCommand.resourceIdentifier;
297
298 Trace() << "Sending inspection " << resource;
299 auto resourceAccess = QSharedPointer<Akonadi2::ResourceAccess>::create(resource);
300 resourceAccess->open();
301 auto id = QUuid::createUuid().toByteArray();
302 return resourceAccess->sendInspectionCommand(id, ApplicationDomain::getTypeName<DomainType>(), inspectionCommand.entityIdentifier, inspectionCommand.property, inspectionCommand.expectedValue)
303 .template then<void>([resourceAccess]() {
304 //TODO wait for inspection notification
305 });
306}
307
292#define REGISTER_TYPE(T) template KAsync::Job<void> Store::remove<T>(const T &domainObject); \ 308#define REGISTER_TYPE(T) template KAsync::Job<void> Store::remove<T>(const T &domainObject); \
293 template KAsync::Job<void> Store::create<T>(const T &domainObject); \ 309 template KAsync::Job<void> Store::create<T>(const T &domainObject); \
294 template KAsync::Job<void> Store::modify<T>(const T &domainObject); \ 310 template KAsync::Job<void> Store::modify<T>(const T &domainObject); \
295 template QSharedPointer<QAbstractItemModel> Store::loadModel<T>(Query query); \ 311 template QSharedPointer<QAbstractItemModel> Store::loadModel<T>(Query query); \
312 template KAsync::Job<void> Resources::inspect<T>(const Inspection &); \
296 template KAsync::Job<T> Store::fetchOne<T>(const Query &); \ 313 template KAsync::Job<T> Store::fetchOne<T>(const Query &); \
297 template KAsync::Job<QList<T::Ptr> > Store::fetchAll<T>(const Query &); \ 314 template KAsync::Job<QList<T::Ptr> > Store::fetchAll<T>(const Query &); \
298 template KAsync::Job<QList<T::Ptr> > Store::fetch<T>(const Query &, int); \ 315 template KAsync::Job<QList<T::Ptr> > Store::fetch<T>(const Query &, int); \
diff --git a/common/clientapi.h b/common/clientapi.h
index eff8e8d..d496715 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -111,6 +111,31 @@ public:
111 static KAsync::Job<QList<typename DomainType::Ptr> > fetch(const Akonadi2::Query &query, int minimumAmount = 0); 111 static KAsync::Job<QList<typename DomainType::Ptr> > fetch(const Akonadi2::Query &query, int minimumAmount = 0);
112}; 112};
113 113
114namespace Resources {
115 struct Inspection {
116 static Inspection PropertyInspection(const Akonadi2::ApplicationDomain::Entity &entity, const QByteArray &property, const QVariant &expectedValue)
117 {
118 Inspection inspection;
119 inspection.resourceIdentifier = entity.resourceInstanceIdentifier();
120 inspection.entityIdentifier = entity.identifier();
121 inspection.property = property;
122 inspection.expectedValue = expectedValue;
123 return inspection;
124 }
125
126 enum Type {
127 PropertyInspectionType
128 };
129
130 QByteArray resourceIdentifier;
131 QByteArray entityIdentifier;
132 QByteArray property;
133 QVariant expectedValue;
134 };
135 template <class DomainType>
136 KAsync::Job<void> inspect(const Inspection &inspectionCommand);
137}
138
114 139
115} 140}
116 141
diff --git a/common/commands.cpp b/common/commands.cpp
index 7a0ae23..16fd742 100644
--- a/common/commands.cpp
+++ b/common/commands.cpp
@@ -59,6 +59,8 @@ QByteArray name(int commandId)
59 return "Ping"; 59 return "Ping";
60 case RevisionReplayedCommand: 60 case RevisionReplayedCommand:
61 return "RevisionReplayed"; 61 return "RevisionReplayed";
62 case InspectionCommand:
63 return "Inspection";
62 case CustomCommand: 64 case CustomCommand:
63 return "Custom"; 65 return "Custom";
64 }; 66 };
diff --git a/common/commands.h b/common/commands.h
index c68ef90..bce278c 100644
--- a/common/commands.h
+++ b/common/commands.h
@@ -47,6 +47,7 @@ enum CommandIds {
47 NotificationCommand, 47 NotificationCommand,
48 PingCommand, 48 PingCommand,
49 RevisionReplayedCommand, 49 RevisionReplayedCommand,
50 InspectionCommand,
50 CustomCommand = 0xffff 51 CustomCommand = 0xffff
51}; 52};
52 53
diff --git a/common/commands/inspection.fbs b/common/commands/inspection.fbs
new file mode 100644
index 0000000..aaae1ae
--- /dev/null
+++ b/common/commands/inspection.fbs
@@ -0,0 +1,12 @@
1namespace Akonadi2.Commands;
2
3table Inspection {
4 id: string;
5 type: int;
6 entityId: string;
7 domainType: string;
8 property: string;
9 expectedValue: string;
10}
11
12root_type Inspection;
diff --git a/common/commands/notification.fbs b/common/commands/notification.fbs
index 6684472..eb00986 100644
--- a/common/commands/notification.fbs
+++ b/common/commands/notification.fbs
@@ -1,9 +1,12 @@
1namespace Akonadi2; 1namespace Akonadi2;
2 2
3enum NotificationType : byte { Shutdown = 1, Status, Warning, Progress } 3enum NotificationType : byte { Shutdown = 1, Status, Warning, Progress, Inspection }
4enum NotificationCode : byte { Success = 0, Failure = 1, UserCode }
4 5
5table Notification { 6table Notification {
6 type: NotificationType = Status; 7 type: NotificationType = Status;
8 message: string;
9 code: int = 0; //Of type NotificationCode
7} 10}
8 11
9root_type Notification; 12root_type Notification;
diff --git a/common/genericresource.cpp b/common/genericresource.cpp
index 29acce4..90fc763 100644
--- a/common/genericresource.cpp
+++ b/common/genericresource.cpp
@@ -6,6 +6,7 @@
6#include "createentity_generated.h" 6#include "createentity_generated.h"
7#include "modifyentity_generated.h" 7#include "modifyentity_generated.h"
8#include "deleteentity_generated.h" 8#include "deleteentity_generated.h"
9#include "inspection_generated.h"
9#include "domainadaptor.h" 10#include "domainadaptor.h"
10#include "commands.h" 11#include "commands.h"
11#include "index.h" 12#include "index.h"
@@ -13,6 +14,7 @@
13#include "definitions.h" 14#include "definitions.h"
14 15
15#include <QUuid> 16#include <QUuid>
17#include <QDataStream>
16 18
17static int sBatchSize = 100; 19static int sBatchSize = 100;
18 20
@@ -112,6 +114,7 @@ private:
112class CommandProcessor : public QObject 114class CommandProcessor : public QObject
113{ 115{
114 Q_OBJECT 116 Q_OBJECT
117 typedef std::function<KAsync::Job<void>(void const *, size_t)> InspectionFunction;
115public: 118public:
116 CommandProcessor(Akonadi2::Pipeline *pipeline, QList<MessageQueue*> commandQueues) 119 CommandProcessor(Akonadi2::Pipeline *pipeline, QList<MessageQueue*> commandQueues)
117 : QObject(), 120 : QObject(),
@@ -135,6 +138,11 @@ public:
135 mLowerBoundRevision = revision; 138 mLowerBoundRevision = revision;
136 } 139 }
137 140
141 void setInspectionCommand(const InspectionFunction &f)
142 {
143 mInspect = f;
144 }
145
138 146
139signals: 147signals:
140 void error(int errorCode, const QString &errorMessage); 148 void error(int errorCode, const QString &errorMessage);
@@ -176,6 +184,14 @@ private slots:
176 return mPipeline->modifiedEntity(queuedCommand->command()->Data(), queuedCommand->command()->size()); 184 return mPipeline->modifiedEntity(queuedCommand->command()->Data(), queuedCommand->command()->size());
177 case Akonadi2::Commands::CreateEntityCommand: 185 case Akonadi2::Commands::CreateEntityCommand:
178 return mPipeline->newEntity(queuedCommand->command()->Data(), queuedCommand->command()->size()); 186 return mPipeline->newEntity(queuedCommand->command()->Data(), queuedCommand->command()->size());
187 case Akonadi2::Commands::InspectionCommand:
188 if (mInspect) {
189 return mInspect(queuedCommand->command()->Data(), queuedCommand->command()->size()).then<qint64>([]() {
190 return -1;
191 });
192 } else {
193 return KAsync::error<qint64>(-1, "Missing inspection command.");
194 }
179 default: 195 default:
180 return KAsync::error<qint64>(-1, "Unhandled command"); 196 return KAsync::error<qint64>(-1, "Unhandled command");
181 } 197 }
@@ -266,6 +282,7 @@ private:
266 bool mProcessingLock; 282 bool mProcessingLock;
267 //The lowest revision we no longer need 283 //The lowest revision we no longer need
268 qint64 mLowerBoundRevision; 284 qint64 mLowerBoundRevision;
285 InspectionFunction mInspect;
269}; 286};
270 287
271 288
@@ -279,6 +296,22 @@ GenericResource::GenericResource(const QByteArray &resourceInstanceIdentifier, c
279 mClientLowerBoundRevision(std::numeric_limits<qint64>::max()) 296 mClientLowerBoundRevision(std::numeric_limits<qint64>::max())
280{ 297{
281 mProcessor = new CommandProcessor(mPipeline.data(), QList<MessageQueue*>() << &mUserQueue << &mSynchronizerQueue); 298 mProcessor = new CommandProcessor(mPipeline.data(), QList<MessageQueue*>() << &mUserQueue << &mSynchronizerQueue);
299 mProcessor->setInspectionCommand([this](void const *command, size_t size) {
300 flatbuffers::Verifier verifier((const uint8_t *)command, size);
301 if (Akonadi2::Commands::VerifyInspectionBuffer(verifier)) {
302 auto buffer = Akonadi2::Commands::GetInspection(command);
303 int inspectionType = buffer->type();
304 QByteArray entityId = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer->entityId()->Data()), buffer->entityId()->size());
305 QByteArray domainType = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer->domainType()->Data()), buffer->domainType()->size());
306 QByteArray property = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer->property()->Data()), buffer->property()->size());
307 QByteArray expectedValueString = QByteArray::fromRawData(reinterpret_cast<const char *>(buffer->expectedValue()->Data()), buffer->expectedValue()->size());
308 QDataStream s(expectedValueString);
309 QVariant expectedValue;
310 s >> expectedValue;
311 return inspect(inspectionType, domainType, entityId, property, expectedValue);
312 }
313 return KAsync::error<void>(-1, "Invalid inspection command.");
314 });
282 QObject::connect(mProcessor, &CommandProcessor::error, [this](int errorCode, const QString &msg) { onProcessorError(errorCode, msg); }); 315 QObject::connect(mProcessor, &CommandProcessor::error, [this](int errorCode, const QString &msg) { onProcessorError(errorCode, msg); });
283 QObject::connect(mPipeline.data(), &Pipeline::revisionUpdated, this, &Resource::revisionUpdated); 316 QObject::connect(mPipeline.data(), &Pipeline::revisionUpdated, this, &Resource::revisionUpdated);
284 mSourceChangeReplay = new ChangeReplay(resourceInstanceIdentifier, [this](const QByteArray &type, const QByteArray &key, const QByteArray &value) { 317 mSourceChangeReplay = new ChangeReplay(resourceInstanceIdentifier, [this](const QByteArray &type, const QByteArray &key, const QByteArray &value) {
@@ -301,6 +334,12 @@ GenericResource::~GenericResource()
301 delete mSourceChangeReplay; 334 delete mSourceChangeReplay;
302} 335}
303 336
337KAsync::Job<void> GenericResource::inspect(int inspectionType, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue)
338{
339 Warning() << "Inspection not implemented";
340 return KAsync::null<void>();
341}
342
304void GenericResource::enableChangeReplay(bool enable) 343void GenericResource::enableChangeReplay(bool enable)
305{ 344{
306 if (enable) { 345 if (enable) {
diff --git a/common/genericresource.h b/common/genericresource.h
index f47c6f8..90b7c29 100644
--- a/common/genericresource.h
+++ b/common/genericresource.h
@@ -48,6 +48,7 @@ public:
48 virtual KAsync::Job<void> synchronizeWithSource(Akonadi2::Storage &mainStore, Akonadi2::Storage &synchronizationStore); 48 virtual KAsync::Job<void> synchronizeWithSource(Akonadi2::Storage &mainStore, Akonadi2::Storage &synchronizationStore);
49 virtual KAsync::Job<void> processAllMessages() Q_DECL_OVERRIDE; 49 virtual KAsync::Job<void> processAllMessages() Q_DECL_OVERRIDE;
50 virtual void setLowerBoundRevision(qint64 revision) Q_DECL_OVERRIDE; 50 virtual void setLowerBoundRevision(qint64 revision) Q_DECL_OVERRIDE;
51 virtual KAsync::Job<void> inspect(int inspectionType, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue);
51 52
52 int error() const; 53 int error() const;
53 54
diff --git a/common/listener.cpp b/common/listener.cpp
index 518aca9..5d55202 100644
--- a/common/listener.cpp
+++ b/common/listener.cpp
@@ -31,11 +31,13 @@
31#include "common/synchronize_generated.h" 31#include "common/synchronize_generated.h"
32#include "common/notification_generated.h" 32#include "common/notification_generated.h"
33#include "common/revisionreplayed_generated.h" 33#include "common/revisionreplayed_generated.h"
34#include "common/inspection_generated.h"
34 35
35#include <QLocalServer> 36#include <QLocalServer>
36#include <QLocalSocket> 37#include <QLocalSocket>
37#include <QTimer> 38#include <QTimer>
38#include <QTime> 39#include <QTime>
40#include <QDataStream>
39 41
40Listener::Listener(const QByteArray &resourceInstanceIdentifier, QObject *parent) 42Listener::Listener(const QByteArray &resourceInstanceIdentifier, QObject *parent)
41 : QObject(parent), 43 : QObject(parent),
@@ -241,6 +243,7 @@ void Listener::processCommand(int commandId, uint messageId, const QByteArray &c
241 } 243 }
242 break; 244 break;
243 } 245 }
246 case Akonadi2::Commands::InspectionCommand:
244 case Akonadi2::Commands::FetchEntityCommand: 247 case Akonadi2::Commands::FetchEntityCommand:
245 case Akonadi2::Commands::DeleteEntityCommand: 248 case Akonadi2::Commands::DeleteEntityCommand:
246 case Akonadi2::Commands::ModifyEntityCommand: 249 case Akonadi2::Commands::ModifyEntityCommand:
diff --git a/common/notification.h b/common/notification.h
new file mode 100644
index 0000000..8ffc24c
--- /dev/null
+++ b/common/notification.h
@@ -0,0 +1,40 @@
1/*
2 * Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
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#pragma once
21
22#include <akonadi2common_export.h>
23#include <QString>
24#include "notification_generated.h"
25
26namespace Akonadi2
27{
28
29/**
30 * A notification
31 */
32class AKONADI2COMMON_EXPORT ResourceNotification
33{
34public:
35 int type;
36 QString message;
37 int code;
38};
39
40}
diff --git a/common/resource.h b/common/resource.h
index 4ed21b5..9a31d03 100644
--- a/common/resource.h
+++ b/common/resource.h
@@ -21,6 +21,7 @@
21#include <akonadi2common_export.h> 21#include <akonadi2common_export.h>
22 22
23#include <Async/Async> 23#include <Async/Async>
24#include "notification.h"
24 25
25namespace Akonadi2 26namespace Akonadi2
26{ 27{
@@ -55,6 +56,7 @@ public:
55 56
56Q_SIGNALS: 57Q_SIGNALS:
57 void revisionUpdated(qint64); 58 void revisionUpdated(qint64);
59 void notify(ResourceNotification);
58 60
59private: 61private:
60 class Private; 62 class Private;
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
index 7be1259..65e9a8c 100644
--- a/common/resourceaccess.cpp
+++ b/common/resourceaccess.cpp
@@ -30,6 +30,7 @@
30#include "common/modifyentity_generated.h" 30#include "common/modifyentity_generated.h"
31#include "common/deleteentity_generated.h" 31#include "common/deleteentity_generated.h"
32#include "common/revisionreplayed_generated.h" 32#include "common/revisionreplayed_generated.h"
33#include "common/inspection_generated.h"
33#include "common/entitybuffer.h" 34#include "common/entitybuffer.h"
34#include "log.h" 35#include "log.h"
35 36
@@ -37,6 +38,8 @@
37#include <QDebug> 38#include <QDebug>
38#include <QDir> 39#include <QDir>
39#include <QProcess> 40#include <QProcess>
41#include <QDataStream>
42#include <QBuffer>
40 43
41#undef Trace 44#undef Trace
42#define Trace() Akonadi2::Log::debugStream(Akonadi2::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, "ResourceAccess") 45#define Trace() Akonadi2::Log::debugStream(Akonadi2::Log::DebugLevel::Trace, __LINE__, __FILE__, Q_FUNC_INFO, "ResourceAccess")
@@ -338,6 +341,25 @@ KAsync::Job<void> ResourceAccess::sendRevisionReplayedCommand(qint64 revision)
338 return sendCommand(Akonadi2::Commands::RevisionReplayedCommand, fbb); 341 return sendCommand(Akonadi2::Commands::RevisionReplayedCommand, fbb);
339} 342}
340 343
344KAsync::Job<void> ResourceAccess::sendInspectionCommand(const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue)
345{
346 flatbuffers::FlatBufferBuilder fbb;
347 auto id = fbb.CreateString(inspectionId.toStdString());
348 auto domain = fbb.CreateString(domainType.toStdString());
349 auto entity = fbb.CreateString(entityId.toStdString());
350 auto prop = fbb.CreateString(property.toStdString());
351
352 QByteArray array;
353 QDataStream s(&array, QIODevice::WriteOnly);
354 s << expectedValue;
355
356 auto expected = fbb.CreateString(array.toStdString());
357 auto location = Akonadi2::Commands::CreateInspection (fbb, id, 0, entity, domain, prop, expected);
358 Akonadi2::Commands::FinishInspectionBuffer(fbb, location);
359 open();
360 return sendCommand(Akonadi2::Commands::InspectionCommand, fbb);
361}
362
341void ResourceAccess::open() 363void ResourceAccess::open()
342{ 364{
343 if (d->socket && d->socket->isValid()) { 365 if (d->socket && d->socket->isValid()) {
diff --git a/common/resourceaccess.h b/common/resourceaccess.h
index 7f61b30..fe3fa99 100644
--- a/common/resourceaccess.h
+++ b/common/resourceaccess.h
@@ -27,6 +27,7 @@
27#include <Async/Async> 27#include <Async/Async>
28 28
29#include <flatbuffers/flatbuffers.h> 29#include <flatbuffers/flatbuffers.h>
30#include "notification.h"
30 31
31namespace Akonadi2 32namespace Akonadi2
32{ 33{
@@ -49,10 +50,12 @@ public:
49 virtual KAsync::Job<void> sendModifyCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType, const QByteArrayList &deletedProperties, const QByteArray &buffer) { return KAsync::null<void>(); }; 50 virtual KAsync::Job<void> sendModifyCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType, const QByteArrayList &deletedProperties, const QByteArray &buffer) { return KAsync::null<void>(); };
50 virtual KAsync::Job<void> sendDeleteCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType) { return KAsync::null<void>(); }; 51 virtual KAsync::Job<void> sendDeleteCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType) { return KAsync::null<void>(); };
51 virtual KAsync::Job<void> sendRevisionReplayedCommand(qint64 revision) {return KAsync::null<void>(); }; 52 virtual KAsync::Job<void> sendRevisionReplayedCommand(qint64 revision) {return KAsync::null<void>(); };
53 virtual KAsync::Job<void> sendInspectionCommand(const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expecedValue) {return KAsync::null<void>(); };
52 54
53Q_SIGNALS: 55Q_SIGNALS:
54 void ready(bool isReady); 56 void ready(bool isReady);
55 void revisionChanged(qint64 revision); 57 void revisionChanged(qint64 revision);
58 void notification(ResourceNotification revision);
56 59
57public Q_SLOTS: 60public Q_SLOTS:
58 virtual void open() = 0; 61 virtual void open() = 0;
@@ -78,6 +81,7 @@ public:
78 KAsync::Job<void> sendModifyCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType, const QByteArrayList &deletedProperties, const QByteArray &buffer) Q_DECL_OVERRIDE; 81 KAsync::Job<void> sendModifyCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType, const QByteArrayList &deletedProperties, const QByteArray &buffer) Q_DECL_OVERRIDE;
79 KAsync::Job<void> sendDeleteCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType) Q_DECL_OVERRIDE; 82 KAsync::Job<void> sendDeleteCommand(const QByteArray &uid, qint64 revision, const QByteArray &resourceBufferType) Q_DECL_OVERRIDE;
80 KAsync::Job<void> sendRevisionReplayedCommand(qint64 revision) Q_DECL_OVERRIDE; 83 KAsync::Job<void> sendRevisionReplayedCommand(qint64 revision) Q_DECL_OVERRIDE;
84 KAsync::Job<void> sendInspectionCommand(const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expecedValue) Q_DECL_OVERRIDE;
81 /** 85 /**
82 * Tries to connect to server, and returns a connected socket on success. 86 * Tries to connect to server, and returns a connected socket on success.
83 */ 87 */
diff --git a/docs/resource.md b/docs/resource.md
index c8f58e9..0988535 100644
--- a/docs/resource.md
+++ b/docs/resource.md
@@ -141,3 +141,10 @@ The remoteid mapping has to be updated in two places:
141 141
142* New entities that are synchronized immediately get a localid assinged, that is then recorded together with the remoteid. This is required to be able to reference other entities directly in the command queue (i.e. for parent folders). 142* New entities that are synchronized immediately get a localid assinged, that is then recorded together with the remoteid. This is required to be able to reference other entities directly in the command queue (i.e. for parent folders).
143* Entities created by clients get a remoteid assigned during change replay, so the entity can be recognized during the next sync. 143* Entities created by clients get a remoteid assigned during change replay, so the entity can be recognized during the next sync.
144
145# Testing / Inspection
146Resources new to be tested, which often requires inspections into the current state of the resource. This is difficult in an asynchronous system where the whole backend logic is encapsulated in a separate process without running tests in a vastly different setup from how it will be run in production.
147
148To alleviate this inspection commands are introduced. Inspection commands are special commands that the resource processes just like all other commands, and that have the sole purpose of inspecting the current resource state. Because the command is processed with the same mechanism as other commands we can rely on ordering of commands in a way that a prior command is guaranteed to be executed once the inspection command is processed.
149
150A typical inspection command could i.e. verify that a file has been created in the expected path after a create command.
diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp
index a984097..27d5f17 100644
--- a/examples/dummyresource/resourcefactory.cpp
+++ b/examples/dummyresource/resourcefactory.cpp
@@ -134,6 +134,26 @@ void DummyResource::removeFromDisk(const QByteArray &instanceIdentifier)
134 Akonadi2::Storage(Akonadi2::storageLocation(), instanceIdentifier + ".synchronization", Akonadi2::Storage::ReadWrite).removeFromDisk(); 134 Akonadi2::Storage(Akonadi2::storageLocation(), instanceIdentifier + ".synchronization", Akonadi2::Storage::ReadWrite).removeFromDisk();
135} 135}
136 136
137KAsync::Job<void> DummyResource::inspect(int inspectionType, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue)
138{
139
140 Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue;
141 if (property == "testInspection") {
142 Akonadi2::ResourceNotification n;
143 n.type = Akonadi2::NotificationType_Inspection;
144 if (expectedValue.toBool()) {
145 //Success
146 n.code = 0;
147 emit notify(n);
148 } else {
149 //Failure
150 n.code = 1;
151 emit notify(n);
152 }
153 }
154 return KAsync::null<void>();
155}
156
137DummyResourceFactory::DummyResourceFactory(QObject *parent) 157DummyResourceFactory::DummyResourceFactory(QObject *parent)
138 : Akonadi2::ResourceFactory(parent) 158 : Akonadi2::ResourceFactory(parent)
139{ 159{
diff --git a/examples/dummyresource/resourcefactory.h b/examples/dummyresource/resourcefactory.h
index 2ed4c5d..3f67187 100644
--- a/examples/dummyresource/resourcefactory.h
+++ b/examples/dummyresource/resourcefactory.h
@@ -40,6 +40,7 @@ public:
40 KAsync::Job<void> synchronizeWithSource(Akonadi2::Storage &mainStore, Akonadi2::Storage &synchronizationStore) Q_DECL_OVERRIDE; 40 KAsync::Job<void> synchronizeWithSource(Akonadi2::Storage &mainStore, Akonadi2::Storage &synchronizationStore) Q_DECL_OVERRIDE;
41 using GenericResource::synchronizeWithSource; 41 using GenericResource::synchronizeWithSource;
42 static void removeFromDisk(const QByteArray &instanceIdentifier); 42 static void removeFromDisk(const QByteArray &instanceIdentifier);
43 KAsync::Job<void> inspect(int inspectionType, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE;
43private: 44private:
44 KAsync::Job<void> replay(Akonadi2::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value) Q_DECL_OVERRIDE; 45 KAsync::Job<void> replay(Akonadi2::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value) Q_DECL_OVERRIDE;
45 Akonadi2::ApplicationDomain::Event::Ptr createEvent(const QByteArray &rid, const QMap<QString, QVariant> &data, Akonadi2::Storage::Transaction &); 46 Akonadi2::ApplicationDomain::Event::Ptr createEvent(const QByteArray &rid, const QMap<QString, QVariant> &data, Akonadi2::Storage::Transaction &);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1e0f6b5..38e5512 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -51,12 +51,14 @@ auto_tests (
51 databasepopulationandfacadequerybenchmark 51 databasepopulationandfacadequerybenchmark
52 dummyresourcewritebenchmark 52 dummyresourcewritebenchmark
53 modelinteractivitytest 53 modelinteractivitytest
54 inspectiontest
54) 55)
55target_link_libraries(dummyresourcetest akonadi2_resource_dummy) 56target_link_libraries(dummyresourcetest akonadi2_resource_dummy)
56target_link_libraries(dummyresourcebenchmark akonadi2_resource_dummy) 57target_link_libraries(dummyresourcebenchmark akonadi2_resource_dummy)
57target_link_libraries(dummyresourcewritebenchmark akonadi2_resource_dummy) 58target_link_libraries(dummyresourcewritebenchmark akonadi2_resource_dummy)
58target_link_libraries(querytest akonadi2_resource_dummy) 59target_link_libraries(querytest akonadi2_resource_dummy)
59target_link_libraries(modelinteractivitytest akonadi2_resource_dummy) 60target_link_libraries(modelinteractivitytest akonadi2_resource_dummy)
61target_link_libraries(inspectiontest akonadi2_resource_dummy)
60 62
61if (BUILD_MAILDIR) 63if (BUILD_MAILDIR)
62 auto_tests ( 64 auto_tests (
diff --git a/tests/inspectiontest.cpp b/tests/inspectiontest.cpp
new file mode 100644
index 0000000..e332844
--- /dev/null
+++ b/tests/inspectiontest.cpp
@@ -0,0 +1,64 @@
1#include <QtTest>
2
3#include <QString>
4
5#include "dummyresource/resourcefactory.h"
6#include "clientapi.h"
7#include "commands.h"
8#include "entitybuffer.h"
9#include "resourceconfig.h"
10#include "modelresult.h"
11#include "pipeline.h"
12#include "log.h"
13
14/**
15 * Test of inspection system using the dummy resource.
16 *
17 * This test requires the dummy resource installed.
18 */
19class InspectionTest : public QObject
20{
21 Q_OBJECT
22private Q_SLOTS:
23 void initTestCase()
24 {
25 Akonadi2::Log::setDebugOutputLevel(Akonadi2::Log::Trace);
26 auto factory = Akonadi2::ResourceFactory::load("org.kde.dummy");
27 QVERIFY(factory);
28 DummyResource::removeFromDisk("org.kde.dummy.instance1");
29 ResourceConfig::addResource("org.kde.dummy.instance1", "org.kde.dummy");
30 }
31
32 void cleanup()
33 {
34 Akonadi2::Store::shutdown(QByteArray("org.kde.dummy.instance1")).exec().waitForFinished();
35 DummyResource::removeFromDisk("org.kde.dummy.instance1");
36 auto factory = Akonadi2::ResourceFactory::load("org.kde.dummy");
37 QVERIFY(factory);
38 Akonadi2::Store::start(QByteArray("org.kde.dummy.instance1")).exec().waitForFinished();
39 }
40
41 void init()
42 {
43 qDebug();
44 qDebug() << "-----------------------------------------";
45 qDebug();
46 }
47
48 void testMarkMailAsRead()
49 {
50 using namespace Akonadi2;
51 using namespace Akonadi2::ApplicationDomain;
52
53 Mail mail(QByteArray("org.kde.dummy.instance1"), QByteArray("identifier"), 0, QSharedPointer<MemoryBufferAdaptor::MemoryBufferAdaptor>::create());
54
55 auto inspectionCommand = Resources::Inspection::PropertyInspection(mail, "unread", true);
56 auto result = Resources::inspect<Mail>(inspectionCommand).exec();
57 result.waitForFinished();
58 QVERIFY(!result.errorCode());
59 Akonadi2::Store::flushMessageQueue(QByteArrayList() << QByteArray("org.kde.dummy.instance1")).exec().waitForFinished();
60 }
61};
62
63QTEST_MAIN(InspectionTest)
64#include "inspectiontest.moc"