Verified Commit c3caad49 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

Port Core to ASI

Except for ItemSync and CollectionSync for now, those are going to
be ported away from jobs anyway as we switch to server-side sync.

Added an extra test for pastehelper.
parent aac8466a
......@@ -122,3 +122,5 @@ add_akonadi_isolated_test(SOURCE tagtest.cpp ADDITIONAL_SOURCES ${CMAKE_BINARY_D
add_akonadi_isolated_test(SOURCE tagsynctest.cpp)
add_akonadi_isolated_test(SOURCE relationtest.cpp)
add_akonadi_isolated_test(SOURCE etmpopulationtest.cpp)
add_akonadi_isolated_test(SOURCE storagetest.cpp)
add_akonadi_isolated_test(SOURCE pastehelpertest.cpp)
......@@ -15,10 +15,10 @@ class EntityCacheTest : public QObject
{
Q_OBJECT
private:
template <typename T, typename FetchJob, typename FetchScope>
template <typename T, typename FetchScope>
void testCache()
{
EntityCache<T, FetchJob, FetchScope> cache(2);
EntityCache<T, FetchScope> cache(2);
QSignalSpy spy(&cache, SIGNAL(dataAvailable()));
QVERIFY(spy.isValid());
......@@ -104,9 +104,9 @@ private Q_SLOTS:
{
QFETCH(bool, collection);
if (collection) {
testCache<Collection, CollectionFetchJob, CollectionFetchScope>();
testCache<Collection, CollectionFetchScope>();
} else {
testCache<Item, ItemFetchJob, ItemFetchScope>();
testCache<Item, ItemFetchScope>();
}
}
......@@ -139,7 +139,7 @@ private Q_SLOTS:
{
ItemFetchScope scope;
EntityListCache<Item, ItemFetchJob, ItemFetchScope> cache(3);
EntityListCache<Item, ItemFetchScope> cache(3);
QSignalSpy spy(&cache, &EntityCacheBase::dataAvailable);
QVERIFY(spy.isValid());
......
/*
SPDX-FileCopyrightText: 2020 Daniel Vrátil
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "pastehelper_p.h"
#include "interface.h"
#include "shared/akscopeguard.h"
#include <QMimeData>
#include <QUrlQuery>
#include <qtest_akonadi.h>
using namespace Akonadi;
Q_DECLARE_METATYPE(Akonadi::Collection::Right)
Q_DECLARE_METATYPE(Akonadi::Collection::Rights)
class PasteHelperTest : public QObject
{
Q_OBJECT
const QString mimeTypeOctetStream = QStringLiteral("application/octet-stream");
const QString mimeTypeContacts = QStringLiteral("text/directory");
private:
Collection createCollection(Collection::Rights rights, const QString &mimeType, bool isVirtual)
{
Collection col(42);
col.setRights(rights);
col.setContentMimeTypes({mimeType});
col.setVirtual(isVirtual);
return col;
}
QUrl itemUrl(const QString &mimeType)
{
QUrl url;
url.setScheme(QStringLiteral("akonadi"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("item"), QStringLiteral("123456"));
query.addQueryItem(QStringLiteral("type"), mimeType);
url.setQuery(query);
return url;
}
QUrl collectionUrl()
{
QUrl url;
url.setScheme(QStringLiteral("akonadi"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("collection"), QStringLiteral("123"));
url.setQuery(query);
return url;
}
private Q_SLOTS:
void testCanPaste_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<Collection>("destCollection");
QTest::addColumn<Qt::DropAction>("dropAction");
QTest::addColumn<bool>("canPaste");
QTest::newRow("Copy item into all-rights col")
<< itemUrl(mimeTypeOctetStream)
<< createCollection(Collection::AllRights, mimeTypeOctetStream, false)
<< Qt::CopyAction
<< true;
QTest::newRow("Move item into all-rights col")
<< itemUrl(mimeTypeOctetStream)
<< createCollection(Collection::AllRights, mimeTypeOctetStream, false)
<< Qt::MoveAction
<< true;
QTest::newRow("Copy item into read-only col")
<< itemUrl(mimeTypeOctetStream)
<< createCollection(Collection::ReadOnly, mimeTypeOctetStream, false)
<< Qt::CopyAction
<< false;
QTest::newRow("Move item into read-only col")
<< itemUrl(mimeTypeOctetStream)
<< createCollection(Collection::ReadOnly, mimeTypeOctetStream, false)
<< Qt::MoveAction
<< false;
QTest::newRow("Copy item into virtual collection")
<< itemUrl(mimeTypeOctetStream)
<< createCollection({Collection::CanLinkItem | Collection::CanUnlinkItem}, mimeTypeOctetStream, true)
<< Qt::CopyAction
<< false;
QTest::newRow("Move item into virtual collection")
<< itemUrl(mimeTypeOctetStream)
<< createCollection({Collection::CanLinkItem | Collection::CanUnlinkItem}, mimeTypeOctetStream, true)
<< Qt::MoveAction
<< false;
QTest::newRow("Link into virtual collection")
<< itemUrl(mimeTypeOctetStream)
<< createCollection({Collection::CanLinkItem | Collection::CanUnlinkItem}, mimeTypeOctetStream, true)
<< Qt::LinkAction
<< true;
QTest::newRow("Copy item into col - mimetype mismatch")
<< itemUrl(mimeTypeOctetStream)
<< createCollection(Collection::AllRights, mimeTypeContacts, false)
<< Qt::CopyAction
<< false;
QTest::newRow("Copy col into all-rights col")
<< collectionUrl()
<< createCollection(Collection::AllRights, {}, false)
<< Qt::CopyAction
<< true;
QTest::newRow("Copy col into read-only col")
<< collectionUrl()
<< createCollection(Collection::ReadOnly, {}, false)
<< Qt::CopyAction
<< false;
QTest::newRow("Move col into all-rights col")
<< collectionUrl()
<< createCollection(Collection::AllRights, {}, false)
<< Qt::MoveAction
<< true;
QTest::newRow("Move col into read-only col")
<< collectionUrl()
<< createCollection(Collection::ReadOnly, {}, false)
<< Qt::MoveAction
<< false;
}
void testCanPaste()
{
QFETCH(QUrl, url);
QFETCH(Collection, destCollection);
QFETCH(Qt::DropAction, dropAction);
QFETCH(bool, canPaste);
QMimeData mimeData;
mimeData.setUrls({url});
QCOMPARE(PasteHelper::canPaste(&mimeData, destCollection, dropAction), canPaste);
}
void testPasteUriList_data()
{
QTest::addColumn<QString>("destMimeType");
QTest::addColumn<Collection::Rights>("destRights");
QTest::addColumn<bool>("destIsVirtual");
QTest::addColumn<Qt::DropAction>("dropAction");
QTest::addColumn<bool>("success");
QTest::newRow("Copy into writable col")
<< mimeTypeOctetStream
<< Collection::Rights{Collection::AllRights}
<< false
<< Qt::CopyAction
<< true;
QTest::newRow("Copy into read-only col")
<< mimeTypeOctetStream
<< Collection::Rights{Collection::ReadOnly}
<< false
<< Qt::CopyAction
<< false;
QTest::newRow("Move into writable col")
<< mimeTypeOctetStream
<< Collection::Rights{Collection::AllRights}
<< false
<< Qt::MoveAction
<< true;
QTest::newRow("Link into virtual col")
<< mimeTypeOctetStream
<< Collection::Rights{Collection::CanLinkItem | Collection::CanUnlinkItem}
<< true
<< Qt::LinkAction
<< true;
QTest::newRow("Move into virtual col")
<< mimeTypeOctetStream
<< Collection::Rights{Collection::CanLinkItem | Collection::CanUnlinkItem}
<< true
<< Qt::MoveAction
<< false;
QTest::newRow("Copy into virtual col")
<< mimeTypeOctetStream
<< Collection::Rights{Collection::CanLinkItem | Collection::CanUnlinkItem}
<< true
<< Qt::CopyAction
<< false;
}
void testPasteUriList()
{
QFETCH(QString, destMimeType);
QFETCH(Collection::Rights, destRights);
QFETCH(bool, destIsVirtual);
QFETCH(Qt::DropAction, dropAction);
QFETCH(bool, success);
// Create source collection
const Collection baseCol{AkonadiTest::collectionIdFromPath(QStringLiteral("res1"))};
Collection sourceCol;
{
sourceCol.setParentCollection(baseCol);
sourceCol.setName(QStringLiteral("pastehelpertest-src"));
sourceCol.setContentMimeTypes({mimeTypeOctetStream, mimeTypeContacts});
sourceCol.setRights(Collection::AllRights);
auto createColTask = Akonadi::createCollection(sourceCol);
createColTask.wait();
QVERIFY(!createColTask.hasError());
sourceCol = createColTask.result();
}
AkScopeGuard removeSourceCollection{[sourceCol]() {
Akonadi::deleteCollection(sourceCol).wait();
}};
// Populate source collection with items
Item item;
{
item.setMimeType(mimeTypeOctetStream);
item.setPayload(QByteArray("FooBar"));
item.setParentCollection(sourceCol);
auto task = Akonadi::createItem(item, sourceCol);
AKVERIFYTASK(task);
item = task.result();
}
// Create destination collection with given properties
Collection destCol;
{
destCol.setParentCollection(baseCol);
destCol.setName(QStringLiteral("pastehelpertest-dst"));
destCol.setContentMimeTypes({destMimeType});
destCol.setRights(destRights);
destCol.setVirtual(destIsVirtual);
auto createColTask = Akonadi::createCollection(destCol);
AKVERIFYTASK(createColTask);
destCol = createColTask.result();
}
AkScopeGuard removeDestcollection{[destCol]() {
Akonadi::deleteCollection(destCol).wait();
}};
// Perform given action
QMimeData data;
data.setUrls({item.url(Item::UrlWithMimeType)});
auto *job = PasteHelper::pasteUriList(&data, destCol, dropAction);
QCOMPARE((job != nullptr), success);
if (success) {
AKVERIFYEXEC(job);
if (dropAction == Qt::MoveAction) {
const auto task = Akonadi::fetchItemsFromCollection(sourceCol, ItemFetchOptions{});
AKVERIFYTASK(task);
QVERIFY(task.result().empty());
}
ItemFetchScope fetchScope;
fetchScope.fetchFullPayload();
const auto task = Akonadi::fetchItemsFromCollection(destCol, fetchScope);
AKVERIFYTASK(task);
const auto fetchedItems = task.result();
QCOMPARE(fetchedItems.size(), 1);
QCOMPARE(fetchedItems[0].payload<QByteArray>(), QByteArray("FooBar"));
}
}
};
#include "pastehelpertest.moc"
QTEST_AKONADIMAIN(PasteHelperTest)
/*
SPDX-FileCopyrightText: 2020 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <QObject>
#include <QTest>
#include <QEventLoop>
#include "qtest_akonadi.h"
#include "storage.h"
using namespace Akonadi;
class StorageTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testCollectionFetch()
{
Storage storage;
QEventLoop eventLoop;
{
storage.fetchSubcollections(Collection::root(), CollectionFetchOptions{}, storage.defaultSession())
.then([&eventLoop](const Collection::List &cols) mutable {
QCOMPARE(cols.size(), 4);
eventLoop.quit();
});
}
eventLoop.exec();
}
void testFetchAllTags()
{
Storage storage;
QEventLoop eventLoop;
{
storage.fetchAllTags(TagFetchOptions{}, storage.defaultSession())
.then([&eventLoop](const Tag::List &tags) mutable {
QCOMPARE(tags.size(), 4);
eventLoop.quit();
});
}
eventLoop.quit();
}
};
QTEST_AKONADIMAIN(StorageTest);
#include "storagetest.moc"
......@@ -166,6 +166,79 @@ private Q_SLOTS:
loop.exec();
QVERIFY(allDone);
}
void testForEach()
{
QVector<int> items = {1, 2, 3, 4, 5};
auto task = taskForEach(items, [](int item) {
Task<int> t;
QTimer::singleShot(std::chrono::milliseconds{10}, [t, item]() mutable {
t.setResult(item);
});
return t;
});
QVERIFY(!task.isFinished());
task.wait();
QVERIFY(task.isFinished());
}
void testForEachError()
{
QVector<int> items = {1, 2, 3, 4, 5};
auto task = taskForEach(items, [](int item) {
Task<int> t;
QTimer::singleShot(std::chrono::milliseconds{10}, [t, item]() mutable {
if (item == 3) {
t.setError(1, QStringLiteral("Oppsie"));
} else {
t.setResult(item);
}
});
return t;
});
QVERIFY(!task.isFinished());
task.wait();
QVERIFY(task.hasError());
QCOMPARE(task.error().code(), 1);
}
void testCollectionTasks()
{
QVector<int> items{1, 2, 3, 4, 5};
auto task = collectTasks(items, [](int item) {
Task<int> t;
QTimer::singleShot(std::chrono::milliseconds{10}, [t, item]() mutable {
t.setResult(item);
});
return t;
});
QVERIFY(!task.isFinished());
task.wait();
QVERIFY(task.isFinished());
QVERIFY(!task.hasError());
QCOMPARE(task.result(), items);
}
void testCollectionTasksWithCollection()
{
QVector<int> items{1, 2, 3, 4, 5};
auto task = collectTasks(items, [](int item) {
Task<QVector<int>> t;
QTimer::singleShot(std::chrono::milliseconds{10}, [t, item]() mutable {
t.setResult({item, item, item});
});
return t;
});
QVERIFY(!task.isFinished());
task.wait();
QVERIFY(task.isFinished());
QVERIFY(!task.hasError());
QCOMPARE(task.result(), (QVector<int>{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5}));
}
};
QTEST_GUILESS_MAIN(TaskTest)
......
......@@ -32,16 +32,15 @@ namespace Akonadi
*
* ...
* // hide a collection by setting the hidden attribute
* Collection collection = collectionFetchJob->collections().at(0);
* collection.attribute<EntityHiddenAttribute>( Collection::AddIfMissing );
* new CollectionModifyJob( collection, this ); // save back to storage
* collection.attribute<EntityHiddenAttribute>(Collection::AddIfMissing);
* Akonadi::updateCollection(collection); // save back to storage
*
* // check if the collection is hidden
* if ( collection.hasAttribute<EntityHiddenAttribute>() )
* qDebug() << "collection is hidden";
* else
* qDebug() << "collection is visible";
*
* if (collection.hasAttribute<EntityHiddenAttribute>()) {
* qDebug() << "collection is hidden";
* } else {
* qDebug() << "collection is visible";
* }
* @endcode
*
* @author Szymon Stefanek <s.stefanek@gmail.com>
......
......@@ -23,7 +23,7 @@ class Collection;
* This attribute is attached to persistent search collections automatically when
* creating a new persistent search with SearchCreateJob.
* Later on the search query can be changed by modifying this attribute of the
* persistent search collection with an CollectionModifyJob.
* persistent search collection with Akonadi::updateCollection()
*
* Example:
*
......@@ -50,8 +50,7 @@ class Collection;
* Akonadi::PersistentSearchAttribute *attribute = searchCollection.attribute<Akonadi::PersistentSearchAttribute>();
* attribute->setQueryString( "... another query string ..." );
*
* Akonadi::CollectionModifyJob *modifyJob = new Akonadi::CollectionModifyJob( searchCollection );
* connect( modifyJob, SIGNAL(result(KJob*)), SLOT(modifyFinished(KJob*)) );
* Akonadi::updateCollection(searchCollection);
* }
* ...
* }
......
......@@ -5,8 +5,8 @@
*/
#include "collectionpathresolver.h"
#include "interface.h"
#include "collectionfetchjob.h"
#include "job_p.h"
#include "akonadicore_debug.h"
......@@ -19,19 +19,15 @@ using namespace Akonadi;
//@cond PRIVATE
class Akonadi::CollectionPathResolverPrivate : public JobPrivate
class Akonadi::CollectionPathResolverPrivate
{
public:
explicit CollectionPathResolverPrivate(CollectionPathResolver *parent)
: JobPrivate(parent)
, mColId(-1)
{
}
: q(parent)
{}
void init(const QString &path, const Collection &rootCollection)
{
Q_Q(CollectionPathResolver);
mPathToId = true;
mPath = path;
if (mPath.startsWith(q->pathDelimiter())) {
......@@ -45,7 +41,15 @@ public:
mCurrentNode = rootCollection;
}
void jobResult(KJob *job);
void collectionsRetrieved(const Collection::List &cols);
void collectionFetchError(const Error &error)
{
qCWarning(AKONADICORE_LOG) << "Failed to fetch Collections:" << error;
q->setError(error.code());
q->setErrorText(error.message());
q->emitResult();
}
QStringList splitPath(const QString &path)
{
......@@ -73,29 +77,21 @@ public:
return rv;
}
Q_DECLARE_PUBLIC(CollectionPathResolver)
Collection mCurrentNode;
QStringList mPathParts;
QString mPath;
Collection::Id mColId;
Collection::Id mColId = -1;
bool mPathToId = false;
private:
CollectionPathResolver * const q;
};
void CollectionPathResolverPrivate::jobResult(KJob *job)
void CollectionPathResolverPrivate::collectionsRetrieved(const Collection::List &cols)
{
if (job->error()) {
return;
}
Q_Q(CollectionPathResolver);
CollectionFetchJob *list = static_cast<CollectionFetchJob *>(job);
CollectionFetchJob *nextJob = nullptr;
const Collection::List cols = list->collections();
if (cols.isEmpty()) {
mColId = -1;
q->setError(CollectionPathResolver::Unknown);
q->setError(KJob::UserDefinedError);
q->setErrorText(i18n("No such collection."));
q->emitResult();
return;
......@@ -114,7 +110,7 @@ void CollectionPathResolverPrivate::jobResult(KJob *job)
if (!found) {
qCWarning(AKONADICORE_LOG) << "No such collection" << currentPart << "with parent" << mCurrentNode.id();
mColId = -1;
q->setError(CollectionPathResolver::Unknown);
q->setError(KJob::UserDefinedError);
q->setErrorText(i18n("No such collection."));
q->emitResult();
return;
......@@ -124,59 +120,64 @@ void CollectionPathResolverPrivate::jobResult(KJob *job)
q->emitResult();
return;
}
nextJob = new CollectionFetchJob(mCurrentNode, CollectionFetchJob::FirstLevel, q);