File indexing completed on 2024-04-14 03:54:30

0001 /*
0002     SPDX-FileCopyrightText: 2022 Eduardo Cruz <eduardo.cruz@kdemail.net>
0003     SPDX-License-Identifier: LGPL-2.1-or-later
0004 */
0005 
0006 #include "runnermanager.h"
0007 
0008 #include <KSharedConfig>
0009 #include <QCoreApplication>
0010 #include <QObject>
0011 #include <QProcess>
0012 #include <QStandardPaths>
0013 #include <QTest>
0014 
0015 #include "abstractrunnertest.h"
0016 #include "kpluginmetadata_utils_p.h"
0017 
0018 Q_DECLARE_METATYPE(KRunner::QueryMatch)
0019 Q_DECLARE_METATYPE(QList<KRunner::QueryMatch>)
0020 
0021 using namespace KRunner;
0022 namespace KRunner
0023 {
0024 extern int __changeCountBeforeSaving;
0025 }
0026 
0027 class RunnerManagerTest : public AbstractRunnerTest
0028 {
0029     Q_OBJECT
0030 private Q_SLOTS:
0031     void initTestCase()
0032     {
0033         __changeCountBeforeSaving = 1;
0034         startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave")});
0035         qputenv("XDG_DATA_DIRS", QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation).toLocal8Bit());
0036         QCoreApplication::setLibraryPaths(QStringList());
0037         initProperties();
0038         qRegisterMetaType<QList<KRunner::QueryMatch>>();
0039     }
0040 
0041     void cleanupTestCase()
0042     {
0043         killRunningDBusProcesses();
0044     }
0045 
0046     /**
0047      * This will test the mechanismm that stalls for 250ms before emiting any result in RunnerManager::scheduleMatchesChanged()
0048      * and the mechanism that anticipates the last results emission in RunnerManager::jobDone().
0049      */
0050     void testScheduleMatchesChanged()
0051     {
0052         QSignalSpy spyQueryFinished(manager.get(), &KRunner::RunnerManager::queryFinished);
0053         QSignalSpy spyMatchesChanged(manager.get(), &KRunner::RunnerManager::matchesChanged);
0054 
0055         QVERIFY(spyQueryFinished.isValid());
0056         QVERIFY(spyMatchesChanged.isValid());
0057 
0058         QCOMPARE(spyQueryFinished.count(), 0);
0059 
0060         // This will track the total execution time
0061         QElapsedTimer timer;
0062         timer.start();
0063 
0064         // This special string will simulate a 300ms delay
0065         manager->launchQuery("fooDelay300");
0066 
0067         // However not yet a matcheschanged, it should be stalled for 250ms
0068         QCOMPARE(spyMatchesChanged.count(), 0);
0069 
0070         // After 250ms it will emit with empty matches, we wait for that.
0071         // We can't put a low upper limit on these wait() calls because the CI environment can be slow.
0072         QVERIFY(spyMatchesChanged.wait()); // This should take just a tad longer than 250ms.
0073 
0074         // This should have taken no less than 250ms. It waits for 250s before "giving up" and emitting an empty matches list.
0075         QVERIFY(timer.elapsed() >= 250);
0076         QCOMPARE(spyMatchesChanged.count(), 1);
0077         QCOMPARE(manager->matches().count(), 0); // This is the empty matches "reset" emission, result is not ready yet
0078         QCOMPARE(spyQueryFinished.count(), 0); // Still the same, query is not done
0079 
0080         // We programmed it to emit the result after 300ms, so we need to wait 50ms more for the next emission
0081         QVERIFY(spyQueryFinished.wait());
0082 
0083         // This should have taken at least 300ms total, as we requested via the special query string
0084         QVERIFY(timer.elapsed() >= 300);
0085 
0086         // At this point RunnerManager::jobDone() should have anticipated the final emission.
0087         QCOMPARE(manager->matches().count(), 1); // The result is here
0088         QCOMPARE(spyQueryFinished.count(), 1); // Will have emited queryFinished, job is done
0089         QCOMPARE(spyMatchesChanged.count(), 2); // We had the second matchesChanged emission, now with the query result
0090 
0091         // Now we will make sure that RunnerManager::scheduleMatchesChanged() emits matchesChanged instantly
0092         // if we start a query with an empty string. It will never produce results, stalling is meaningless
0093         manager->launchQuery("");
0094         QCOMPARE(spyMatchesChanged.count(), 3); // One more, instantly, without stall
0095         QCOMPARE(manager->matches().count(), 0); // Empty results for empty query string
0096         QVERIFY(spyQueryFinished.wait());
0097     }
0098 
0099     /**
0100      * This will test queryFinished signal from reset() is emitted when the previous runners are
0101      * still running.
0102      */
0103     void testQueryFinishedFromReset()
0104     {
0105         QSignalSpy spyQueryFinished(manager.get(), &KRunner::RunnerManager::queryFinished);
0106 
0107         manager->launchQuery("fooDelay1000");
0108         QTest::qSleep(500);
0109         QCOMPARE(spyQueryFinished.size(), 0);
0110 
0111         manager->launchQuery("fooDelay300");
0112         QCOMPARE(spyQueryFinished.size(), 1); // From reset()
0113 
0114         QVERIFY(spyQueryFinished.wait());
0115         QCOMPARE(spyQueryFinished.size(), 2);
0116     }
0117 
0118     /**
0119      * When we delete the RunnerManager while a job is still running, we should not crash
0120      */
0121     void testNotCrashWhenDeletingRunnerManager()
0122     {
0123         RunnerManager manager;
0124         manager.setAllowedRunners({QStringLiteral("fakerunnerplugin")});
0125         manager.loadRunner(KPluginMetaData::findPluginById(QStringLiteral("krunnertest"), QStringLiteral("fakerunnerplugin")));
0126 
0127         QCOMPARE(manager.runners().size(), 1);
0128 
0129         manager.launchQuery("somequery");
0130     }
0131 
0132     void testRunnerManagerStateGroups()
0133     {
0134         auto stateGrp = KSharedConfig::openConfig(QString(), KConfig::NoGlobals)->group("Testme");
0135         auto configGrp = KSharedConfig::openConfig(QString(), KConfig::NoGlobals)->group("Plugins");
0136         stateGrp.deleteGroup();
0137         RunnerManager manager(configGrp, stateGrp, this);
0138         manager.setAllowedRunners({QStringLiteral("fakerunnerplugin")});
0139         manager.loadRunner(KPluginMetaData::findPluginById(QStringLiteral("krunnertest"), QStringLiteral("fakerunnerplugin")));
0140         QSignalSpy spyQueryFinished(&manager, &KRunner::RunnerManager::queryFinished);
0141 
0142         manager.launchQuery("foo");
0143         spyQueryFinished.wait();
0144         manager.run(manager.matches().constFirst());
0145         manager.matchSessionComplete();
0146         QCOMPARE(stateGrp.readEntry("LaunchCounts"), "1 foo");
0147         QCOMPARE(stateGrp.config()->groupList().size(), 1);
0148     }
0149 
0150     void testRunnerSuspendWhileReloadingConfig()
0151     {
0152         RunnerManager manager;
0153         manager.loadRunner(KPluginMetaData::findPluginById(QStringLiteral("krunnertest2"), QStringLiteral("suspendedrunnerplugin")));
0154         QCOMPARE(manager.runners().size(), 1);
0155 
0156         AbstractRunner *runner = manager.runners().constFirst();
0157         QVERIFY(runner->isMatchingSuspended());
0158 
0159         QSignalSpy spy(&manager, &KRunner::RunnerManager::queryFinished);
0160         manager.launchQuery("foo");
0161         QVERIFY2(spy.wait(), "RunnerManager did not emit the queryFinished signal");
0162 
0163         QCOMPARE(manager.matches().size(), 1);
0164 
0165         QVERIFY(!runner->isMatchingSuspended());
0166     }
0167 
0168     void testAbstractRunnerTestTimeout()
0169     {
0170         QEXPECT_FAIL("", "This test is expected to fail", Continue);
0171         const auto matches = launchQuery("fooDelay6000");
0172         QVERIFY(matches.isEmpty());
0173     }
0174 };
0175 
0176 QTEST_MAIN(RunnerManagerTest)
0177 
0178 #include "runnermanagertest.moc"