File indexing completed on 2024-04-21 03:56:46

0001 /*
0002     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <KRunner/Action>
0008 #include <KRunner/RunnerManager>
0009 #include <QObject>
0010 #include <QProcess>
0011 #include <QSignalSpy>
0012 #include <QStandardPaths>
0013 #include <QTest>
0014 #include <QTime>
0015 #include <QTimer>
0016 
0017 #include "abstractrunnertest.h"
0018 #include "kpluginmetadata_utils_p.h"
0019 
0020 using namespace KRunner;
0021 
0022 Q_DECLARE_METATYPE(KRunner::QueryMatch)
0023 Q_DECLARE_METATYPE(QList<KRunner::QueryMatch>)
0024 
0025 class DBusRunnerTest : public AbstractRunnerTest
0026 {
0027     Q_OBJECT
0028 public:
0029     DBusRunnerTest();
0030     ~DBusRunnerTest() override;
0031 
0032 private Q_SLOTS:
0033     void cleanup();
0034     void testMatch();
0035     void testMulti();
0036     void testFilterProperties();
0037     void testFilterProperties_data();
0038     void testRequestActionsOnce();
0039     void testDBusRunnerSyntaxIntegration();
0040     void testIconData();
0041     void testLifecycleMethods();
0042     void testRequestActionsWildcards();
0043 };
0044 
0045 DBusRunnerTest::DBusRunnerTest()
0046     : AbstractRunnerTest()
0047 {
0048     qRegisterMetaType<QList<KRunner::QueryMatch>>();
0049     QStandardPaths::setTestModeEnabled(true);
0050 }
0051 
0052 DBusRunnerTest::~DBusRunnerTest()
0053 {
0054 }
0055 
0056 void DBusRunnerTest::cleanup()
0057 {
0058     // Make sure kill the running processes after each test
0059     killRunningDBusProcesses();
0060 }
0061 
0062 void DBusRunnerTest::testMatch()
0063 {
0064     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0065     initProperties();
0066     const auto matches = launchQuery(QStringLiteral("foo"));
0067 
0068     // verify matches
0069     QCOMPARE(matches.count(), 1);
0070     auto result = matches.first();
0071 
0072     // see testremoterunner.cpp
0073     QCOMPARE(result.id(), QStringLiteral("dbusrunnertest_id1")); // note the runner name is prepended
0074     QCOMPARE(result.text(), QStringLiteral("Match 1"));
0075     QCOMPARE(result.iconName(), QStringLiteral("icon1"));
0076     QCOMPARE(result.categoryRelevance(), qToUnderlying(KRunner::QueryMatch::CategoryRelevance::Highest));
0077     QCOMPARE(result.isMultiLine(), true);
0078     // relevance can't be compared easily because RunnerContext meddles with it
0079 
0080     // verify actions
0081     QCOMPARE(result.actions().size(), 1);
0082     auto action = result.actions().constFirst();
0083 
0084     QCOMPARE(action.text(), QStringLiteral("Action 1"));
0085 
0086     QSignalSpy processSpy(process, &QProcess::readyRead);
0087     manager->run(result);
0088     processSpy.wait();
0089     QCOMPARE(process->readAllStandardOutput().trimmed().split('\n').constLast(), QByteArray("Running:id1:"));
0090 
0091     manager->run(result, action);
0092     processSpy.wait();
0093     QCOMPARE(process->readAllStandardOutput().trimmed().split('\n').constLast(), QByteArray("Running:id1:action1"));
0094 }
0095 
0096 void DBusRunnerTest::testMulti()
0097 {
0098     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a1")}, QStringLiteral("net.krunnertests.multi.a1"));
0099     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a2")}, QStringLiteral("net.krunnertests.multi.a2"));
0100     manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
0101 
0102     auto md = parseMetaDataFromDesktopFile(QFINDTESTDATA("plugins/dbusrunnertestmulti.desktop"));
0103     QVERIFY(md.isValid());
0104     manager->loadRunner(md);
0105     const auto matches = launchQuery(QStringLiteral("foo"));
0106 
0107     // verify matches, must be one from each
0108     QCOMPARE(matches.count(), 2);
0109 
0110     const QString first = matches.at(0).data().toList().constFirst().toString();
0111     const QString second = matches.at(1).data().toList().constFirst().toString();
0112     QVERIFY(first != second);
0113     QVERIFY(first == QLatin1String("net.krunnertests.multi.a1") || first == QStringLiteral("net.krunnertests.multi.a2"));
0114     QVERIFY(second == QLatin1String("net.krunnertests.multi.a1") || second == QStringLiteral("net.krunnertests.multi.a2"));
0115 }
0116 
0117 void DBusRunnerTest::testRequestActionsOnce()
0118 {
0119     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0120     initProperties();
0121 
0122     launchQuery(QStringLiteral("foo"));
0123     QVERIFY(!manager->matches().constFirst().actions().isEmpty());
0124     manager->matchSessionComplete();
0125     launchQuery(QStringLiteral("fooo"));
0126     const QString processOutput(process->readAllStandardOutput());
0127     QCOMPARE(processOutput.count("Matching"), 2);
0128     QCOMPARE(processOutput.count("Actions"), 1);
0129     QVERIFY(!manager->matches().constFirst().actions().isEmpty());
0130 }
0131 
0132 void DBusRunnerTest::testFilterProperties_data()
0133 {
0134     QTest::addColumn<QString>("rejectedQuery");
0135     QTest::addColumn<QString>("acceptedQuery");
0136 
0137     QTest::newRow("min-letter-count") << "fo"
0138                                       << "foo";
0139     QTest::newRow("match-regex") << "barfoo"
0140                                  << "foobar";
0141 }
0142 
0143 void DBusRunnerTest::testFilterProperties()
0144 {
0145     QFETCH(QString, rejectedQuery);
0146     QFETCH(QString, acceptedQuery);
0147     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0148     initProperties();
0149 
0150     launchQuery(rejectedQuery);
0151     // Match method was not called, because of the min letter count or match regex property
0152     QVERIFY(process->readAllStandardOutput().isEmpty());
0153     // accepted query fits those constraints
0154     launchQuery(acceptedQuery);
0155     QCOMPARE(QString(process->readAllStandardOutput()).remove("Actions").trimmed(), QStringLiteral("Matching:") + acceptedQuery);
0156 }
0157 
0158 void DBusRunnerTest::testDBusRunnerSyntaxIntegration()
0159 {
0160     startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0161     initProperties();
0162     const QList<RunnerSyntax> syntaxes = runner->syntaxes();
0163     QCOMPARE(syntaxes.size(), 2);
0164 
0165     QCOMPARE(syntaxes.at(0).exampleQueries().size(), 1);
0166     QCOMPARE(syntaxes.at(0).exampleQueries().constFirst(), QStringLiteral("syntax1"));
0167     QCOMPARE(syntaxes.at(0).description(), QStringLiteral("description1"));
0168     QCOMPARE(syntaxes.at(1).exampleQueries().size(), 1);
0169     QCOMPARE(syntaxes.at(1).exampleQueries().constFirst(), QStringLiteral("syntax2"));
0170     QCOMPARE(syntaxes.at(1).description(), QStringLiteral("description2"));
0171 }
0172 
0173 void DBusRunnerTest::testIconData()
0174 {
0175     startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0176     initProperties();
0177 
0178     const auto matches = launchQuery(QStringLiteral("fooCostomIcon"));
0179     QCOMPARE(matches.count(), 1);
0180     auto result = matches.first();
0181 
0182     QImage expectedIcon(10, 10, QImage::Format_RGBA8888);
0183     expectedIcon.fill(Qt::blue);
0184 
0185     QCOMPARE(result.icon().availableSizes().first(), QSize(10, 10));
0186     QCOMPARE(result.icon().pixmap(QSize(10, 10)), QPixmap::fromImage(expectedIcon));
0187 }
0188 
0189 void DBusRunnerTest::testLifecycleMethods()
0190 {
0191     QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave"), QString()});
0192     manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
0193     auto md = parseMetaDataFromDesktopFile(QFINDTESTDATA("plugins/dbusrunnertestruntimeconfig.desktop"));
0194     manager->loadRunner(md);
0195     QCOMPARE(manager->runners().count(), 1);
0196     // Match session should be set up automatically
0197     launchQuery(QStringLiteral("fooo"));
0198 
0199     // Make sure we got our match, end the match session and give the process a bit of time to get the DBus signal
0200     QTRY_COMPARE_WITH_TIMEOUT(manager->matches().count(), 1, 2000);
0201     manager->matchSessionComplete();
0202     QTest::qWait(500);
0203 
0204     const QStringList lifeCycleSteps = QString::fromLocal8Bit(process->readAllStandardOutput()).split(QLatin1Char('\n'), Qt::SkipEmptyParts);
0205     const QStringList expectedLifeCycleSteps = {
0206         QStringLiteral("Config"),
0207         QStringLiteral("Actions"),
0208         QStringLiteral("Matching:fooo"),
0209         QStringLiteral("Teardown"),
0210     };
0211     QCOMPARE(lifeCycleSteps, expectedLifeCycleSteps);
0212 
0213     // The query does not match our min letter count we set at runtime
0214     launchQuery(QStringLiteral("foo"));
0215     QVERIFY(manager->matches().isEmpty());
0216     // The query does not match our match regex we set at runtime
0217     launchQuery(QStringLiteral("barfoo"));
0218     QVERIFY(manager->matches().isEmpty());
0219 }
0220 
0221 void DBusRunnerTest::testRequestActionsWildcards()
0222 {
0223     initProperties();
0224     manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
0225     auto md = parseMetaDataFromDesktopFile(QFINDTESTDATA("plugins/dbusrunnertestmulti.desktop"));
0226     QVERIFY(md.isValid());
0227     manager->loadRunner(md);
0228     QCOMPARE(manager->runners().count(), 1);
0229 
0230     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a1")}, QStringLiteral("net.krunnertests.multi.a1"));
0231     startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a2")}, QStringLiteral("net.krunnertests.multi.a2"));
0232     const auto matches = launchQuery("foo");
0233     QCOMPARE(matches.count(), 2);
0234 
0235     QCOMPARE(matches.at(0).actions().count(), 1);
0236     QCOMPARE(matches.at(0).actions(), matches.at(1).actions());
0237 }
0238 
0239 QTEST_MAIN(DBusRunnerTest)
0240 
0241 #include "dbusrunnertest.moc"