File indexing completed on 2024-09-15 03:41:43
0001 /* This file is part of the KDE libraries 0002 0003 SPDX-FileCopyrightText: 2012, 2019 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2012 Kai Dombrowe <just89@gmx.de> 0005 0006 SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "netwm.h" 0010 #include <QSignalSpy> 0011 #include <QWidget> 0012 #include <private/qtx11extras_p.h> 0013 0014 #include <kstartupinfo.h> 0015 #include <qtest_widgets.h> 0016 0017 #include <xcb/xcb.h> 0018 0019 #include "cptr_p.h" 0020 0021 Q_DECLARE_METATYPE(KStartupInfoId) 0022 Q_DECLARE_METATYPE(KStartupInfoData) 0023 0024 class KStartupInfo_UnitTest : public QObject 0025 { 0026 Q_OBJECT 0027 public: 0028 KStartupInfo_UnitTest() 0029 : m_listener(KStartupInfo::CleanOnCantDetect, this) 0030 , m_receivedCount(0) 0031 { 0032 qRegisterMetaType<KStartupInfoId>(); 0033 qRegisterMetaType<KStartupInfoData>(); 0034 connect(&m_listener, &KStartupInfo::gotNewStartup, this, &KStartupInfo_UnitTest::slotNewStartup); 0035 } 0036 0037 protected Q_SLOTS: 0038 void slotNewStartup(const KStartupInfoId &id, const KStartupInfoData &data) 0039 { 0040 ++m_receivedCount; 0041 m_receivedId = id; 0042 m_receivedData = data; 0043 Q_EMIT ready(); 0044 } 0045 Q_SIGNALS: 0046 void ready(); 0047 0048 private Q_SLOTS: 0049 void testStart(); 0050 void dontCrashCleanup_data(); 0051 void dontCrashCleanup(); 0052 void checkCleanOnCantDetectTest(); 0053 void checkStartupTest_data(); 0054 void checkStartupTest(); 0055 void createNewStartupIdTest(); 0056 void createNewStartupIdForTimestampTest(); 0057 0058 private: 0059 KStartupInfo m_listener; 0060 0061 int m_receivedCount; 0062 KStartupInfoId m_receivedId; 0063 KStartupInfoData m_receivedData; 0064 }; 0065 0066 void KStartupInfo_UnitTest::testStart() 0067 { 0068 KStartupInfoId id; 0069 id.initId(KStartupInfo::createNewStartupId()); 0070 0071 KStartupInfoData data; 0072 const QString appId = "/dir with space/kstartupinfo_unittest.desktop"; 0073 data.setApplicationId(appId); 0074 const QString iconPath = "/dir with space/kstartupinfo_unittest.png"; 0075 data.setIcon(iconPath); 0076 const QString description = "A description"; 0077 data.setDescription(description); 0078 const QString name = "A name"; 0079 data.setName(name); 0080 const int pid = 12345; 0081 data.addPid(pid); 0082 const QString bin = "dir with space/kstartupinfo_unittest"; 0083 data.setBin(bin); 0084 0085 QSignalSpy removedSpy(&m_listener, &KStartupInfo::gotRemoveStartup); 0086 QVERIFY(removedSpy.isValid()); 0087 0088 KStartupInfo::sendStartup(id, data); 0089 KStartupInfo::sendFinish(id, data); 0090 0091 QSignalSpy spy(this, &KStartupInfo_UnitTest::ready); 0092 spy.wait(5000); 0093 0094 QCOMPARE(m_receivedCount, 1); 0095 // qDebug() << m_receivedId.id(); // something like "$HOSTNAME;1342544979;490718;8602_TIME0" 0096 QCOMPARE(m_receivedData.name(), name); 0097 QCOMPARE(m_receivedData.description(), description); 0098 QCOMPARE(m_receivedData.applicationId(), appId); 0099 QCOMPARE(m_receivedData.icon(), iconPath); 0100 QCOMPARE(m_receivedData.bin(), bin); 0101 // qDebug() << m_receivedData.bin() << m_receivedData.name() << m_receivedData.description() << m_receivedData.icon() << m_receivedData.pids() << 0102 // m_receivedData.hostname() << m_receivedData.applicationId(); 0103 0104 int waitTime = 0; 0105 while (waitTime < 5000 && removedSpy.count() < 1) { 0106 QTest::qWait(200); 0107 waitTime += 200; 0108 } 0109 QCOMPARE(removedSpy.count(), 1); 0110 } 0111 0112 static void doSync() 0113 { 0114 auto *c = QX11Info::connection(); 0115 const auto cookie = xcb_get_input_focus(c); 0116 xcb_generic_error_t *error = nullptr; 0117 UniqueCPointer<xcb_get_input_focus_reply_t> sync(xcb_get_input_focus_reply(c, cookie, &error)); 0118 if (error) { 0119 free(error); 0120 } 0121 } 0122 0123 void KStartupInfo_UnitTest::dontCrashCleanup_data() 0124 { 0125 QTest::addColumn<bool>("silent"); 0126 QTest::addColumn<bool>("change"); 0127 QTest::addColumn<int>("countRemoveStartup"); 0128 0129 QTest::newRow("normal") << false << false << 2; 0130 QTest::newRow("silent") << true << false << 0; 0131 QTest::newRow("uninited") << false << true << 0; 0132 } 0133 0134 void KStartupInfo_UnitTest::dontCrashCleanup() 0135 { 0136 qputenv("KSTARTUPINFO_TIMEOUT", QByteArrayLiteral("1")); 0137 0138 KStartupInfoId id; 0139 KStartupInfoId id2; 0140 id.initId(QByteArrayLiteral("somefancyidwhichisrandom_kstartupinfo_unittest_0")); 0141 id2.initId(QByteArrayLiteral("somefancyidwhichisrandom_kstartupinfo_unittest_1")); 0142 0143 KStartupInfoData data; 0144 data.setApplicationId(QStringLiteral("/dir with space/kstartupinfo_unittest.desktop")); 0145 data.setIcon(QStringLiteral("/dir with space/kstartupinfo_unittest.png")); 0146 data.setDescription(QStringLiteral("A description")); 0147 data.setName(QStringLiteral("A name")); 0148 data.addPid(12345); 0149 data.setBin(QStringLiteral("dir with space/kstartupinfo_unittest")); 0150 QFETCH(bool, silent); 0151 if (silent) { 0152 data.setSilent(KStartupInfoData::Yes); 0153 } 0154 0155 QSignalSpy spy(&m_listener, &KStartupInfo::gotRemoveStartup); 0156 QFETCH(bool, change); 0157 if (change) { 0158 KStartupInfo::sendChange(id, data); 0159 KStartupInfo::sendChange(id2, data); 0160 } else { 0161 KStartupInfo::sendStartup(id, data); 0162 KStartupInfo::sendStartup(id2, data); 0163 } 0164 0165 // let's do a roundtrip to the X server 0166 doSync(); 0167 0168 QFETCH(int, countRemoveStartup); 0169 int waitTime = 1900; 0170 QTest::qWait(1900); 0171 while (waitTime <= 5000) { 0172 QTest::qWait(200); 0173 waitTime += 200; 0174 if (spy.count() == countRemoveStartup) { 0175 break; 0176 } 0177 } 0178 QCOMPARE(spy.count(), countRemoveStartup); 0179 } 0180 0181 void KStartupInfo_UnitTest::checkCleanOnCantDetectTest() 0182 { 0183 KStartupInfoId id; 0184 KStartupInfoId id2; 0185 id.initId(QByteArrayLiteral("somefancyidwhichisrandom_kstartupinfo_unittest_0")); 0186 id2.initId(QByteArrayLiteral("somefancyidwhichisrandom_kstartupinfo_unittest_1")); 0187 0188 KStartupInfoData data; 0189 data.setApplicationId(QStringLiteral("/dir with space/kstartupinfo_unittest.desktop")); 0190 data.setIcon(QStringLiteral("/dir with space/kstartupinfo_unittest.png")); 0191 data.setDescription(QStringLiteral("A description")); 0192 data.setName(QStringLiteral("A name")); 0193 data.setBin(QStringLiteral("dir with space/kstartupinfo_unittest")); 0194 data.setWMClass(QByteArrayLiteral("0")); 0195 0196 xcb_connection_t *c = QX11Info::connection(); 0197 xcb_window_t window = xcb_generate_id(c); 0198 uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE}; 0199 xcb_create_window(c, 0200 XCB_COPY_FROM_PARENT, 0201 window, 0202 QX11Info::appRootWindow(), 0203 0, 0204 0, 0205 100, 0206 100, 0207 0, 0208 XCB_COPY_FROM_PARENT, 0209 XCB_COPY_FROM_PARENT, 0210 XCB_CW_EVENT_MASK, 0211 values); 0212 0213 KStartupInfo::sendStartup(id, data); 0214 KStartupInfo::sendStartup(id2, data); 0215 0216 int previousCount = m_receivedCount; 0217 0218 doSync(); 0219 QTest::qWait(10); 0220 0221 xcb_map_window(c, window); 0222 xcb_flush(c); 0223 QTest::qWait(10); 0224 0225 xcb_unmap_window(c, window); 0226 xcb_flush(c); 0227 QTest::qWait(100); 0228 xcb_map_window(c, window); 0229 xcb_flush(c); 0230 0231 QCOMPARE(m_receivedCount, previousCount + 2); 0232 QCOMPARE(m_receivedId, id2); 0233 } 0234 0235 void KStartupInfo_UnitTest::checkStartupTest_data() 0236 { 0237 QTest::addColumn<QByteArray>("wmClass"); 0238 QTest::addColumn<int>("pid"); 0239 0240 QTest::newRow("wmClass") << QByteArrayLiteral("kstartupinfotest") << 0; 0241 QTest::newRow("pid") << QByteArray() << 12345; 0242 } 0243 0244 void KStartupInfo_UnitTest::checkStartupTest() 0245 { 0246 KStartupInfoId id; 0247 KStartupInfoId id2; 0248 id.initId(QByteArrayLiteral("somefancyidwhichisrandom_kstartupinfo_unittest_0")); 0249 id2.initId(QByteArrayLiteral("somefancyidwhichisrandom_kstartupinfo_unittest_1")); 0250 0251 KStartupInfoData data; 0252 data.setApplicationId(QStringLiteral("/dir with space/kstartupinfo_unittest.desktop")); 0253 data.setIcon(QStringLiteral("/dir with space/kstartupinfo_unittest.png")); 0254 data.setDescription(QStringLiteral("A description")); 0255 data.setName(QStringLiteral("A name")); 0256 data.setBin(QStringLiteral("dir with space/kstartupinfo_unittest")); 0257 QFETCH(int, pid); 0258 data.addPid(pid); 0259 data.setHostname(QByteArrayLiteral("localhost")); 0260 0261 // important for this test: WMClass 0262 QFETCH(QByteArray, wmClass); 0263 data.setWMClass(wmClass); 0264 0265 xcb_connection_t *c = QX11Info::connection(); 0266 xcb_window_t window = xcb_generate_id(c); 0267 uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE}; 0268 xcb_create_window(c, 0269 XCB_COPY_FROM_PARENT, 0270 window, 0271 QX11Info::appRootWindow(), 0272 0, 0273 0, 0274 100, 0275 100, 0276 0, 0277 XCB_COPY_FROM_PARENT, 0278 XCB_COPY_FROM_PARENT, 0279 XCB_CW_EVENT_MASK, 0280 values); 0281 0282 xcb_change_property(c, 0283 XCB_PROP_MODE_REPLACE, 0284 window, 0285 XCB_ATOM_WM_CLASS, 0286 XCB_ATOM_STRING, 0287 8, 0288 wmClass.length() * 2 + 1, 0289 "kstartupinfotest\0kstartupinfotest"); 0290 xcb_change_property(c, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 8, 9, "localhost"); 0291 NETWinInfo winInfo(QX11Info::connection(), window, QX11Info::appRootWindow(), NET::Properties(), NET::Properties2()); 0292 winInfo.setPid(pid); 0293 0294 KStartupInfo info(KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this); 0295 KStartupInfo::sendStartup(id, data); 0296 KStartupInfo::sendStartup(id2, data); 0297 0298 doSync(); 0299 QTest::qWait(100); 0300 0301 QCOMPARE(info.checkStartup(window), KStartupInfo::Match); 0302 QCOMPARE(info.checkStartup(window), KStartupInfo::Match); 0303 } 0304 0305 void KStartupInfo_UnitTest::createNewStartupIdTest() 0306 { 0307 const QByteArray &id = KStartupInfo::createNewStartupId(); 0308 QVERIFY(!id.isEmpty()); 0309 const int index = id.indexOf(QByteArrayLiteral("TIME")); 0310 QVERIFY(index != -1); 0311 const QByteArray time = id.mid(index + 4); 0312 QVERIFY(time.toULongLong() != 0u); 0313 } 0314 0315 void KStartupInfo_UnitTest::createNewStartupIdForTimestampTest() 0316 { 0317 const QByteArray &id = KStartupInfo::createNewStartupIdForTimestamp(5); 0318 QVERIFY(!id.isEmpty()); 0319 const int index = id.indexOf(QByteArrayLiteral("TIME")); 0320 QVERIFY(index != -1); 0321 QCOMPARE(id.mid(index + 4).toULongLong(), 5u); 0322 } 0323 0324 QTEST_MAIN(KStartupInfo_UnitTest) 0325 0326 #include "kstartupinfo_unittest.moc"