diff --git a/core/device.h b/core/device.h index 15fd73ea..699a8b06 100644 --- a/core/device.h +++ b/core/device.h @@ -1,154 +1,154 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #ifndef DEVICE_H #define DEVICE_H #include #include #include #include "networkpacket.h" #include "backends/devicelink.h" class DeviceLink; class KdeConnectPlugin; class KDECONNECTCORE_EXPORT Device : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device") Q_PROPERTY(QString type READ type CONSTANT) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString iconName READ iconName CONSTANT) Q_PROPERTY(QString statusIconName READ statusIconName) Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged) Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChanged) Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged) Q_PROPERTY(bool hasPairingRequests READ hasPairingRequests NOTIFY hasPairingRequestsChanged) public: enum DeviceType { Unknown, Desktop, Laptop, Phone, Tablet, Tv, }; /** * Restores the @p device from the saved configuration * * We already know it but we need to wait for an incoming DeviceLink to communicate */ Device(QObject* parent, const QString& id); /** * Device known via an incoming connection sent to us via a devicelink. * * We know everything but we don't trust it yet */ Device(QObject* parent, const NetworkPacket& np, DeviceLink* dl); ~Device() override; QString id() const; QString name() const; QString dbusPath() const { return "/modules/kdeconnect/devices/"+id(); } QString type() const; QString iconName() const; QString statusIconName() const; Q_SCRIPTABLE QString encryptionInfo() const; //Add and remove links void addLink(const NetworkPacket& identityPacket, DeviceLink*); void removeLink(DeviceLink*); Q_SCRIPTABLE bool isTrusted() const; Q_SCRIPTABLE QStringList availableLinks() const; - bool isReachable() const; + virtual bool isReachable() const; Q_SCRIPTABLE QStringList loadedPlugins() const; Q_SCRIPTABLE bool hasPlugin(const QString& name) const; Q_SCRIPTABLE QString pluginsConfigFile() const; KdeConnectPlugin* plugin(const QString& pluginName) const; Q_SCRIPTABLE void setPluginEnabled(const QString& pluginName, bool enabled); Q_SCRIPTABLE bool isPluginEnabled(const QString& pluginName) const; void cleanUnneededLinks(); int protocolVersion(); QStringList supportedPlugins() const; QHostAddress getLocalIpAddress() const; public Q_SLOTS: ///sends a @p np packet to the device ///virtual for testing purposes. virtual bool sendPacket(NetworkPacket& np); //Dbus operations public Q_SLOTS: Q_SCRIPTABLE void requestPair(); //to all links Q_SCRIPTABLE void unpair(); //from all links Q_SCRIPTABLE void reloadPlugins(); //from kconf Q_SCRIPTABLE void acceptPairing(); Q_SCRIPTABLE void rejectPairing(); Q_SCRIPTABLE bool hasPairingRequests() const; Q_SCRIPTABLE QString pluginIconName(const QString& pluginName); private Q_SLOTS: void privateReceivedPacket(const NetworkPacket& np); void linkDestroyed(QObject* o); void pairStatusChanged(DeviceLink::PairStatus current); void addPairingRequest(PairingHandler* handler); void removePairingRequest(PairingHandler* handler); Q_SIGNALS: Q_SCRIPTABLE void pluginsChanged(); Q_SCRIPTABLE void reachableChanged(bool reachable); Q_SCRIPTABLE void trustedChanged(bool trusted); Q_SCRIPTABLE void pairingError(const QString& error); Q_SCRIPTABLE void nameChanged(const QString& name); Q_SCRIPTABLE void hasPairingRequestsChanged(bool hasPairingRequests); private: //Methods static DeviceType str2type(const QString& deviceType); static QString type2str(DeviceType deviceType); void setName(const QString& name); QString iconForStatus(bool reachable, bool paired) const; private: class DevicePrivate; DevicePrivate *d; }; Q_DECLARE_METATYPE(Device*) #endif // DEVICE_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 87dfd755..cf385223 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,42 +1,43 @@ find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Test) include_directories( ${KDEConnectCore_BINARY_DIR} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/plugins/sendnotifications/ ${CMAKE_BINARY_DIR}/smsapp/ ) set(kdeconnect_libraries kdeconnectcore KF5::I18n KF5::KIOWidgets Qt5::DBus Qt5::Network Qt5::Test qca-qt5 ) set(kdeconnect_sms_libraries kdeconnectsmshelper Qt5::Test ) ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(networkpackettests.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(testsocketlinereader.cpp TEST_NAME testsocketlinereader LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(testsslsocketlinereader.cpp TEST_NAME testsslsocketlinereader LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(kdeconnectconfigtest.cpp TEST_NAME kdeconnectconfigtest LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(lanlinkprovidertest.cpp TEST_NAME lanlinkprovidertest LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(devicetest.cpp TEST_NAME devicetest LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(testnotificationlistener.cpp + testdevice.cpp ../plugins/sendnotifications/sendnotificationsplugin.cpp ../plugins/sendnotifications/notificationslistener.cpp ../plugins/sendnotifications/notifyingapplication.cpp TEST_NAME testnotificationlistener LINK_LIBRARIES ${kdeconnect_libraries} Qt5::DBus KF5::Notifications KF5::IconThemes) if(SMSAPP_ENABLED) ecm_add_test(testsmshelper.cpp LINK_LIBRARIES ${kdeconnect_sms_libraries}) endif() diff --git a/tests/testdevice.cpp b/tests/testdevice.cpp new file mode 100644 index 00000000..5aab05e2 --- /dev/null +++ b/tests/testdevice.cpp @@ -0,0 +1,52 @@ +/** + * Copyright 2015 Holger Kaelberer + * Copyright 2019 Simon Redman + * + * 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 . + */ + +#include "testdevice.h" + +TestDevice::TestDevice(QObject* parent, const QString& id) + : Device (parent, id) + , sentPackets(0) + , lastPacket(nullptr) +{} + +TestDevice::~TestDevice() +{ + delete lastPacket; +} + +bool TestDevice::isReachable() const +{ + return true; +} + +bool TestDevice::sendPacket(NetworkPacket& np) +{ + ++sentPackets; + // copy packet manually to allow for inspection (can't use copy-constructor) + deleteLastPacket(); + lastPacket = new NetworkPacket(np.type()); + Q_ASSERT(lastPacket); + for (QVariantMap::ConstIterator iter = np.body().constBegin(); + iter != np.body().constEnd(); iter++) + lastPacket->set(iter.key(), iter.value()); + lastPacket->setPayload(np.payload(), np.payloadSize()); + return true; +} diff --git a/tests/testdevice.h b/tests/testdevice.h new file mode 100644 index 00000000..ebdd525d --- /dev/null +++ b/tests/testdevice.h @@ -0,0 +1,60 @@ +/** + * Copyright 2015 Holger Kaelberer + * Copyright 2019 Simon Redman + * + * 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 . + */ + +#include +#include "core/device.h" + +// Tweaked Device for testing: +class TestDevice: public Device +{ + Q_OBJECT +private: + int sentPackets; + NetworkPacket* lastPacket; + +public: + explicit TestDevice(QObject* parent, const QString& id); + + ~TestDevice() override; + + bool isReachable() const override; + + int getSentPackets() const + { + return sentPackets; + } + + NetworkPacket* getLastPacket() + { + return lastPacket; + } + +private: + void deleteLastPacket() + { + delete lastPacket; + lastPacket = nullptr; + } + +public Q_SLOTS: + bool sendPacket(NetworkPacket& np) override; + +}; diff --git a/tests/testnotificationlistener.cpp b/tests/testnotificationlistener.cpp index 211a3923..4897df6a 100644 --- a/tests/testnotificationlistener.cpp +++ b/tests/testnotificationlistener.cpp @@ -1,507 +1,456 @@ /** * Copyright 2015 Holger Kaelberer * * 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 . */ #include #include #include #include #include #include #include #include #include #include "core/daemon.h" #include "core/device.h" +#include "testdevice.h" #include "core/kdeconnectplugin.h" #include "kdeconnect-version.h" #include "plugins/sendnotifications/sendnotificationsplugin.h" #include "plugins/sendnotifications/notificationslistener.h" #include "plugins/sendnotifications/notifyingapplication.h" // Tweaked NotificationsPlugin for testing class TestNotificationsPlugin : public SendNotificationsPlugin { Q_OBJECT public: explicit TestNotificationsPlugin(QObject* parent, const QVariantList& args) : SendNotificationsPlugin(parent, args) { } ~TestNotificationsPlugin() override = default; // allow to access notificationsListener for testing: NotificationsListener* getNotificationsListener() const { return notificationsListener; } void setNotificationsListener(NotificationsListener* value) { notificationsListener = value; } }; -// Tweaked Device for testing: -class TestDevice: public Device -{ - Q_OBJECT -private: - int sentPackets; - NetworkPacket* lastPacket; - -public: - explicit TestDevice(QObject* parent, const QString& id) - : Device (parent, id) - , sentPackets(0) - , lastPacket(nullptr) - {} - - ~TestDevice() override - { - delete lastPacket; - } - - int getSentPackets() const - { - return sentPackets; - } - - NetworkPacket* getLastPacket() - { - return lastPacket; - } - - void deleteLastPacket() - { - delete lastPacket; - lastPacket = nullptr; - } - -public Q_SLOTS: - bool sendPacket(NetworkPacket& np) override - { - ++sentPackets; - // copy packet manually to allow for inspection (can't use copy-constructor) - deleteLastPacket(); - lastPacket = new NetworkPacket(np.type()); - Q_ASSERT(lastPacket); - for (QVariantMap::ConstIterator iter = np.body().constBegin(); - iter != np.body().constEnd(); iter++) - lastPacket->set(iter.key(), iter.value()); - lastPacket->setPayload(np.payload(), np.payloadSize()); - return true; - } -}; - // Tweaked NotificationsListener for testing: class TestedNotificationsListener: public NotificationsListener { Q_OBJECT public: explicit TestedNotificationsListener(KdeConnectPlugin* aPlugin) : NotificationsListener(aPlugin) {} ~TestedNotificationsListener() override {} QHash& getApplications() { return m_applications; } void setApplications(const QHash& value) { m_applications = value; } QSharedPointer iconForIconName(const QString& iconName) const { return NotificationsListener::iconForIconName(iconName); } protected: bool parseImageDataArgument(const QVariant& argument, int& width, int& height, int& rowStride, int& bitsPerSample, int& channels, bool& hasAlpha, QByteArray& imageData) const override { width = argument.toMap().value(QStringLiteral("width")).toInt(); height = argument.toMap().value(QStringLiteral("height")).toInt(); rowStride = argument.toMap().value(QStringLiteral("rowStride")).toInt(); bitsPerSample = argument.toMap().value(QStringLiteral("bitsPerSample")).toInt(); channels = argument.toMap().value(QStringLiteral("channels")).toInt(); hasAlpha = argument.toMap().value(QStringLiteral("hasAlpha")).toBool(); imageData = argument.toMap().value(QStringLiteral("imageData")).toByteArray(); return true; } }; 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(QStringLiteral("testid")); TestDevice* d = new TestDevice(nullptr, dId); int proxiedNotifications = 0; QCOMPARE(proxiedNotifications, d->getSentPackets()); plugin = new TestNotificationsPlugin(this, QVariantList({ QVariant::fromValue(d), "notifications_plugin", {"kdeconnect.notification"}, "preferences-desktop-notification"})); QVERIFY(plugin->getNotificationsListener()); delete plugin->getNotificationsListener(); // inject our tweaked NotificationsListener: TestedNotificationsListener* listener = new TestedNotificationsListener(plugin); QVERIFY(listener); plugin->setNotificationsListener(listener); QCOMPARE(listener, plugin->getNotificationsListener()); // make sure config is default: plugin->config()->set(QStringLiteral("generalPersistent"), false); plugin->config()->set(QStringLiteral("generalIncludeBody"), true); plugin->config()->set(QStringLiteral("generalUrgency"), 0); QCOMPARE(plugin->config()->get("generalPersistent"), false); QCOMPARE(plugin->config()->get("generalIncludeBody"), true); QCOMPARE(plugin->config()->get("generalUrgency"), false); // applications are modified directly: listener->getApplications().clear(); QCOMPARE(listener->getApplications().count(), 0); // // Go !!! // uint replacesId = 99; uint retId; QString appName(QStringLiteral("some-appName")); QString body(QStringLiteral("some-body")); QString icon(QStringLiteral("some-icon")); QString summary(QStringLiteral("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 packet QCOMPARE(++proxiedNotifications, d->getSentPackets()); // ... with our properties, QCOMPARE(d->getLastPacket()->get("id"), replacesId); QCOMPARE(d->getLastPacket()->get("appName"), appName); QCOMPARE(d->getLastPacket()->get("ticker"), summary + ": " + body); QCOMPARE(d->getLastPacket()->get("isClearable"), true); QCOMPARE(d->getLastPacket()->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(QStringLiteral("some-appName2")); QString body2(QStringLiteral("some-body2")); QString icon2(QStringLiteral("some-icon2")); QString summary2(QStringLiteral("some-summary2")); retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{"urgency", 2}}, 10); QCOMPARE(retId, replacesId+1); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QCOMPARE(d->getLastPacket()->get("id"), replacesId+1); QCOMPARE(d->getLastPacket()->get("appName"), appName2); QCOMPARE(d->getLastPacket()->get("ticker"), summary2 + ": " + body2); QCOMPARE(d->getLastPacket()->get("isClearable"), false); // timeout != 0 QCOMPARE(d->getLastPacket()->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(QStringLiteral("generalPersistent"), true); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 1); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName2, replacesId, icon2, summary2, body2, {}, {{}}, 3); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // but timeout == 0 is retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); plugin->config()->set(QStringLiteral("generalPersistent"), false); // if min-urgency is set, lower urgency levels are not synced: plugin->config()->set(QStringLiteral("generalUrgency"), 1); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 0}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // equal urgency is retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 1}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // higher urgency as well retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 2}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); plugin->config()->set(QStringLiteral("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->getSentPackets()); // others are still: retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{}}, 0); QCOMPARE(retId, replacesId+1); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // 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->getSentPackets()); // notifications with blacklisted subjects are not synced: QVERIFY(listener->getApplications().contains(appName)); listener->getApplications()[appName].blacklistExpression.setPattern(QStringLiteral("black[12]|foo(bar|baz)")); retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary black1"), body, {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary foobar"), body, {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // other subjects are synced: retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary foo"), body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary black3"), body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // also body is checked by blacklist if requested: plugin->config()->set(QStringLiteral("generalIncludeBody"), true); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body black1"), {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body foobaz"), {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // body does not matter if inclusion was not requested: plugin->config()->set(QStringLiteral("generalIncludeBody"), false); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body black1"), {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // without body, also ticker value is different: QCOMPARE(d->getLastPacket()->get("ticker"), summary); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body foobaz"), {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // back to normal: listener->getApplications()[appName].blacklistExpression.setPattern(QLatin1String("")); plugin->config()->set(QStringLiteral("generalIncludeBody"), true); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName2, replacesId, icon2, summary2, body2, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // icon synchronization: QStringList iconPaths; // appIcon int count = 0; const QStringList icons = KIconLoader::global()->queryIcons(-KIconLoader::SizeEnormous, KIconLoader::Application); for (const auto& iconName : icons) { if (!iconName.endsWith(QLatin1String(".png"))) continue; if (count++ > 3) // max 3 iterations break; iconPaths.append(iconName); // memorize some paths for later // existing icons are sync-ed if requested plugin->config()->set(QStringLiteral("generalSynchronizeIcons"), true); QFileInfo fi(iconName); retId = listener->Notify(appName, replacesId, fi.baseName(), summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QCOMPARE(d->getLastPacket()->payloadSize(), listener->iconForIconName(fi.baseName())->size()); // works also with absolute paths retId = listener->Notify(appName, replacesId, iconName, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QCOMPARE(d->getLastPacket()->payloadSize(), fi.size()); // extensions other than png are not accepted: retId = listener->Notify(appName, replacesId, iconName + ".svg", summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(!d->getLastPacket()->hasPayload()); // if sync not requested no payload: plugin->config()->set(QStringLiteral("generalSynchronizeIcons"), false); retId = listener->Notify(appName, replacesId, fi.baseName(), summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(!d->getLastPacket()->hasPayload()); QCOMPARE(d->getLastPacket()->payloadSize(), 0); } plugin->config()->set(QStringLiteral("generalSynchronizeIcons"), true); // image-path in hints if (iconPaths.size() > 0) { retId = listener->Notify(appName, replacesId, iconPaths.size() > 1 ? iconPaths[1] : icon, summary, body, {}, {{"image-path", iconPaths[0]}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QFileInfo hintsFi(iconPaths[0]); // image-path has priority over appIcon parameter: QCOMPARE(d->getLastPacket()->payloadSize(), hintsFi.size()); } // image_path in hints if (iconPaths.size() > 0) { retId = listener->Notify(appName, replacesId, iconPaths.size() > 1 ? iconPaths[1] : icon, summary, body, {}, {{"image_path", iconPaths[0]}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QFileInfo hintsFi(iconPaths[0]); // image_path has priority over appIcon parameter: QCOMPARE(d->getLastPacket()->payloadSize(), hintsFi.size()); } // image-data in hints // set up: QBuffer* buffer; QImage image; int width = 2, height = 2, rowStride = 4*width, bitsPerSample = 8, channels = 4; bool hasAlpha = 1; char rawData[] = { 0x01, 0x02, 0x03, 0x04, // raw rgba data 0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34 }; QVariantMap imageData = {{"width", width}, {"height", height}, {"rowStride", rowStride}, {"bitsPerSample", bitsPerSample}, {"channels", channels}, {"hasAlpha", hasAlpha}, {"imageData", QByteArray(rawData, sizeof(rawData))}}; QVariantMap hints; #define COMPARE_PIXEL(x, y) \ QCOMPARE(qRed(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 0]); \ QCOMPARE(qGreen(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 1]); \ QCOMPARE(qBlue(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 2]); \ QCOMPARE(qAlpha(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 3]); hints.insert(QStringLiteral("image-data"), imageData); if (iconPaths.size() > 0) hints.insert(QStringLiteral("image-path"), iconPaths[0]); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, hints, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); buffer = dynamic_cast(d->getLastPacket()->payload().data()); QCOMPARE(d->getLastPacket()->payloadSize(), buffer->size()); // image-data is attached as png data QVERIFY(image.loadFromData(reinterpret_cast(buffer->data().constData()), buffer->size(), "PNG")); // image-data has priority over image-path: QCOMPARE(image.byteCount(), rowStride*height); // rgba -> argb conversion was done correctly: COMPARE_PIXEL(0,0); COMPARE_PIXEL(1,0); COMPARE_PIXEL(0,1); COMPARE_PIXEL(1,1); // same for image_data in hints hints.clear(); hints.insert(QStringLiteral("image-data"), imageData); if (iconPaths.size() > 0) hints.insert(QStringLiteral("image_path"), iconPaths[0]); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, hints, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); buffer = dynamic_cast(d->getLastPacket()->payload().data()); QCOMPARE(d->getLastPacket()->payloadSize(), buffer->size()); // image-data is attached as png data QVERIFY(image.loadFromData(reinterpret_cast(buffer->data().constData()), buffer->size(), "PNG")); // image_data has priority over image_path/image-path: QCOMPARE(image.byteCount(), rowStride*height); // rgba -> argb conversion was done correctly: COMPARE_PIXEL(0,0); COMPARE_PIXEL(1,0); COMPARE_PIXEL(0,1); COMPARE_PIXEL(1,1); // same for icon_data, which has lowest priority hints.clear(); hints.insert(QStringLiteral("icon_data"), imageData); retId = listener->Notify(appName, replacesId, QLatin1String(""), summary, body, {}, hints, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()); QVERIFY(d->getLastPacket()->hasPayload()); buffer = dynamic_cast(d->getLastPacket()->payload().data()); // image-data is attached as png data QVERIFY(image.loadFromData(reinterpret_cast(buffer->data().constData()), buffer->size(), "PNG")); QCOMPARE(image.byteCount(), rowStride*height); // rgba -> argb conversion was done correctly: COMPARE_PIXEL(0,0); COMPARE_PIXEL(1,0); COMPARE_PIXEL(0,1); COMPARE_PIXEL(1,1); #undef COMPARE_PIXEL } QTEST_GUILESS_MAIN(TestNotificationListener); #include "testnotificationlistener.moc"