#include #include #include #include "clientapi.h" #include "facade.h" #include "synclistresult.h" #include "resourceconfig.h" class RevisionNotifier : public QObject { Q_OBJECT public: RevisionNotifier() : QObject() {}; void notify(qint64 revision) { emit revisionChanged(revision); } Q_SIGNALS: void revisionChanged(qint64); }; class DummyResourceFacade : public Akonadi2::StoreFacade { public: ~DummyResourceFacade(){}; KAsync::Job create(const Akonadi2::ApplicationDomain::Event &domainObject) Q_DECL_OVERRIDE { return KAsync::null(); }; KAsync::Job modify(const Akonadi2::ApplicationDomain::Event &domainObject) Q_DECL_OVERRIDE { return KAsync::null(); }; KAsync::Job remove(const Akonadi2::ApplicationDomain::Event &domainObject) Q_DECL_OVERRIDE { return KAsync::null(); }; KAsync::Job load(const Akonadi2::Query &query, const std::function &resultCallback) { return KAsync::start([this, resultCallback](KAsync::Future &future) { qDebug() << "load called"; for(const auto &result : results) { resultCallback(result); } future.setValue(0); future.setFinished(); }); } KAsync::Job load(const Akonadi2::Query &query, const QSharedPointer > &resultProvider) Q_DECL_OVERRIDE { auto runner = QSharedPointer::create(query); //The runner only lives as long as the resultProvider resultProvider->setQueryRunner(runner); QWeakPointer > weakResultProvider = resultProvider; capturedResultProvider = resultProvider; runner->setQuery([this, weakResultProvider, query](qint64 oldRevision, qint64 newRevision) -> KAsync::Job { qDebug() << "Creating query for revisions: " << oldRevision << newRevision; return KAsync::start([this, weakResultProvider, query](KAsync::Future &future) { auto resultProvider = weakResultProvider.toStrongRef(); if (!resultProvider) { Warning() << "Tried executing query after result provider is already gone"; future.setError(0, QString()); future.setFinished(); return; } //TODO only emit changes and don't replace everything resultProvider->clear(); //rerun query std::function addCallback = std::bind(&Akonadi2::ResultProvider::add, resultProvider, std::placeholders::_1); load(query, addCallback).then([resultProvider, &future, query](qint64 queriedRevision) { future.setValue(queriedRevision); future.setFinished(); }).exec(); }); }); //Ensure the notification is emitted in the right thread //Otherwise we get crashes as we call revisionChanged from the test. if (!notifier) { notifier.reset(new RevisionNotifier); } //TODO somehow disconnect as resultNotifier is destroyed. Otherwise we keep the runner alive forever. if (query.liveQuery) { QObject::connect(notifier.data(), &RevisionNotifier::revisionChanged, [runner](qint64 newRevision) { runner->revisionChanged(newRevision); }); } return KAsync::start([runner](KAsync::Future &future) { runner->run().then([&future]() { //TODO if not live query, destroy runner. future.setFinished(); }).exec(); }); } QList results; QSharedPointer notifier; QWeakPointer > capturedResultProvider; }; class ClientAPITest : public QObject { Q_OBJECT private Q_SLOTS: static std::shared_ptr registerDummyFacade() { auto facade = std::make_shared(); Akonadi2::FacadeFactory::instance().registerFacade("dummyresource", [facade](const QByteArray &instanceIdentifier) { return facade; } ); return facade; } void initTestCase() { Akonadi2::FacadeFactory::instance().resetFactory(); ResourceConfig::clear(); } void testLoad() { auto facade = registerDummyFacade(); facade->results << QSharedPointer::create("resource", "id", 0, QSharedPointer()); ResourceConfig::addResource("dummyresource.instance1", "dummyresource"); Akonadi2::Query query; query.resources << "dummyresource.instance1"; query.liveQuery = false; async::SyncListResult result(Akonadi2::Store::load(query)); result.exec(); QCOMPARE(result.size(), 1); } void testLiveQuery() { auto facade = registerDummyFacade(); facade->results << QSharedPointer::create("resource", "id", 0, QSharedPointer()); ResourceConfig::addResource("dummyresource.instance1", "dummyresource"); Akonadi2::Query query; query.resources << "dummyresource.instance1"; query.liveQuery = true; async::SyncListResult result(Akonadi2::Store::load(query)); result.exec(); QCOMPARE(result.size(), 1); //Enter a second result facade->results << QSharedPointer::create("resource", "id2", 0, QSharedPointer()); QVERIFY(facade->notifier); facade->notifier->revisionChanged(2); QTRY_COMPARE(result.size(), 2); } void testQueryLifetime() { auto facade = registerDummyFacade(); facade->results << QSharedPointer::create("resource", "id", 0, QSharedPointer()); ResourceConfig::addResource("dummyresource.instance1", "dummyresource"); Akonadi2::Query query; query.resources << "dummyresource.instance1"; query.liveQuery = true; { async::SyncListResult result(Akonadi2::Store::load(query)); result.exec(); QCOMPARE(result.size(), 1); } //It's running in a separate thread, so we have to wait for a moment. QTRY_VERIFY(!facade->capturedResultProvider); } void resourceManagement() { ResourceConfig::clear(); Akonadi2::FacadeFactory::instance().registerStaticFacades(); ResourceConfig::addResource("resourceconfig", "resourceconfig"); Akonadi2::ApplicationDomain::AkonadiResource res; res.setProperty("identifier", "dummyresource.identifier1"); res.setProperty("type", "dummyresource"); Akonadi2::Store::create(res, "resourceconfig"); { Akonadi2::Query query; query.resources << "resourceconfig"; query.propertyFilter.insert("type", "dummyresource"); async::SyncListResult result(Akonadi2::Store::load(query)); result.exec(); QCOMPARE(result.size(), 1); } Akonadi2::Store::remove(res, "resourceconfig"); { Akonadi2::Query query; query.resources << "resourceconfig"; query.propertyFilter.insert("type", "dummyresource"); async::SyncListResult result(Akonadi2::Store::load(query)); result.exec(); QCOMPARE(result.size(), 0); } } }; QTEST_MAIN(ClientAPITest) #include "clientapitest.moc"