File indexing completed on 2024-06-16 04:50:16
0001 /* This file is based on qtest_kde.h from kdelibs 0002 SPDX-FileCopyrightText: 2006 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2009 Volker Krause <vkrause@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #pragma once 0009 0010 #include "agentinstance.h" 0011 #include "agentmanager.h" 0012 #include "collectionfetchscope.h" 0013 #include "collectionpathresolver.h" 0014 #include "itemfetchscope.h" 0015 #include "monitor.h" 0016 #include "servermanager.h" 0017 0018 #include <QDBusConnection> 0019 #include <QDBusInterface> 0020 #include <QDBusReply> 0021 #include <QSignalSpy> 0022 #include <QTest> 0023 #include <QTimer> 0024 0025 /** 0026 * \short Akonadi Replacement for QTEST_MAIN from QTestLib 0027 * 0028 * This macro should be used for classes that run inside the Akonadi Testrunner. 0029 * So instead of writing QTEST_MAIN( TestClass ) you write 0030 * QTEST_AKONADIMAIN( TestClass ). 0031 * 0032 * Unlike QTEST_MAIN, this macro actually does call QApplication::exec() so 0033 * that the application is running during test execution. This is needed 0034 * for proper clean up of Sessions. 0035 * 0036 * \param TestObject The class you use for testing. 0037 * 0038 * \see QTestLib 0039 * \see QTEST_KDEMAIN 0040 */ 0041 #define QTEST_AKONADIMAIN(TestObject) \ 0042 int main(int argc, char *argv[]) \ 0043 { \ 0044 qputenv("LC_ALL", "C"); \ 0045 qunsetenv("KDE_COLOR_DEBUG"); \ 0046 QApplication app(argc, argv); \ 0047 app.setApplicationName(QStringLiteral("qttest")); \ 0048 app.setOrganizationDomain(QStringLiteral("kde.org")); \ 0049 app.setOrganizationName(QStringLiteral("KDE")); \ 0050 QGuiApplication::setQuitOnLastWindowClosed(false); \ 0051 QCoreApplication::setQuitLockEnabled(false); \ 0052 qRegisterMetaType<QList<QUrl>>(); \ 0053 int result = 0; \ 0054 QTimer::singleShot(0, &app, [argc, argv, &result]() { \ 0055 TestObject tc; \ 0056 result = QTest::qExec(&tc, argc, argv); \ 0057 qApp->quit(); \ 0058 }); \ 0059 app.exec(); \ 0060 return result; \ 0061 } 0062 0063 namespace AkonadiTest 0064 { 0065 /** 0066 * Checks that the test is running in the proper test environment 0067 */ 0068 void checkTestIsIsolated() 0069 { 0070 if (qEnvironmentVariableIsEmpty("TESTRUNNER_DB_ENVIRONMENT")) 0071 qFatal("This test must be run using ctest, in order to use the testrunner environment. Aborting, to avoid messing up your real akonadi"); 0072 if (!qgetenv("XDG_DATA_HOME").contains("testrunner")) 0073 qFatal("Did you forget to run the test using QTEST_AKONADIMAIN?"); 0074 } 0075 0076 /** 0077 * Switch all resources offline to reduce interference from them 0078 */ 0079 void setAllResourcesOffline() 0080 { 0081 // switch all resources offline to reduce interference from them 0082 const auto lst = Akonadi::AgentManager::self()->instances(); 0083 for (Akonadi::AgentInstance agent : lst) { 0084 agent.setIsOnline(false); 0085 } 0086 } 0087 0088 template<typename Object, typename Func> 0089 bool akWaitForSignal(Object sender, Func member, int timeout = 1000) 0090 { 0091 QSignalSpy spy(sender, member); 0092 bool ok = false; 0093 [&]() { 0094 QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, timeout); 0095 ok = true; 0096 }(); 0097 return ok; 0098 } 0099 0100 bool akWaitForSignal(const QObject *sender, const char *member, int timeout = 1000) 0101 { 0102 QSignalSpy spy(sender, member); 0103 bool ok = false; 0104 [&]() { 0105 QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, timeout); 0106 ok = true; 0107 }(); 0108 return ok; 0109 } 0110 0111 qint64 collectionIdFromPath(const QString &path) 0112 { 0113 auto resolver = new Akonadi::CollectionPathResolver(path); 0114 bool success = resolver->exec(); 0115 if (!success) { 0116 qDebug() << "path resolution for " << path << " failed: " << resolver->errorText(); 0117 return -1; 0118 } 0119 qint64 id = resolver->collection(); 0120 return id; 0121 } 0122 0123 QString testrunnerServiceName() 0124 { 0125 const QString pid = QString::fromLocal8Bit(qgetenv("AKONADI_TESTRUNNER_PID")); 0126 Q_ASSERT(!pid.isEmpty()); 0127 return QStringLiteral("org.kde.Akonadi.Testrunner-") + pid; 0128 } 0129 0130 bool restartAkonadiServer() 0131 { 0132 QDBusInterface testrunnerIface(testrunnerServiceName(), QStringLiteral("/"), QStringLiteral("org.kde.Akonadi.Testrunner"), QDBusConnection::sessionBus()); 0133 if (!testrunnerIface.isValid()) { 0134 qWarning() << "Unable to get a dbus interface to the testrunner!"; 0135 } 0136 0137 QDBusReply<void> reply = testrunnerIface.call(QStringLiteral("restartAkonadiServer")); 0138 if (!reply.isValid()) { 0139 qWarning() << reply.error(); 0140 return false; 0141 } else if (Akonadi::ServerManager::isRunning()) { 0142 return true; 0143 } else { 0144 bool ok = false; 0145 [&]() { 0146 QSignalSpy spy(Akonadi::ServerManager::self(), &Akonadi::ServerManager::started); 0147 QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, 10000); 0148 ok = true; 0149 }(); 0150 return ok; 0151 } 0152 } 0153 0154 bool trackAkonadiProcess(bool track) 0155 { 0156 QDBusInterface testrunnerIface(testrunnerServiceName(), QStringLiteral("/"), QStringLiteral("org.kde.Akonadi.Testrunner"), QDBusConnection::sessionBus()); 0157 if (!testrunnerIface.isValid()) { 0158 qWarning() << "Unable to get a dbus interface to the testrunner!"; 0159 } 0160 0161 QDBusReply<void> reply = testrunnerIface.call(QStringLiteral("trackAkonadiProcess"), track); 0162 if (!reply.isValid()) { 0163 qWarning() << reply.error(); 0164 return false; 0165 } else { 0166 return true; 0167 } 0168 } 0169 0170 std::unique_ptr<Akonadi::Monitor> getTestMonitor() 0171 { 0172 auto m = new Akonadi::Monitor(); 0173 m->fetchCollection(true); 0174 m->setCollectionMonitored(Akonadi::Collection::root(), true); 0175 m->setAllMonitored(true); 0176 auto &itemFS = m->itemFetchScope(); 0177 itemFS.setAncestorRetrieval(Akonadi::ItemFetchScope::All); 0178 auto &colFS = m->collectionFetchScope(); 0179 colFS.setAncestorRetrieval(Akonadi::CollectionFetchScope::All); 0180 0181 QSignalSpy readySpy(m, &Akonadi::Monitor::monitorReady); 0182 readySpy.wait(); 0183 0184 return std::unique_ptr<Akonadi::Monitor>(m); 0185 } 0186 0187 } // namespace 0188 0189 /** 0190 * Runs an Akonadi::Job synchronously and aborts if the job failed. 0191 * Similar to QVERIFY( job->exec() ) but includes the job error message 0192 * in the output in case of a failure. 0193 */ 0194 #define AKVERIFYEXEC(job) QVERIFY2(job->exec(), job->errorString().toUtf8().constData())