Commit 5535d6e5 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

Generate Protocol from an XML specification (ABI break)

Instead of maintaining 12k lines of hand-written protocol code, we
specify the protocol in an XML and use a custom-written generator
that generates the code for us.

It's not only much easier to modify the protocol - we only need to
change a single thing in the XML instead of touching several places
of the implementation - but it's also much safer, as there's less
risk of accidentally introducing a bug in the code.

The major difference between the original hand-written code and the
generated code is that we no longer use QSharedDataPointer and virtual
methods in the Private classes, but instead all members are directly
in the command clas with most getters and setters inlined. This means
that copying commands is quite costly, so we pass them around as
QSharedPointers or const references. This should give us a tiny little
bit more performance.
parent 5da66a91
...@@ -24,8 +24,8 @@ macro(add_akonadi_isolated_test_advanced _source _additionalsources _linklibrari ...@@ -24,8 +24,8 @@ macro(add_akonadi_isolated_test_advanced _source _additionalsources _linklibrari
add_executable( ${_name} ${_test} ${_additionalsources}) add_executable( ${_name} ${_test} ${_additionalsources})
ecm_mark_as_test(${_name}) ecm_mark_as_test(${_name})
target_link_libraries(${_name} target_link_libraries(${_name}
Qt5::Test Qt5::Gui Qt5::Widgets Qt5::Network KF5::KIOCore KF5::AkonadiCore KF5::DBusAddons Qt5::Test Qt5::Gui Qt5::Widgets Qt5::Network KF5::KIOCore KF5::AkonadiCore
${_linklibraries}) KF5::AkonadiPrivate KF5::DBusAddons ${_linklibraries})
if (NOT DEFINED _testrunner) if (NOT DEFINED _testrunner)
if (${PROJECT_NAME} STREQUAL Akonadi AND TARGET akonaditest) if (${PROJECT_NAME} STREQUAL Akonadi AND TARGET akonaditest)
......
...@@ -133,7 +133,7 @@ public: ...@@ -133,7 +133,7 @@ public:
virtual ~FakeNotificationConnection() virtual ~FakeNotificationConnection()
{} {}
void emitNotify(const Akonadi::Protocol::ChangeNotification &ntf) void emitNotify(const Akonadi::Protocol::ChangeNotificationPtr &ntf)
{ {
Q_EMIT commandReceived(3, ntf); Q_EMIT commandReceived(3, ntf);
} }
......
...@@ -38,7 +38,7 @@ public: ...@@ -38,7 +38,7 @@ public:
{ {
} }
bool emitNotification(const Akonadi::Protocol::ChangeNotification &msg) Q_DECL_OVERRIDE { bool emitNotification(const Akonadi::Protocol::ChangeNotificationPtr &msg) Q_DECL_OVERRIDE {
// TODO: Check/Log // TODO: Check/Log
return Akonadi::ChangeRecorderPrivate::emitNotification(msg); return Akonadi::ChangeRecorderPrivate::emitNotification(msg);
} }
...@@ -55,11 +55,11 @@ public: ...@@ -55,11 +55,11 @@ public:
return qobject_cast<FakeNotificationConnection *>(d_ptr->ntfConnection); return qobject_cast<FakeNotificationConnection *>(d_ptr->ntfConnection);
} }
QQueue<Akonadi::Protocol::ChangeNotification> pendingNotifications() const QQueue<Akonadi::Protocol::ChangeNotificationPtr> pendingNotifications() const
{ {
return d_ptr->pendingNotifications; return d_ptr->pendingNotifications;
} }
QQueue<Akonadi::Protocol::ChangeNotification> pipeline() const QQueue<Akonadi::Protocol::ChangeNotificationPtr> pipeline() const
{ {
return d_ptr->pipeline; return d_ptr->pipeline;
} }
......
...@@ -38,7 +38,7 @@ public: ...@@ -38,7 +38,7 @@ public:
{ {
} }
bool emitNotification(const Akonadi::Protocol::ChangeNotification &msg) Q_DECL_OVERRIDE { bool emitNotification(const Akonadi::Protocol::ChangeNotificationPtr &msg) Q_DECL_OVERRIDE {
// TODO: Check/Log // TODO: Check/Log
return Akonadi::MonitorPrivate::emitNotification(msg); return Akonadi::MonitorPrivate::emitNotification(msg);
} }
...@@ -55,11 +55,11 @@ public: ...@@ -55,11 +55,11 @@ public:
return qobject_cast<FakeNotificationConnection *>(d_ptr->ntfConnection); return qobject_cast<FakeNotificationConnection *>(d_ptr->ntfConnection);
} }
QQueue<Akonadi::Protocol::ChangeNotification> pendingNotifications() const QQueue<Akonadi::Protocol::ChangeNotificationPtr> pendingNotifications() const
{ {
return d_ptr->pendingNotifications; return d_ptr->pendingNotifications;
} }
QQueue<Akonadi::Protocol::ChangeNotification> pipeline() const QQueue<Akonadi::Protocol::ChangeNotificationPtr> pipeline() const
{ {
return d_ptr->pipeline; return d_ptr->pipeline;
} }
......
...@@ -349,6 +349,7 @@ void ItemAppendTest::testItemMerge() ...@@ -349,6 +349,7 @@ void ItemAppendTest::testItemMerge()
QCOMPARE(merge->item().remoteRevision(), mergedItem.remoteRevision()); QCOMPARE(merge->item().remoteRevision(), mergedItem.remoteRevision());
QCOMPARE(merge->item().payloadData(), mergedItem.payloadData()); QCOMPARE(merge->item().payloadData(), mergedItem.payloadData());
QCOMPARE(merge->item().size(), mergedItem.size()); QCOMPARE(merge->item().size(), mergedItem.size());
qDebug() << merge->item().flags() << mergedItem.flags();
QCOMPARE(merge->item().flags(), mergedItem.flags()); QCOMPARE(merge->item().flags(), mergedItem.flags());
} }
......
...@@ -99,15 +99,15 @@ void MonitorNotificationTest::testSingleMessage_impl(MonitorImpl *monitor, FakeC ...@@ -99,15 +99,15 @@ void MonitorNotificationTest::testSingleMessage_impl(MonitorImpl *monitor, FakeC
monitor->setSession(m_fakeSession); monitor->setSession(m_fakeSession);
monitor->fetchCollection(true); monitor->fetchCollection(true);
Protocol::ChangeNotification::List list; Protocol::ChangeNotificationList list;
Collection parent(1); Collection parent(1);
Collection added(2); Collection added(2);
Protocol::CollectionChangeNotification msg; auto msg = Protocol::CollectionChangeNotificationPtr::create();
msg.setParentCollection(parent.id()); msg->setParentCollection(parent.id());
msg.setOperation(Protocol::CollectionChangeNotification::Add); msg->setOperation(Protocol::CollectionChangeNotification::Add);
msg.setId(added.id()); msg->setId(added.id());
QHash<Collection::Id, Collection> data; QHash<Collection::Id, Collection> data;
data.insert(parent.id(), parent); data.insert(parent.id(), parent);
...@@ -163,7 +163,7 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo ...@@ -163,7 +163,7 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo
monitor->setSession(m_fakeSession); monitor->setSession(m_fakeSession);
monitor->fetchCollection(true); monitor->fetchCollection(true);
Protocol::ChangeNotification::List list; Protocol::ChangeNotificationList list;
QHash<Collection::Id, Collection> data; QHash<Collection::Id, Collection> data;
int i = 1; int i = 1;
...@@ -171,10 +171,10 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo ...@@ -171,10 +171,10 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo
Collection parent(i++); Collection parent(i++);
Collection added(i++); Collection added(i++);
Protocol::CollectionChangeNotification msg; auto msg = Protocol::CollectionChangeNotificationPtr::create();
msg.setParentCollection(parent.id()); msg->setParentCollection(parent.id());
msg.setOperation(Protocol::CollectionChangeNotification::Add); msg->setOperation(Protocol::CollectionChangeNotification::Add);
msg.setId(added.id()); msg->setId(added.id());
data.insert(parent.id(), parent); data.insert(parent.id(), parent);
data.insert(added.id(), added); data.insert(added.id(), added);
...@@ -185,7 +185,7 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo ...@@ -185,7 +185,7 @@ void MonitorNotificationTest::testFillPipeline_impl(MonitorImpl *monitor, FakeCo
QVERIFY(monitor->pipeline().isEmpty()); QVERIFY(monitor->pipeline().isEmpty());
QVERIFY(monitor->pendingNotifications().isEmpty()); QVERIFY(monitor->pendingNotifications().isEmpty());
Q_FOREACH (const Protocol::ChangeNotification &ntf, list) { Q_FOREACH (const Protocol::ChangeNotificationPtr &ntf, list) {
monitor->notificationConnection()->emitNotify(ntf); monitor->notificationConnection()->emitNotify(ntf);
} }
...@@ -232,7 +232,7 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect ...@@ -232,7 +232,7 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect
monitor->setSession(m_fakeSession); monitor->setSession(m_fakeSession);
monitor->fetchCollection(true); monitor->fetchCollection(true);
Protocol::ChangeNotification::List list; Protocol::ChangeNotificationList list;
Collection col2(2); Collection col2(2);
col2.setParentCollection(Collection::root()); col2.setParentCollection(Collection::root());
...@@ -244,10 +244,10 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect ...@@ -244,10 +244,10 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect
while (i < 8) { while (i < 8) {
Collection added(i++); Collection added(i++);
Protocol::CollectionChangeNotification msg; auto msg = Protocol::CollectionChangeNotificationPtr::create();
msg.setParentCollection(i % 2 ? 2 : added.id() - 1); msg->setParentCollection(i % 2 ? 2 : added.id() - 1);
msg.setOperation(Protocol::CollectionChangeNotification::Add); msg->setOperation(Protocol::CollectionChangeNotification::Add);
msg.setId(added.id()); msg->setId(added.id());
list << msg; list << msg;
} }
...@@ -271,7 +271,7 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect ...@@ -271,7 +271,7 @@ void MonitorNotificationTest::testMonitor_impl(MonitorImpl *monitor, FakeCollect
QVERIFY(monitor->pipeline().isEmpty()); QVERIFY(monitor->pipeline().isEmpty());
QVERIFY(monitor->pendingNotifications().isEmpty()); QVERIFY(monitor->pendingNotifications().isEmpty());
Q_FOREACH (const Protocol::ChangeNotification &ntf, list) { Q_FOREACH (const Protocol::ChangeNotificationPtr &ntf, list) {
monitor->notificationConnection()->emitNotify(ntf); monitor->notificationConnection()->emitNotify(ntf);
} }
......
...@@ -24,9 +24,7 @@ using namespace Akonadi; ...@@ -24,9 +24,7 @@ using namespace Akonadi;
Q_DECLARE_METATYPE(Scope) Q_DECLARE_METATYPE(Scope)
Q_DECLARE_METATYPE(QVector<Protocol::Ancestor>) Q_DECLARE_METATYPE(QVector<Protocol::Ancestor>)
Q_DECLARE_METATYPE(Protocol::FetchCollectionsResponse)
Q_DECLARE_METATYPE(Protocol::FetchScope) Q_DECLARE_METATYPE(Protocol::FetchScope)
Q_DECLARE_METATYPE(Protocol::FetchTagsResponse)
class ProtocolHelperTest : public QObject class ProtocolHelperTest : public QObject
{ {
...@@ -257,7 +255,7 @@ private Q_SLOTS: ...@@ -257,7 +255,7 @@ private Q_SLOTS:
Protocol::FetchScope::RemoteRevision | Protocol::FetchScope::RemoteRevision |
Protocol::FetchScope::MTime | Protocol::FetchScope::MTime |
Protocol::FetchScope::IgnoreErrors); Protocol::FetchScope::IgnoreErrors);
fs.setAncestorDepth(Akonadi::Protocol::Ancestor::AllAncestors); fs.setAncestorDepth(Protocol::FetchScope::AllAncestors);
QTest::newRow("full") << scope << fs; QTest::newRow("full") << scope << fs;
} }
......
...@@ -30,64 +30,73 @@ using namespace Akonadi::Protocol; ...@@ -30,64 +30,73 @@ using namespace Akonadi::Protocol;
void NotificationMessageTest::testCompress() void NotificationMessageTest::testCompress()
{ {
ChangeNotification::List list; ChangeNotificationList list;
CollectionChangeNotification msg; CollectionChangeNotification msg;
msg.setOperation(CollectionChangeNotification::Add); msg.setOperation(CollectionChangeNotification::Add);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
msg.setOperation(CollectionChangeNotification::Modify); msg.setOperation(CollectionChangeNotification::Modify);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(!CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
QCOMPARE(static_cast<CollectionChangeNotification&>(list.first()).operation(), CollectionChangeNotification::Add); QCOMPARE(list.first().staticCast<CollectionChangeNotification>()->operation(), CollectionChangeNotification::Add);
msg.setOperation(CollectionChangeNotification::Remove); msg.setOperation(CollectionChangeNotification::Remove);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 2); QCOMPARE(list.count(), 2);
} }
void NotificationMessageTest::testCompress2() void NotificationMessageTest::testCompress2()
{ {
ChangeNotification::List list; ChangeNotificationList list;
CollectionChangeNotification msg; CollectionChangeNotification msg;
msg.setOperation(CollectionChangeNotification::Modify); msg.setOperation(CollectionChangeNotification::Modify);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
msg.setOperation(CollectionChangeNotification::Remove); msg.setOperation(CollectionChangeNotification::Remove);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 2); QCOMPARE(list.count(), 2);
QCOMPARE(static_cast<CollectionChangeNotification&>(list.first()).operation(), CollectionChangeNotification::Modify); QCOMPARE(list.first().staticCast<CollectionChangeNotification>()->operation(), CollectionChangeNotification::Modify);
QCOMPARE(static_cast<CollectionChangeNotification&>(list.last()).operation(), CollectionChangeNotification::Remove); QCOMPARE(list.last().staticCast<CollectionChangeNotification>()->operation(), CollectionChangeNotification::Remove);
} }
void NotificationMessageTest::testCompress3() void NotificationMessageTest::testCompress3()
{ {
ChangeNotification::List list; ChangeNotificationList list;
CollectionChangeNotification msg; CollectionChangeNotification msg;
msg.setOperation(CollectionChangeNotification::Modify); msg.setOperation(CollectionChangeNotification::Modify);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(!CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
} }
void NotificationMessageTest::testPartModificationMerge() void NotificationMessageTest::testPartModificationMerge()
{ {
ChangeNotification::List list; ChangeNotificationList list;
CollectionChangeNotification msg; CollectionChangeNotification msg;
msg.setOperation(CollectionChangeNotification::Modify); msg.setOperation(CollectionChangeNotification::Modify);
msg.setChangedParts(QSet<QByteArray>() << "PART1"); msg.setChangedParts(QSet<QByteArray>() << "PART1");
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
msg.setChangedParts(QSet<QByteArray>() << "PART2"); msg.setChangedParts(QSet<QByteArray>() << "PART2");
CollectionChangeNotification::appendAndCompress(list, msg); QVERIFY(!CollectionChangeNotification::appendAndCompress(
list, CollectionChangeNotificationPtr::create(msg)));
QCOMPARE(list.count(), 1); QCOMPARE(list.count(), 1);
QCOMPARE(static_cast<CollectionChangeNotification&>(list.first()).changedParts(), (QSet<QByteArray>() << "PART1" << "PART2")); QCOMPARE(list.first().staticCast<CollectionChangeNotification>()->changedParts(), (QSet<QByteArray>() << "PART1" << "PART2"));
} }
This diff is collapsed.
...@@ -63,15 +63,15 @@ private Q_SLOTS: ...@@ -63,15 +63,15 @@ private Q_SLOTS:
private: private:
template<typename T> template<typename T>
typename std::enable_if<std::is_base_of<Akonadi::Protocol::Command, T>::value, T>::type typename std::enable_if<std::is_base_of<Akonadi::Protocol::Command, T>::value, QSharedPointer<T>>::type
serializeAndDeserialize(const T &in) serializeAndDeserialize(const QSharedPointer<T> &in)
{ {
QBuffer buf; QBuffer buf;
buf.open(QIODevice::ReadWrite); buf.open(QIODevice::ReadWrite);
Akonadi::Protocol::serialize(&buf, in); Akonadi::Protocol::serialize(&buf, in);
buf.seek(0); buf.seek(0);
return T(Akonadi::Protocol::deserialize(&buf)); return Akonadi::Protocol::deserialize(&buf).staticCast<T>();
} }
......
This diff is collapsed.
/*
Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "dbinitializer.h"
#include "storage/collectiontreecache.h"
#include "storage/selectquerybuilder.h"
#include "private/scope_p.h"
#include "aktest.h"
#include "fakeakonadiserver.h"
#include <QObject>
#include <QSignalSpy>
#include <QTest>
using namespace Akonadi;
using namespace Akonadi::Server;
class InspectableCollectionTreeCache : public CollectionTreeCache
{
Q_OBJECT
public:
InspectableCollectionTreeCache()
: CollectionTreeCache()
, mCachePopulated(0)
{}
bool waitForCachePopulated()
{
QSignalSpy spy(this, &InspectableCollectionTreeCache::cachePopulated);
return mCachePopulated == 1 || spy.wait(5000);
}
Q_SIGNALS:
void cachePopulated();
protected:
void init() Q_DECL_OVERRIDE
{
CollectionTreeCache::init();
mCachePopulated = 1;
Q_EMIT cachePopulated();
}
void quit() Q_DECL_OVERRIDE
{
}
private:
QAtomicInt mCachePopulated;
};
class CollectionTreeCacheTest : public QObject
{
Q_OBJECT
public:
CollectionTreeCacheTest()
{
try {
FakeAkonadiServer::instance()->init();
} catch (const FakeAkonadiServerException &e) {
qWarning() << "Server exception: " << e.what();
qFatal("Fake Akonadi Server failed to start up, aborting test");
}
}
~CollectionTreeCacheTest()
{
FakeAkonadiServer::instance()->quit();
}
private:
void populateDb(DbInitializer &db)
{
// ResA
// |- Col A1
// |- Col A2
// | |- Col A3
// | |- Col A7
// | |- Col A5
// | |- Col A8
// |- Col A6
// | |- Col A10
// |- Col A9
auto res = db.createResource("TestResource");
auto resA = db.createCollection("Res A", Collection());
auto colA1 = db.createCollection("Col A1", resA);
auto colA2 = db.createCollection("Col A2", resA);
auto colA3 = db.createCollection("Col A3", colA2);
auto colA5 = db.createCollection("Col A5", colA2);
auto colA6 = db.createCollection("Col A6", resA);
auto colA7 = db.createCollection("Col A7", colA2);
auto colA8 = db.createCollection("Col A8", colA7);
auto colA9 = db.createCollection("Col A9", resA);
auto colA10 = db.createCollection("Col A10", colA6);
// Move the collection to the final parent
colA5.setParent(colA7);
colA5.update();
}
private Q_SLOTS:
void populateTest()
{
DbInitializer db;
populateDb(db);
InspectableCollectionTreeCache treeCache;
QVERIFY(treeCache.waitForCachePopulated());
auto allCols = treeCache.retrieveCollections(Scope(), std::numeric_limits<int>::max(), 1);
SelectQueryBuilder<Collection> qb;
QVERIFY(qb.exec());