Verified Commit e48d618d authored by Alexander Lohnau's avatar Alexander Lohnau 💬
Browse files

dbusrunner: Add Config method to set config at runtime

parent 506c40ac
......@@ -241,22 +241,32 @@ void DBusRunnerTest::testIconData()
void DBusRunnerTest::testLifecycleMethods()
{
QProcess *process = startDBusRunnerProcess({QStringLiteral("net.krunnertests.dave"), QString()});
initProperties();
manager.reset(new RunnerManager()); // This case is special, because we want to load the runners manually
auto md = KPluginMetaData::fromDesktopFile(QFINDTESTDATA("dbusrunnertestruntimeconfig.desktop"), {QStringLiteral("plasma-runner.desktop")});
manager->loadRunner(md);
QCOMPARE(manager->runners().count(), 1);
// Match session should be set up automatically
launchQuery(QStringLiteral("foo"));
manager->matchSessionComplete();
launchQuery(QStringLiteral("fooo"));
// Wait for async D-Bus method to be called
QEventLoop loop;
QTimer::singleShot(1000, &loop, [&loop]() {
loop.quit();
});
loop.exec();
// Make sure we got our match, end the match session and give the process a bit of time to get the DBus signal
QTRY_COMPARE_WITH_TIMEOUT(manager->matches().count(), 1, 2000);
manager->matchSessionComplete();
QTest::qWait(500);
QCOMPARE(manager->matches().count(), 1);
const QStringList lifeCycleSteps = QString::fromLocal8Bit(process->readAllStandardOutput()).split(QLatin1Char('\n'), Qt::SkipEmptyParts);
const QStringList expectedLifeCycleSteps = {QStringLiteral("Matching:foo"), QStringLiteral("Teardown")};
const QStringList expectedLifeCycleSteps = {
QStringLiteral("Config"),
QStringLiteral("Matching:fooo"),
QStringLiteral("Teardown"),
};
QCOMPARE(lifeCycleSteps, expectedLifeCycleSteps);
// The query does not match our min letter count we set at runtime
launchQuery(QStringLiteral("foo"));
QVERIFY(manager->matches().isEmpty());
// The query does not match our match regex we set at runtime
launchQuery(QStringLiteral("barfoo"));
QVERIFY(manager->matches().isEmpty());
}
void DBusRunnerTest::testRequestActionsOnceWildcards()
......@@ -277,6 +287,7 @@ void DBusRunnerTest::testRequestActionsOnceWildcards()
// We have started the process later and the actions should now be fetched when the match session is started
startDBusRunnerProcess({QStringLiteral("net.krunnertests.multi.a1")}, QStringLiteral("net.krunnertests.multi.a1"));
QTest::qWait(500); // Wait a bit for the runner to pick up the new service
launchQuery("fooo");
QVERIFY(!manager->matches().isEmpty());
......
......@@ -19,4 +19,3 @@ X-Plasma-Runner-Min-Letter-Count=3
X-Plasma-Runner-Match-Regex=^fo
X-Plasma-Runner-Syntaxes=syntax1,syntax2
X-Plasma-Runner-Syntax-Descriptions=description1,description2
X-Plasma-Runner-Lifecycle-Methods=true
[Desktop Entry]
Name=DBus runner test
Comment=DBus runner test
X-KDE-ServiceTypes=Plasma/Runner
Type=Service
Icon=internet-web-browser
X-KDE-PluginInfo-Author=Some Developer
X-KDE-PluginInfo-Email=kde@example.com
X-KDE-PluginInfo-Name=dbusrunnertest
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Plasma-API=DBus
X-Plasma-DBusRunner-Service=net.krunnertests.dave
X-Plasma-DBusRunner-Path=/dave
X-Plasma-Request-Actions-Once=true
X-Plasma-Runner-Syntaxes=syntax1,syntax2
X-Plasma-Runner-Syntax-Descriptions=description1,description2
X-Plasma-Runner-Lifecycle-Methods=true
......@@ -99,6 +99,19 @@ void TestRemoteRunner::Teardown()
}
}
QVariantMap TestRemoteRunner::Config()
{
if (m_showLifecycleMethodCalls) {
std::cout << "Config" << std::endl;
std::cout.flush();
}
return {
{"X-Plasma-Runner-Match-Regex", "^fo"},
{"X-Plasma-Runner-Min-Letter-Count", 4},
};
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
......
......@@ -2,6 +2,7 @@
#include "../src/dbusutils_p.h"
#include <QObject>
#include <QVariantMap>
class TestRemoteRunner : public QObject
{
......@@ -14,6 +15,7 @@ public Q_SLOTS:
RemoteMatches Match(const QString &searchTerm);
void Run(const QString &id, const QString &actionId);
void Teardown();
QVariantMap Config();
private:
bool m_showLifecycleMethodCalls = false;
......
......@@ -7,6 +7,18 @@
-->
<method name="Teardown"/>
<!--
This method can be used to set runner config at runtime. In case the service wildcard is used
the config is only for one service requested.
It gets only called when the X-Plasma-Runner-Lifecycle-Methods method is explicitly set to true.
This method is called before Prepare and before the matching is started.
-->
<method name="Config">
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap" />
<arg name="config" type="{sv}" direction="out">
</arg>
</method>
<!--
Returns a list of actions supported by this runner.
For example, a song match returned by a music player runner can be queued, added to the playlist, or played.
......
......@@ -95,6 +95,15 @@ DBusRunner::DBusRunner(const KPluginMetaData &pluginMetaData, QObject *parent)
DBusRunner::~DBusRunner() = default;
void DBusRunner::reloadConfiguration()
{
// If we have already loaded a config, but the runner is told to reload it's config
if (m_callLifecycleMethods) {
suspendMatching(true);
requestConfig();
}
}
void DBusRunner::teardown()
{
if (m_callLifecycleMethods && m_matchWasCalled) {
......@@ -103,6 +112,7 @@ void DBusRunner::teardown()
QDBusConnection::sessionBus().asyncCall(method);
}
}
m_actionsForSessionRequested = false;
m_matchWasCalled = false;
}
......@@ -126,7 +136,6 @@ void DBusRunner::requestActions()
auto getActionsMethod = QDBusMessage::createMethodCall(service, m_path, QStringLiteral(IFACE_NAME), QStringLiteral("Actions"));
QDBusPendingReply<RemoteActions> reply = QDBusConnection::sessionBus().call(getActionsMethod);
reply.waitForFinished();
if (!reply.isValid()) {
qCDebug(KRUNNER) << "Error requesting actions; calling" << service << " :" << reply.error().name() << reply.error().message();
return;
......@@ -140,6 +149,35 @@ void DBusRunner::requestActions()
}
}
void DBusRunner::requestConfig()
{
const QString service = *m_matchingServices.constBegin();
auto getConfigMethod = QDBusMessage::createMethodCall(service, m_path, QStringLiteral(IFACE_NAME), QStringLiteral("Config"));
QDBusPendingReply<QVariantMap> reply = QDBusConnection::sessionBus().asyncCall(getConfigMethod);
auto watcher = new QDBusPendingCallWatcher(reply);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, service]() {
watcher->deleteLater();
QDBusReply<QVariantMap> reply = *watcher;
if (!reply.isValid()) {
suspendMatching(false);
qCDebug(KRUNNER) << "Error requesting config; calling" << service << " :" << reply.error().name() << reply.error().message();
return;
}
const QVariantMap config = reply.value();
for (auto it = config.cbegin(), end = config.cend(); it != end; ++it) {
if (it.key() == QLatin1String("X-Plasma-Runner-Match-Regex")) {
QRegularExpression regex(it.value().toString());
regex.optimize();
setMatchRegex(regex);
} else if (it.key() == QLatin1String("X-Plasma-Runner-Min-Letter-Count")) {
setMinLetterCount(it.value().toInt());
}
}
suspendMatching(false);
});
}
void DBusRunner::match(Plasma::RunnerContext &context)
{
QSet<QString> services;
......@@ -150,8 +188,9 @@ void DBusRunner::match(Plasma::RunnerContext &context)
// Request the actions
if ((m_requestActionsOnce && !m_actionsOnceRequested) // We only want to fetch the actions once but haven't done so yet
|| (!m_requestActionsOnce)) { // We want to fetch the actions for each match session
|| (!m_actionsForSessionRequested)) { // We want to fetch the actions for each match session
m_actionsOnceRequested = true;
m_actionsForSessionRequested = true;
QMetaObject::invokeMethod(this, "requestActions");
}
}
......
......@@ -25,6 +25,7 @@ public:
~DBusRunner() override;
void match(Plasma::RunnerContext &context) override;
void reloadConfiguration() override;
void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action) override;
QList<QAction *> actionsForMatch(const Plasma::QueryMatch &match) override;
......@@ -33,6 +34,7 @@ public Q_SLOTS:
void requestActions();
private:
void requestConfig();
void setActions(const RemoteActions &remoteActions);
static QImage decodeImage(const RemoteImage &remoteImage);
QMutex m_mutex; // needed round any variable also accessed from Match
......@@ -42,6 +44,7 @@ private:
bool m_hasUniqueResults = false;
bool m_requestActionsOnce = false;
bool m_actionsOnceRequested = false;
bool m_actionsForSessionRequested = false;
bool m_matchWasCalled = false;
bool m_callLifecycleMethods = false;
QSet<QString> m_requestedActionServices;
......
......@@ -379,12 +379,16 @@ public:
void runnerMatchingSuspended(bool suspended)
{
if (suspended || !prepped || teardownRequested) {
auto *runner = qobject_cast<AbstractRunner *>(q->sender());
if (suspended || !prepped || teardownRequested || !runner) {
return;
}
if (auto *runner = qobject_cast<AbstractRunner *>(q->sender())) {
startJob(runner);
const QString query = context.query();
if (singleMode || runner->minLetterCount() <= query.size()) {
if (singleMode || !runner->hasMatchRegex() || runner->matchRegex().match(query).hasMatch()) {
startJob(runner);
}
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment