File indexing completed on 2024-04-21 15:03:01

0001 /*
0002     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "runnermanager.h"
0008 
0009 #ifdef KSERVICE_BUILD_DEPRECATED_SINCE
0010 #if KRUNNER_BUILD_DEPRECATED_SINCE(5, 72) && KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0011 #define WITH_KSERVICE 1
0012 #endif
0013 #endif
0014 
0015 #ifndef WITH_KSERVICE
0016 #define WITH_KSERVICE 0
0017 #endif
0018 
0019 #include <QAction>
0020 #include <QObject>
0021 #include <QProcess>
0022 #include <QSignalSpy>
0023 #include <QStandardPaths>
0024 #include <QTest>
0025 #include <QTime>
0026 #include <QTimer>
0027 
0028 #if WITH_KSERVICE
0029 #include <KSycoca>
0030 #endif
0031 
0032 #include "abstractrunnertest.h"
0033 #include "kpluginmetadata_utils_p.h"
0034 
0035 using namespace Plasma;
0036 
0037 Q_DECLARE_METATYPE(Plasma::QueryMatch)
0038 Q_DECLARE_METATYPE(QList<Plasma::QueryMatch>)
0039 
0040 class DBusRunnerTest : public AbstractRunnerTest
0041 {
0042     Q_OBJECT
0043 public:
0044     DBusRunnerTest();
0045     ~DBusRunnerTest() override;
0046 
0047 private Q_SLOTS:
0048     void initTestCase();
0049     void cleanup();
0050     void testMatch();
0051     void testMulti();
0052     void testFilterProperties();
0053     void testFilterProperties_data();
0054     void testRequestActionsOnce();
0055     void testDBusRunnerSyntaxIntegration();
0056     void testIconData();
0057     void testLifecycleMethods();
0058     void testRequestActionsOnceWildcards();
0059 #if WITH_KSERVICE
0060     void testMulti_data();
0061 #endif
0062 };
0063 
0064 DBusRunnerTest::DBusRunnerTest()
0065     : AbstractRunnerTest()
0066 {
0067     qRegisterMetaType<QList<Plasma::QueryMatch>>();
0068 }
0069 
0070 DBusRunnerTest::~DBusRunnerTest()
0071 {
0072 }
0073 
0074 void DBusRunnerTest::initTestCase()
0075 {
0076     // Set up a layer in the bin dir so ksycoca & KPluginMetaData find the Plasma/Runner service type
0077     const QByteArray defaultDataDirs = qEnvironmentVariableIsSet("XDG_DATA_DIRS") ? qgetenv("XDG_DATA_DIRS") : QByteArray("/usr/local:/usr");
0078     const QByteArray modifiedDataDirs = QFile::encodeName(QCoreApplication::applicationDirPath()) + QByteArrayLiteral("/data:") + defaultDataDirs;
0079     qputenv("XDG_DATA_DIRS", modifiedDataDirs);
0080 
0081     QStandardPaths::setTestModeEnabled(true);
0082 }
0083 
0084 void DBusRunnerTest::cleanup()
0085 {
0086     // Make sure kill the running processes after each test
0087     killRunningDBusProcesses();
0088 }
0089 
0090 void DBusRunnerTest::testMatch()
0091 {
0092     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0093     initProperties();
0094     launchQuery(QStringLiteral("foo"));
0095 
0096     // verify matches
0097     const QList<QueryMatch> matches = manager->matches();
0098     QCOMPARE(matches.count(), 1);
0099     auto result = matches.first();
0100 
0101     // see testremoterunner.cpp
0102     QCOMPARE(result.id(), QStringLiteral("dbusrunnertest_id1")); // note the runner name is prepended
0103     QCOMPARE(result.text(), QStringLiteral("Match 1"));
0104     QCOMPARE(result.iconName(), QStringLiteral("icon1"));
0105     QCOMPARE(result.type(), Plasma::QueryMatch::ExactMatch);
0106     QCOMPARE(result.isMultiLine(), true);
0107     // relevance can't be compared easily because RunnerContext meddles with it
0108 
0109     // verify actions
0110     QTRY_COMPARE_WITH_TIMEOUT(manager->actionsForMatch(result).count(), 1, 10000);
0111     auto action = manager->actionsForMatch(result).constFirst();
0112 
0113     QCOMPARE(action->text(), QStringLiteral("Action 1"));
0114 
0115     QSignalSpy processSpy(process, &QProcess::readyRead);
0116     manager->run(result);
0117     processSpy.wait();
0118     QCOMPARE(process->readAllStandardOutput().trimmed().split('\n').constLast(), QByteArray("Running:id1:"));
0119 
0120     result.setSelectedAction(action);
0121     manager->run(result);
0122     processSpy.wait();
0123     QCOMPARE(process->readAllStandardOutput().trimmed().split('\n').constLast(), QByteArray("Running:id1:action1"));
0124 }
0125 
0126 #if WITH_KSERVICE
0127 void DBusRunnerTest::testMulti_data()
0128 {
0129     QTest::addColumn<bool>("useKService");
0130 
0131     QTest::newRow("deprecated") << true;
0132     QTest::newRow("non-deprecated") << false;
0133 }
0134 #endif
0135 
0136 void DBusRunnerTest::testMulti()
0137 {
0138 #if WITH_KSERVICE
0139     QFETCH(bool, useKService);
0140 #endif
0141     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a1")}, QStringLiteral("net.krunnertests.multi.a1"));
0142     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a2")}, QStringLiteral("net.krunnertests.multi.a2"));
0143     manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
0144 
0145 #if WITH_KSERVICE
0146     if (useKService) {
0147         KService::Ptr s(new KService(QFINDTESTDATA("dbusrunnertestmulti.desktop")));
0148         QVERIFY(s);
0149         manager->loadRunner(s);
0150     } else {
0151 #endif
0152         auto md = parseMetaDataFromDesktopFile(QFINDTESTDATA("dbusrunnertestmulti.desktop"));
0153         QVERIFY(md.isValid());
0154         manager->loadRunner(md);
0155 #if WITH_KSERVICE
0156     }
0157 #endif
0158     launchQuery(QStringLiteral("foo"));
0159 
0160     // verify matches, must be one from each
0161     const QList<QueryMatch> matches = manager->matches();
0162     QCOMPARE(matches.count(), 2);
0163 
0164     const QString first = matches.at(0).data().toList().constFirst().toString();
0165     const QString second = matches.at(1).data().toList().constFirst().toString();
0166     QVERIFY(first != second);
0167     QVERIFY(first == QLatin1String("net.krunnertests.multi.a1") || first == QStringLiteral("net.krunnertests.multi.a2"));
0168     QVERIFY(second == QLatin1String("net.krunnertests.multi.a1") || second == QStringLiteral("net.krunnertests.multi.a2"));
0169 }
0170 
0171 void DBusRunnerTest::testRequestActionsOnce()
0172 {
0173     startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0174     initProperties();
0175 
0176     // Construct a fake match with necessary data
0177     QueryMatch fakeMatch(runner);
0178     fakeMatch.setId(QStringLiteral("dbusrunnertest_id1"));
0179     fakeMatch.setData(QVariantList({QStringLiteral("net.krunnertests.dave"), QStringList({QStringLiteral("action1"), QStringLiteral("action2")})}));
0180 
0181     // The actions should not be fetched before we have set up the match session
0182     QCOMPARE(manager->actionsForMatch(fakeMatch).count(), 0);
0183     launchQuery(QStringLiteral("foo"));
0184     // We need to retry this, because the DBus call to fetch the actions is async
0185     QTRY_COMPARE_WITH_TIMEOUT(manager->actionsForMatch(fakeMatch).count(), 2, 2500);
0186 }
0187 
0188 void DBusRunnerTest::testFilterProperties_data()
0189 {
0190     QTest::addColumn<QString>("rejectedQuery");
0191     QTest::addColumn<QString>("acceptedQuery");
0192 
0193     QTest::newRow("min-letter-count") << "fo"
0194                                       << "foo";
0195     QTest::newRow("match-regex") << "barfoo"
0196                                  << "foobar";
0197 }
0198 
0199 void DBusRunnerTest::testFilterProperties()
0200 {
0201     QFETCH(QString, rejectedQuery);
0202     QFETCH(QString, acceptedQuery);
0203     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0204     initProperties();
0205 
0206     launchQuery(rejectedQuery);
0207     // Match method was not called, because of the min letter count or match regex property
0208     QVERIFY(process->readAllStandardOutput().isEmpty());
0209     // accepted query fits those constraints
0210     launchQuery(acceptedQuery);
0211     QCOMPARE(process->readAllStandardOutput().trimmed(), QString(QStringLiteral("Matching:") + acceptedQuery).toLocal8Bit());
0212 }
0213 
0214 void DBusRunnerTest::testDBusRunnerSyntaxIntegration()
0215 {
0216     startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0217     initProperties();
0218     const QList<RunnerSyntax> syntaxes = runner->syntaxes();
0219     QCOMPARE(syntaxes.size(), 2);
0220 
0221     QCOMPARE(syntaxes.at(0).exampleQueries().size(), 1);
0222     QCOMPARE(syntaxes.at(0).exampleQueries().constFirst(), QStringLiteral("syntax1"));
0223     QCOMPARE(syntaxes.at(0).description(), QStringLiteral("description1"));
0224     QCOMPARE(syntaxes.at(1).exampleQueries().size(), 1);
0225     QCOMPARE(syntaxes.at(1).exampleQueries().constFirst(), QStringLiteral("syntax2"));
0226     QCOMPARE(syntaxes.at(1).description(), QStringLiteral("description2"));
0227 }
0228 
0229 void DBusRunnerTest::testIconData()
0230 {
0231     startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0232     initProperties();
0233 
0234     launchQuery(QStringLiteral("fooCostomIcon"));
0235 
0236     const auto matches = manager->matches();
0237     QCOMPARE(matches.count(), 1);
0238     auto result = matches.first();
0239 
0240     QImage expectedIcon(10, 10, QImage::Format_RGBA8888);
0241     expectedIcon.fill(Qt::blue);
0242 
0243     QCOMPARE(result.icon().availableSizes().first(), QSize(10, 10));
0244     QCOMPARE(result.icon().pixmap(QSize(10, 10)), QPixmap::fromImage(expectedIcon));
0245 }
0246 
0247 void DBusRunnerTest::testLifecycleMethods()
0248 {
0249     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave"), QString()});
0250     manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
0251     auto md = parseMetaDataFromDesktopFile(QFINDTESTDATA("dbusrunnertestruntimeconfig.desktop"));
0252     manager->loadRunner(md);
0253     QCOMPARE(manager->runners().count(), 1);
0254     // Match session should be set up automatically
0255     launchQuery(QStringLiteral("fooo"));
0256 
0257     // Make sure we got our match, end the match session and give the process a bit of time to get the DBus signal
0258     QTRY_COMPARE_WITH_TIMEOUT(manager->matches().count(), 1, 2000);
0259     manager->matchSessionComplete();
0260     QTest::qWait(500);
0261 
0262     const QStringList lifeCycleSteps = QString::fromLocal8Bit(process->readAllStandardOutput()).split(QLatin1Char('\n'), Qt::SkipEmptyParts);
0263     const QStringList expectedLifeCycleSteps = {
0264         QStringLiteral("Config"),
0265         QStringLiteral("Matching:fooo"),
0266         QStringLiteral("Teardown"),
0267     };
0268     QCOMPARE(lifeCycleSteps, expectedLifeCycleSteps);
0269 
0270     // The query does not match our min letter count we set at runtime
0271     launchQuery(QStringLiteral("foo"));
0272     QVERIFY(manager->matches().isEmpty());
0273     // The query does not match our match regex we set at runtime
0274     launchQuery(QStringLiteral("barfoo"));
0275     QVERIFY(manager->matches().isEmpty());
0276 }
0277 
0278 void DBusRunnerTest::testRequestActionsOnceWildcards()
0279 {
0280     initProperties();
0281     manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
0282     auto md = parseMetaDataFromDesktopFile(QFINDTESTDATA("dbusrunnertestmulti.desktop"));
0283     QVERIFY(md.isValid());
0284     manager->loadRunner(md);
0285     QCOMPARE(manager->runners().count(), 1);
0286     launchQuery("foo");
0287     QVERIFY(manager->matches().isEmpty());
0288     QueryMatch match(manager->runners().constFirst());
0289     match.setId("test");
0290     match.setData(QStringList{"net.krunnertests.multi.a1"});
0291     QVERIFY(manager->actionsForMatch(match).isEmpty());
0292     manager->matchSessionComplete();
0293 
0294     // We have started the process later and the actions should now be fetched when the match session is started
0295     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a1")}, QStringLiteral("net.krunnertests.multi.a1"));
0296     QTest::qWait(500); // Wait a bit for the runner to pick up the new service
0297 
0298     launchQuery("fooo");
0299     QVERIFY(!manager->matches().isEmpty());
0300     QVERIFY(!manager->actionsForMatch(match).isEmpty());
0301 }
0302 
0303 QTEST_MAIN(DBusRunnerTest)
0304 
0305 #include "dbusrunnertest.moc"