File indexing completed on 2024-04-21 03:56:47
0001 /* 0002 SPDX-FileCopyrightText: 2020 Alexander Lohnau <alexander.lohnau@gmx.de> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #ifndef KRUNNER_ABSTRACTRUNNERTEST_H 0007 #define KRUNNER_ABSTRACTRUNNERTEST_H 0008 0009 #include <KPluginMetaData> 0010 #include <KRunner/AbstractRunner> 0011 #include <KRunner/RunnerManager> 0012 #include <QStandardPaths> 0013 0014 #include <QSignalSpy> 0015 #include <QTest> 0016 #if KRUNNER_DBUS_RUNNER_TESTING 0017 #include <QDBusConnection> 0018 #include <QDBusConnectionInterface> 0019 #include <QDBusServiceWatcher> 0020 #include <QProcess> 0021 #include <QTimer> 0022 #endif 0023 0024 namespace KRunner 0025 { 0026 /** 0027 * This class provides a basic structure for a runner test. 0028 * The compile definitions should be configured using the `krunner_configure_test` cmake macro 0029 * @since 5.80 0030 */ 0031 class AbstractRunnerTest : public QObject 0032 { 0033 public: 0034 using QObject::QObject; 0035 std::unique_ptr<KRunner::RunnerManager> manager = nullptr; 0036 KRunner::AbstractRunner *runner = nullptr; 0037 0038 /** 0039 * Load the runner and set the manager and runner properties. 0040 */ 0041 void initProperties() 0042 { 0043 qputenv("LC_ALL", "C.utf-8"); 0044 manager.reset(new KRunner::RunnerManager()); 0045 0046 #if KRUNNER_DBUS_RUNNER_TESTING 0047 auto md = manager->convertDBusRunnerToJson(QStringLiteral(KRUNNER_TEST_DESKTOP_FILE)); 0048 QVERIFY(md.isValid()); 0049 manager->loadRunner(md); 0050 #else 0051 const QString pluginId = QFileInfo(QStringLiteral(KRUNNER_TEST_RUNNER_PLUGIN_NAME)).completeBaseName(); 0052 auto metaData = KPluginMetaData::findPluginById(QStringLiteral(KRUNNER_TEST_RUNNER_PLUGIN_DIR), pluginId); 0053 QVERIFY2(metaData.isValid(), qPrintable("Could not find plugin " + pluginId + " in folder " + KRUNNER_TEST_RUNNER_PLUGIN_DIR)); 0054 0055 // Set internal variables 0056 manager->loadRunner(metaData); 0057 #endif 0058 QCOMPARE(manager->runners().count(), 1); 0059 runner = manager->runners().constFirst(); 0060 0061 // Just make sure all went well 0062 QVERIFY(runner); 0063 } 0064 0065 /** 0066 * Launch a query and wait for the RunnerManager to finish 0067 * @param query 0068 * @param runnerName 0069 * @return matches of the current query 0070 */ 0071 QList<QueryMatch> launchQuery(const QString &query, const QString &runnerName = QString()) 0072 { 0073 QSignalSpy spy(manager.get(), &KRunner::RunnerManager::queryFinished); 0074 manager->launchQuery(query, runnerName); 0075 if (!QTest::qVerify(spy.wait(), "spy.wait()", "RunnerManager did not emit the queryFinished signal", __FILE__, __LINE__)) { 0076 return {}; 0077 } 0078 return manager->matches(); 0079 } 0080 #if KRUNNER_DBUS_RUNNER_TESTING 0081 /** 0082 * Launch the configured DBus executable with the given arguments and wait for the process to be started. 0083 * @param args 0084 * @param waitForService Wait for this service to be registered, this will default to the service from the metadata 0085 * @return Process that was successfully started 0086 */ 0087 QProcess *startDBusRunnerProcess(const QStringList &args = {}, const QString waitForService = QString()) 0088 { 0089 qputenv("LC_ALL", "C.utf-8"); 0090 QProcess *process = new QProcess(); 0091 auto md = manager->convertDBusRunnerToJson(QStringLiteral(KRUNNER_TEST_DESKTOP_FILE)); 0092 QString serviceToWatch = waitForService; 0093 if (serviceToWatch.isEmpty()) { 0094 serviceToWatch = md.value(QStringLiteral("X-Plasma-DBusRunner-Service")); 0095 } 0096 QEventLoop loop; 0097 // Wait for the service to show up. Same logic as the dbusrunner 0098 connect(QDBusConnection::sessionBus().interface(), 0099 &QDBusConnectionInterface::serviceOwnerChanged, 0100 &loop, 0101 [&loop, serviceToWatch](const QString &serviceName, const QString &, const QString &newOwner) { 0102 if (serviceName == serviceToWatch && !newOwner.isEmpty()) { 0103 loop.quit(); 0104 } 0105 }); 0106 0107 // Otherwise, we just wait forever without any indication what we are waiting for 0108 QTimer::singleShot(10000, &loop, [&loop, process]() { 0109 loop.quit(); 0110 0111 if (process->state() == QProcess::ProcessState::NotRunning) { 0112 qWarning() << "stderr of" << KRUNNER_TEST_DBUS_EXECUTABLE << "is:"; 0113 qWarning().noquote() << process->readAllStandardError(); 0114 } 0115 Q_ASSERT_X(false, "AbstractRunnerTest::startDBusRunnerProcess", "DBus service was not registered within 10 seconds"); 0116 }); 0117 process->start(QStringLiteral(KRUNNER_TEST_DBUS_EXECUTABLE), args); 0118 loop.exec(); 0119 process->waitForStarted(5); 0120 0121 Q_ASSERT(process->state() == QProcess::ProcessState::Running); 0122 m_runningProcesses << process; 0123 return process; 0124 } 0125 0126 /** 0127 * Kill all processes that got started with the startDBusRunnerProcess 0128 */ 0129 void killRunningDBusProcesses() 0130 { 0131 for (auto &process : std::as_const(m_runningProcesses)) { 0132 process->kill(); 0133 QVERIFY(process->waitForFinished()); 0134 if (QTest::currentTestFailed()) { 0135 qWarning().noquote() << "Output from " << process->program() << ": " << process->readAll(); 0136 } 0137 } 0138 qDeleteAll(m_runningProcesses); 0139 m_runningProcesses.clear(); 0140 } 0141 0142 private: 0143 QList<QProcess *> m_runningProcesses; 0144 #endif 0145 }; 0146 } 0147 0148 #endif