4b5bde4858
Configurably attach icons as payload to notification packages. By design and due to restrictions on mobile devices *only* png is sent. As KIconLoader preferably returns svg icons from iconPath() we fall back to loading from "hicolor" using KIconTheme directly. Otherwise *many* icons are dropped because of svg format. This also improves slightly the test-case to use a tweaked TestDevice to allow for inspecting sent NetworkPackage-s. REVIEW: 126666
366 lines
14 KiB
C++
366 lines
14 KiB
C++
/**
|
|
* Copyright 2015 Holger Kaelberer <holger.k@elberer.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License or (at your option) version 3 or any later version
|
|
* accepted by the membership of KDE e.V. (or its successor approved
|
|
* by the membership of KDE e.V.), which shall act as a proxy
|
|
* defined in Section 14 of version 3 of the license.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <QSocketNotifier>
|
|
#include <QApplication>
|
|
#include <QNetworkAccessManager>
|
|
#include <QTest>
|
|
#include <QTemporaryFile>
|
|
#include <QStandardPaths>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include "core/daemon.h"
|
|
#include "core/device.h"
|
|
#include "core/kdeconnectplugin.h"
|
|
#include "kdeconnect-version.h"
|
|
#include "plugins/notifications/notificationsplugin.h"
|
|
#include "plugins/notifications/notificationslistener.h"
|
|
#include "plugins/notifications/notificationsdbusinterface.h"
|
|
#include "plugins/notifications/notifyingapplication.h"
|
|
|
|
// Tweaked NotificationsPlugin for testing
|
|
class TestNotificationsPlugin : public NotificationsPlugin
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit TestNotificationsPlugin(QObject *parent, const QVariantList &args)
|
|
: NotificationsPlugin(parent, args)
|
|
{
|
|
}
|
|
|
|
virtual ~TestNotificationsPlugin() {};
|
|
|
|
// allow to access notificationsListener for testing:
|
|
NotificationsListener* getNotificationsListener() const
|
|
{
|
|
return notificationsListener;
|
|
}
|
|
|
|
void setNotificationsListener(NotificationsListener* value)
|
|
{
|
|
notificationsListener = value;
|
|
}
|
|
|
|
NotificationsDbusInterface* getNotificationsDbusInterface() const
|
|
{
|
|
return notificationsDbusInterface;
|
|
}
|
|
};
|
|
|
|
// Tweaked Device for testing:
|
|
class TestDevice: public Device
|
|
{
|
|
Q_OBJECT
|
|
private:
|
|
int sentPackages;
|
|
NetworkPackage *lastPackage;
|
|
|
|
public:
|
|
explicit TestDevice(QObject* parent, const QString& id)
|
|
: Device (parent, id)
|
|
, sentPackages(0)
|
|
, lastPackage(nullptr)
|
|
{}
|
|
|
|
virtual ~TestDevice()
|
|
{
|
|
delete lastPackage;
|
|
}
|
|
|
|
int getSentPackages() const
|
|
{
|
|
return sentPackages;
|
|
}
|
|
|
|
const NetworkPackage* getLastPackage() const
|
|
{
|
|
return lastPackage;
|
|
}
|
|
|
|
public Q_SLOTS:
|
|
virtual bool sendPackage(NetworkPackage& np) override
|
|
{
|
|
++sentPackages;
|
|
// copy package manually to allow for inspection (can't use copy-constructor)
|
|
delete lastPackage;
|
|
lastPackage = new NetworkPackage(np.type());
|
|
Q_ASSERT(lastPackage);
|
|
for (QVariantMap::ConstIterator iter = np.body().constBegin();
|
|
iter != np.body().constEnd(); iter++)
|
|
lastPackage->set(iter.key(), iter.value());
|
|
lastPackage->setPayload(np.payload(), np.payloadSize());
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// Tweaked NotificationsListener for testing:
|
|
class TestedNotificationsListener: public NotificationsListener
|
|
{
|
|
|
|
public:
|
|
explicit TestedNotificationsListener(KdeConnectPlugin* aPlugin,
|
|
NotificationsDbusInterface* aDbusInterface)
|
|
: NotificationsListener(aPlugin, aDbusInterface)
|
|
{}
|
|
|
|
virtual ~TestedNotificationsListener()
|
|
{}
|
|
|
|
QHash<QString, NotifyingApplication>& getApplications()
|
|
{
|
|
return applications;
|
|
}
|
|
|
|
void setApplications(const QHash<QString, NotifyingApplication>& value)
|
|
{
|
|
applications = value;
|
|
}
|
|
|
|
};
|
|
|
|
class TestNotificationListener : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
TestNotificationListener()
|
|
: plugin(nullptr)
|
|
{
|
|
QStandardPaths::setTestModeEnabled(true);
|
|
}
|
|
|
|
private Q_SLOTS:
|
|
void testNotify();
|
|
|
|
private:
|
|
TestNotificationsPlugin* plugin;
|
|
};
|
|
|
|
void TestNotificationListener::testNotify()
|
|
{
|
|
//
|
|
// set things up:
|
|
//
|
|
|
|
QString dId("testid");
|
|
TestDevice *d = new TestDevice(nullptr, dId); // not setting any parent or we will double free the dbusInterface
|
|
|
|
int proxiedNotifications = 0;
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
plugin = new TestNotificationsPlugin(this,
|
|
QVariantList({ QVariant::fromValue<Device*>(d),
|
|
"notifications_plugin",
|
|
{"kdeconnect.notification"}}));
|
|
QVERIFY(plugin->getNotificationsListener());
|
|
delete plugin->getNotificationsListener();
|
|
|
|
// inject our tweaked NotificationsListener:
|
|
TestedNotificationsListener* listener = new TestedNotificationsListener(plugin, plugin->getNotificationsDbusInterface());
|
|
QVERIFY(listener);
|
|
plugin->setNotificationsListener(listener);
|
|
QCOMPARE(listener, plugin->getNotificationsListener());
|
|
|
|
// make sure config is default:
|
|
plugin->config()->set("generalPersistent", false);
|
|
plugin->config()->set("generalIncludeBody", true);
|
|
plugin->config()->set("generalUrgency", 0);
|
|
QCOMPARE(plugin->config()->get<bool>("generalPersistent"), false);
|
|
QCOMPARE(plugin->config()->get<bool>("generalIncludeBody"), true);
|
|
QCOMPARE(plugin->config()->get<bool>("generalUrgency"), false);
|
|
|
|
// applications are modified directly:
|
|
listener->getApplications().clear();
|
|
QCOMPARE(listener->getApplications().count(), 0);
|
|
|
|
//
|
|
// Go !!!
|
|
//
|
|
|
|
uint replacesId = 99;
|
|
uint retId;
|
|
QString appName("some-appName");
|
|
QString body("some-body");
|
|
QString icon("some-icon");
|
|
QString summary("some-summary");
|
|
|
|
// regular Notify call that is synchronized ...
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0);
|
|
// ... should return replacesId,
|
|
QCOMPARE(retId, replacesId);
|
|
// ... have triggered sending a package
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
// ... with our properties,
|
|
QCOMPARE(d->getLastPackage()->get<uint>("id"), replacesId);
|
|
QCOMPARE(d->getLastPackage()->get<QString>("appName"), appName);
|
|
QCOMPARE(d->getLastPackage()->get<QString>("ticker"), summary + ": " + body);
|
|
QCOMPARE(d->getLastPackage()->get<bool>("isClearable"), true);
|
|
QCOMPARE(d->getLastPackage()->hasPayload(), false);
|
|
|
|
// ... and create a new application internally that is initialized correctly:
|
|
QCOMPARE(listener->getApplications().count(), 1);
|
|
QVERIFY(listener->getApplications().contains(appName));
|
|
QVERIFY(listener->getApplications()[appName].active);
|
|
QCOMPARE(listener->getApplications()[appName].name, appName);
|
|
QVERIFY(listener->getApplications()[appName].blacklistExpression.pattern().isEmpty());
|
|
QCOMPARE(listener->getApplications()[appName].name, appName);
|
|
QCOMPARE(listener->getApplications()[appName].icon, icon);
|
|
|
|
// another one, with other timeout and urgency values:
|
|
QString appName2("some-appName2");
|
|
QString body2("some-body2");
|
|
QString icon2("some-icon2");
|
|
QString summary2("some-summary2");
|
|
|
|
retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{"urgency", 2}}, 10);
|
|
QCOMPARE(retId, replacesId+1);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
QCOMPARE(d->getLastPackage()->get<uint>("id"), replacesId+1);
|
|
QCOMPARE(d->getLastPackage()->get<QString>("appName"), appName2);
|
|
QCOMPARE(d->getLastPackage()->get<QString>("ticker"), summary2 + ": " + body2);
|
|
QCOMPARE(d->getLastPackage()->get<bool>("isClearable"), false); // timeout != 0
|
|
QCOMPARE(d->getLastPackage()->hasPayload(), false);
|
|
QCOMPARE(listener->getApplications().count(), 2);
|
|
QVERIFY(listener->getApplications().contains(appName2));
|
|
QVERIFY(listener->getApplications().contains(appName));
|
|
|
|
// if persistent-only is set, timeouts > 0 are not synced:
|
|
plugin->config()->set("generalPersistent", true);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 1);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
retId = listener->Notify(appName2, replacesId, icon2, summary2, body2, {}, {{}}, 3);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
// but timeout == 0 is
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
plugin->config()->set("generalPersistent", false);
|
|
|
|
// if min-urgency is set, lower urgency levels are not synced:
|
|
plugin->config()->set("generalUrgency", 1);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 0}}, 0);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
// equal urgency is
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 1}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
// higher urgency as well
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 2}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
plugin->config()->set("generalUrgency", 0);
|
|
|
|
// notifications for a deactivated application are not synced:
|
|
QVERIFY(listener->getApplications().contains(appName));
|
|
listener->getApplications()[appName].active = false;
|
|
QVERIFY(!listener->getApplications()[appName].active);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 0}}, 0);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
// others are still:
|
|
retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId+1);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
// back to normal:
|
|
listener->getApplications()[appName].active = true;
|
|
QVERIFY(listener->getApplications()[appName].active);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
|
|
// notifications with blacklisted subjects are not synced:
|
|
QVERIFY(listener->getApplications().contains(appName));
|
|
listener->getApplications()[appName].blacklistExpression.setPattern("black[12]|foo(bar|baz)");
|
|
retId = listener->Notify(appName, replacesId, icon, "summary black1", body, {}, {{}}, 0);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
retId = listener->Notify(appName, replacesId, icon, "summary foobar", body, {}, {{}}, 0);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
// other subjects are synced:
|
|
retId = listener->Notify(appName, replacesId, icon, "summary foo", body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
retId = listener->Notify(appName, replacesId, icon, "summary black3", body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
// also body is checked by blacklist if requested:
|
|
plugin->config()->set("generalIncludeBody", true);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, "body black1", {}, {{}}, 0);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
retId = listener->Notify(appName, replacesId, icon, summary, "body foobaz", {}, {{}}, 0);
|
|
QCOMPARE(retId, 0U);
|
|
QCOMPARE(proxiedNotifications, d->getSentPackages());
|
|
// body does not matter if inclusion was not requested:
|
|
plugin->config()->set("generalIncludeBody", false);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, "body black1", {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
// without body, also ticker value is different:
|
|
QCOMPARE(d->getLastPackage()->get<QString>("ticker"), summary);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, "body foobaz", {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
|
|
// back to normal:
|
|
listener->getApplications()[appName].blacklistExpression.setPattern("");
|
|
plugin->config()->set("generalIncludeBody", true);
|
|
retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
retId = listener->Notify(appName2, replacesId, icon2, summary2, body2, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
|
|
// icon synchronization:
|
|
int count = 0;
|
|
for (const auto& iconName: KIconLoader::global()->queryIcons(-KIconLoader::SizeEnormous, KIconLoader::Application)) {
|
|
if (!iconName.endsWith(".png"))
|
|
continue;
|
|
if (count++ > 3) // max 3 iterations
|
|
break;
|
|
|
|
// existing icons are sync-ed if requested
|
|
plugin->config()->set("generalSynchronizeIcons", true);
|
|
QFileInfo fi(iconName);
|
|
//qDebug() << "XXX" << iconName << fi.baseName() << fi.size();
|
|
retId = listener->Notify(appName, replacesId, fi.baseName(), summary, body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
QVERIFY(d->getLastPackage()->hasPayload());
|
|
QCOMPARE(d->getLastPackage()->payloadSize(), fi.size());
|
|
|
|
// otherwise no payload:
|
|
plugin->config()->set("generalSynchronizeIcons", false);
|
|
retId = listener->Notify(appName, replacesId, fi.baseName(), summary, body, {}, {{}}, 0);
|
|
QCOMPARE(retId, replacesId);
|
|
QCOMPARE(++proxiedNotifications, d->getSentPackages());
|
|
QVERIFY(!d->getLastPackage()->hasPayload());
|
|
QCOMPARE(d->getLastPackage()->payloadSize(), 0);
|
|
}
|
|
}
|
|
|
|
|
|
QTEST_GUILESS_MAIN(TestNotificationListener);
|
|
|
|
#include "testnotificationlistener.moc"
|