File indexing completed on 2024-09-08 12:15:51
0001 /* 0002 This file is part of the KDE libraries 0003 0004 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com> 0005 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "kglobalshortcuttest.h" 0011 #include "sequencehelpers_p.h" 0012 #include <QAction> 0013 #include <QDBusInterface> 0014 #include <QSignalSpy> 0015 #include <QStandardPaths> 0016 #include <QTest> 0017 #include <QThread> 0018 #include <kglobalaccel.h> 0019 0020 #include <qplatformdefs.h> 0021 0022 #include <QDBusConnectionInterface> 0023 0024 #ifdef HAVE_XCB_XTEST 0025 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0026 #include <private/qtx11extras_p.h> 0027 #else 0028 #include <QX11Info> 0029 #endif 0030 #define XK_MISCELLANY 0031 #define XK_XKB_KEYS 0032 #include <X11/keysymdef.h> 0033 #include <xcb/xcb_keysyms.h> 0034 #include <xcb/xtest.h> 0035 #endif 0036 0037 /* 0038 * NOTE 0039 * ---- 0040 * These tests could be better. They don't include actually triggering actions, 0041 * and we just choose very improbable shortcuts to avoid conflicts with real 0042 * applications' shortcuts. 0043 * 0044 */ 0045 0046 const QKeySequence sequenceA = QKeySequence(Qt::SHIFT | Qt::META | Qt::CTRL | Qt::ALT | Qt::Key_F12); 0047 const QKeySequence sequenceB = QKeySequence(Qt::Key_F29); 0048 const QKeySequence sequenceC = QKeySequence(Qt::SHIFT | Qt::META | Qt::CTRL | Qt::Key_F28); 0049 const QKeySequence sequenceD = QKeySequence(Qt::META | Qt::ALT | Qt::Key_F30); 0050 const QKeySequence sequenceE = QKeySequence(Qt::META | Qt::Key_F29); 0051 const QKeySequence sequenceF = QKeySequence(Qt::META | Qt::Key_F27); 0052 0053 // we need a GUI so that the implementation can grab keys 0054 QTEST_MAIN(KGlobalShortcutTest) 0055 0056 void KGlobalShortcutTest::initTestCase() 0057 { 0058 QStandardPaths::setTestModeEnabled(true); 0059 m_daemonInstalled = true; 0060 0061 QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); 0062 if (!bus->isServiceRegistered(QStringLiteral("org.kde.kglobalaccel"))) { 0063 QDBusReply<void> reply = bus->startService(QStringLiteral("org.kde.kglobalaccel")); 0064 if (!reply.isValid()) { 0065 m_daemonInstalled = false; 0066 } 0067 } 0068 } 0069 0070 /** 0071 * NOTE: This method alters a KDE config file in your home directory. 0072 * 0073 * KDE4: ~/.kde4/share/config/kglobalshortcutsrc 0074 * KF5: ~/.config/kglobalshortcutsrc 0075 * 0076 * At least on KDE4 this file cannot be modified by hand if the Plasma session 0077 * is running. So in case you have to clean the file of spurious entries 0078 * (e.g. because of broken or failing test cases) 0079 * you have to logout from Plasma. 0080 * 0081 * The following sections are created and normally removed automatically: 0082 * [qttest] 0083 */ 0084 void KGlobalShortcutTest::setupTest(const QString &id) 0085 { 0086 QString componentName = "qttest"; 0087 0088 if (m_actionA) { 0089 KGlobalAccel::self()->removeAllShortcuts(m_actionA); 0090 delete m_actionA; 0091 } 0092 0093 if (m_actionB) { 0094 KGlobalAccel::self()->removeAllShortcuts(m_actionB); 0095 delete m_actionB; 0096 } 0097 0098 // Ensure that the previous test did cleanup correctly 0099 #if KGLOBALACCEL_ENABLE_DEPRECATED_SINCE(4, 2) 0100 KGlobalAccel *kga = KGlobalAccel::self(); 0101 QList<QStringList> components = kga->allMainComponents(); 0102 QStringList componentId; 0103 componentId << componentName << QString() << "KDE Test Program" << QString(); 0104 // QVERIFY(!components.contains(componentId)); 0105 #endif 0106 0107 m_actionA = new QAction("Text For Action A", this); 0108 m_actionA->setObjectName("Action A:" + id); 0109 m_actionA->setProperty("componentName", componentName); 0110 m_actionA->setProperty("componentDisplayName", "KDE Test Program"); 0111 KGlobalAccel::self()->setShortcut(m_actionA, QList<QKeySequence>() << sequenceA << sequenceB, KGlobalAccel::NoAutoloading); 0112 KGlobalAccel::self()->setDefaultShortcut(m_actionA, QList<QKeySequence>() << sequenceA << sequenceB, KGlobalAccel::NoAutoloading); 0113 0114 m_actionB = new QAction("Text For Action B", this); 0115 m_actionB->setObjectName("Action B:" + id); 0116 m_actionB->setProperty("componentName", componentName); 0117 m_actionB->setProperty("componentDisplayName", "KDE Test Program"); 0118 KGlobalAccel::self()->setShortcut(m_actionB, QList<QKeySequence>(), KGlobalAccel::NoAutoloading); 0119 KGlobalAccel::self()->setDefaultShortcut(m_actionB, QList<QKeySequence>(), KGlobalAccel::NoAutoloading); 0120 } 0121 0122 void KGlobalShortcutTest::testSetShortcut() 0123 { 0124 setupTest("testSetShortcut"); 0125 0126 if (!m_daemonInstalled) { 0127 QSKIP("kglobalaccel not installed"); 0128 } 0129 0130 // Just ensure that the desired values are set for both actions 0131 QList<QKeySequence> cutA; 0132 cutA << sequenceA << sequenceB; 0133 0134 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionA), cutA); 0135 QCOMPARE(KGlobalAccel::self()->defaultShortcut(m_actionA), cutA); 0136 0137 QVERIFY(KGlobalAccel::self()->shortcut(m_actionB).isEmpty()); 0138 QVERIFY(KGlobalAccel::self()->defaultShortcut(m_actionB).isEmpty()); 0139 } 0140 0141 void KGlobalShortcutTest::testActivateShortcut() 0142 { 0143 #ifdef HAVE_XCB_XTEST 0144 if (!QX11Info::isPlatformX11()) { 0145 QSKIP("This test can only be run on platform xcb"); 0146 } 0147 0148 setupTest("testActivateShortcut"); 0149 if (!m_daemonInstalled) { 0150 QSKIP("kglobalaccel not installed"); 0151 } 0152 QSignalSpy actionASpy(m_actionA, &QAction::triggered); 0153 QVERIFY(actionASpy.isValid()); 0154 0155 xcb_connection_t *c = QX11Info::connection(); 0156 xcb_window_t w = QX11Info::appRootWindow(); 0157 0158 xcb_key_symbols_t *syms = xcb_key_symbols_alloc(c); 0159 auto getCode = [syms](int code) { 0160 xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(syms, code); 0161 const xcb_keycode_t ret = keyCodes[0]; 0162 free(keyCodes); 0163 return ret; 0164 }; 0165 const xcb_keycode_t shift = getCode(XK_Shift_L); 0166 const xcb_keycode_t meta = getCode(XK_Super_L); 0167 const xcb_keycode_t control = getCode(XK_Control_L); 0168 const xcb_keycode_t alt = getCode(XK_Alt_L); 0169 const xcb_keycode_t f12 = getCode(XK_F12); 0170 xcb_key_symbols_free(syms); 0171 0172 xcb_test_fake_input(c, XCB_KEY_PRESS, meta, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0173 xcb_test_fake_input(c, XCB_KEY_PRESS, control, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0174 xcb_test_fake_input(c, XCB_KEY_PRESS, alt, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0175 xcb_test_fake_input(c, XCB_KEY_PRESS, shift, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0176 xcb_test_fake_input(c, XCB_KEY_PRESS, f12, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0177 0178 xcb_test_fake_input(c, XCB_KEY_RELEASE, f12, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0179 xcb_test_fake_input(c, XCB_KEY_RELEASE, shift, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0180 xcb_test_fake_input(c, XCB_KEY_RELEASE, meta, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0181 xcb_test_fake_input(c, XCB_KEY_RELEASE, control, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0182 xcb_test_fake_input(c, XCB_KEY_RELEASE, alt, XCB_TIME_CURRENT_TIME, w, 0, 0, 0); 0183 xcb_flush(c); 0184 0185 QVERIFY(actionASpy.wait()); 0186 QCOMPARE(actionASpy.count(), 1); 0187 #else 0188 QSKIP("This test requires to be compiled with XCB-XTEST"); 0189 #endif 0190 } 0191 0192 // Current state 0193 // m_actionA: (sequenceA, sequenceB) 0194 // m_actionB: (,) 0195 0196 void KGlobalShortcutTest::testFindActionByKey() 0197 { 0198 // Skip this. The above testcase hasn't changed the actions 0199 setupTest("testFindActionByKey"); 0200 if (!m_daemonInstalled) { 0201 QSKIP("kglobalaccel not installed"); 0202 } 0203 0204 #if KGLOBALACCEL_ENABLE_DEPRECATED_SINCE(5, 90) 0205 QList<KGlobalShortcutInfo> actionId = KGlobalAccel::self()->getGlobalShortcutsByKey(sequenceB); 0206 #else 0207 QList<KGlobalShortcutInfo> actionId = KGlobalAccel::self()->globalShortcutsByKey(sequenceB); 0208 #endif 0209 0210 QCOMPARE(actionId.size(), 1); 0211 0212 QString actionIdAComponentUniqueName("qttest"); 0213 QString actionIdAUniqueName("Action A:testFindActionByKey"); 0214 QString actionIdAComponentFriendlyName("KDE Test Program"); 0215 QString actionIdAFriendlyName("Text For Action A"); 0216 0217 QCOMPARE(actionId.first().componentUniqueName(), actionIdAComponentUniqueName); 0218 QCOMPARE(actionId.first().uniqueName(), actionIdAUniqueName); 0219 QCOMPARE(actionId.first().componentFriendlyName(), actionIdAComponentFriendlyName); 0220 QCOMPARE(actionId.first().friendlyName(), actionIdAFriendlyName); 0221 0222 #if KGLOBALACCEL_ENABLE_DEPRECATED_SINCE(5, 90) 0223 actionId = KGlobalAccel::self()->getGlobalShortcutsByKey(sequenceA); 0224 #else 0225 actionId = KGlobalAccel::self()->globalShortcutsByKey(sequenceA); 0226 #endif 0227 0228 QCOMPARE(actionId.size(), 1); 0229 0230 QCOMPARE(actionId.first().componentUniqueName(), actionIdAComponentUniqueName); 0231 QCOMPARE(actionId.first().uniqueName(), actionIdAUniqueName); 0232 QCOMPARE(actionId.first().componentFriendlyName(), actionIdAComponentFriendlyName); 0233 QCOMPARE(actionId.first().friendlyName(), actionIdAFriendlyName); 0234 } 0235 0236 void KGlobalShortcutTest::testChangeShortcut() 0237 { 0238 // Skip this. The above testcase hasn't changed the actions 0239 setupTest("testChangeShortcut"); 0240 0241 if (!m_daemonInstalled) { 0242 QSKIP("kglobalaccel not installed"); 0243 } 0244 // Change the shortcut 0245 KGlobalAccel::self()->setShortcut(m_actionA, QList<QKeySequence>() << sequenceC, KGlobalAccel::NoAutoloading); 0246 // Ensure that the change is active 0247 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionA), QList<QKeySequence>() << sequenceC); 0248 // We haven't changed the default shortcut, ensure it is unchanged 0249 QList<QKeySequence> cutA; 0250 cutA << sequenceA << sequenceB; 0251 QCOMPARE(KGlobalAccel::self()->defaultShortcut(m_actionA), cutA); 0252 0253 // Try to set a already take shortcut 0254 QList<QKeySequence> cutB; 0255 cutB << KGlobalAccel::self()->shortcut(m_actionA).first() << QKeySequence(sequenceE); 0256 KGlobalAccel::self()->setShortcut(m_actionB, cutB, KGlobalAccel::NoAutoloading); 0257 // Ensure that no change was made to the primary active shortcut 0258 QVERIFY(KGlobalAccel::self()->shortcut(m_actionB).first().isEmpty()); 0259 // Ensure that the change to the secondary active shortcut was made 0260 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionB).at(1), QKeySequence(sequenceE)); 0261 // Ensure that the default shortcut is still empty 0262 QVERIFY(KGlobalAccel::self()->defaultShortcut(m_actionB).isEmpty()); // unchanged 0263 0264 // Only change the active shortcut 0265 cutB[0] = sequenceD; 0266 KGlobalAccel::self()->setShortcut(m_actionB, cutB, KGlobalAccel::NoAutoloading); 0267 // Check that the change went through 0268 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionB), cutB); 0269 // Check that the default shortcut is not active 0270 QVERIFY(KGlobalAccel::self()->defaultShortcut(m_actionB).isEmpty()); // unchanged 0271 0272 struct SeqInfo { 0273 QKeySequence seqA; 0274 QKeySequence seqB; 0275 bool expected; 0276 }; 0277 0278 static const SeqInfo values[] = { 0279 {QKeySequence('A', 'B', 'C'), QKeySequence('A', 'B', 'C'), true}, 0280 {QKeySequence('A', 'B', 'C'), QKeySequence('B', 'C'), true}, 0281 {QKeySequence('A', 'B', 'C'), QKeySequence('A', 'B'), true}, 0282 {QKeySequence('A', 'B', 'C'), QKeySequence('B'), true}, 0283 {QKeySequence('A', 'B', 'C'), QKeySequence('D', 'B', 'C'), false}, 0284 {QKeySequence('A', 'B', 'C'), QKeySequence('A', 'B', 'D'), false}, 0285 {QKeySequence('A', 'B', 'C'), QKeySequence('D'), false}, 0286 {QKeySequence('A', 'B', 'B', 'C'), QKeySequence('B', 'B'), true}, 0287 {QKeySequence('A', 'B', 'C', 'D'), QKeySequence('A', 'B'), true}, 0288 {QKeySequence('A', 'B', 'C', 'D'), QKeySequence('B', 'C'), true}, 0289 {QKeySequence('A', 'B', 'C', 'D'), QKeySequence('C', 'D'), true}, 0290 }; 0291 0292 for (const auto &info : values) { 0293 KGlobalAccel::self()->removeAllShortcuts(m_actionA); 0294 KGlobalAccel::self()->setGlobalShortcut(m_actionA, info.seqA); 0295 0296 cutB.clear(); 0297 cutB << info.seqB; 0298 KGlobalAccel::self()->removeAllShortcuts(m_actionB); 0299 KGlobalAccel::self()->setGlobalShortcut(m_actionB, info.seqB); 0300 0301 QList<QKeySequence> seq = KGlobalAccel::self()->shortcut(m_actionB); 0302 if (!info.expected) { 0303 QCOMPARE(seq, cutB); 0304 } else { 0305 QVERIFY(seq.first().isEmpty()); 0306 } 0307 } 0308 } 0309 0310 void KGlobalShortcutTest::testStealShortcut() 0311 { 0312 setupTest("testStealShortcut"); 0313 if (!m_daemonInstalled) { 0314 QSKIP("kglobalaccel not installed"); 0315 } 0316 0317 // Steal a shortcut from an action. First ensure the initial state is 0318 // correct 0319 QList<QKeySequence> cutA; 0320 cutA << sequenceA << sequenceB; 0321 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionA), cutA); 0322 QCOMPARE(KGlobalAccel::self()->defaultShortcut(m_actionA), cutA); 0323 0324 KGlobalAccel::stealShortcutSystemwide(sequenceA); 0325 // let DBus do its thing in case it happens asynchronously 0326 QTest::qWait(200); 0327 QList<QKeySequence> shortcuts = KGlobalAccel::self()->shortcut(m_actionA); 0328 QVERIFY(!shortcuts.isEmpty()); 0329 QVERIFY(shortcuts.first().isEmpty()); 0330 } 0331 0332 void KGlobalShortcutTest::testSaveRestore() 0333 { 0334 setupTest("testSaveRestore"); 0335 if (!m_daemonInstalled) { 0336 QSKIP("kglobalaccel not installed"); 0337 } 0338 0339 // It /would be nice/ to test persistent storage. That is not so easy... 0340 QList<QKeySequence> cutA = KGlobalAccel::self()->shortcut(m_actionA); 0341 // Delete the action 0342 delete m_actionA; 0343 0344 // Recreate it 0345 m_actionA = new QAction("Text For Action A", this); 0346 m_actionA->setObjectName("Action A:testSaveRestore"); 0347 m_actionA->setProperty("componentName", "qttest"); 0348 m_actionA->setProperty("componentDisplayName", "KDE Test Program"); 0349 0350 // Now it's empty 0351 QVERIFY(KGlobalAccel::self()->shortcut(m_actionA).isEmpty()); 0352 0353 KGlobalAccel::self()->setShortcut(m_actionA, QList<QKeySequence>()); 0354 // Now it's restored 0355 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionA), cutA); 0356 0357 // And again 0358 delete m_actionA; 0359 m_actionA = new QAction("Text For Action A", this); 0360 m_actionA->setObjectName("Action A:testSaveRestore"); 0361 m_actionA->setProperty("componentName", "qttest"); 0362 m_actionA->setProperty("componentDisplayName", "KDE Test Program"); 0363 KGlobalAccel::self()->setShortcut(m_actionA, QList<QKeySequence>() << QKeySequence() << (cutA.isEmpty() ? QKeySequence() : cutA.first())); 0364 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionA), cutA); 0365 } 0366 0367 // Duplicated again! 0368 enum actionIdFields { 0369 ComponentUnique = 0, 0370 ActionUnique = 1, 0371 ComponentFriendly = 2, 0372 ActionFriendly = 3, 0373 }; 0374 0375 void KGlobalShortcutTest::testListActions() 0376 { 0377 setupTest("testListActions"); 0378 if (!m_daemonInstalled) { 0379 QSKIP("kglobalaccel not installed"); 0380 } 0381 0382 // As in kdebase/workspace/kcontrol/keys/globalshortcuts.cpp 0383 #if KGLOBALACCEL_ENABLE_DEPRECATED_SINCE(4, 2) 0384 KGlobalAccel *kga = KGlobalAccel::self(); 0385 QList<QStringList> components = kga->allMainComponents(); 0386 // qDebug() << components; 0387 QStringList componentId; 0388 componentId << "qttest" << QString() << "KDE Test Program" << QString(); 0389 QVERIFY(components.contains(componentId)); 0390 #endif 0391 0392 #if KGLOBALACCEL_ENABLE_DEPRECATED_SINCE(4, 2) 0393 QList<QStringList> actions = kga->allActionsForComponent(componentId); 0394 QVERIFY(!actions.isEmpty()); 0395 QStringList actionIdA; 0396 actionIdA << "qttest" 0397 << "Action A:testListActions" 0398 << "KDE Test Program" 0399 << "Text For Action A"; 0400 QStringList actionIdB; 0401 actionIdB << "qttest" 0402 << "Action B:testListActions" 0403 << "KDE Test Program" 0404 << "Text For Action B"; 0405 // qDebug() << actions; 0406 QVERIFY(actions.contains(actionIdA)); 0407 QVERIFY(actions.contains(actionIdB)); 0408 #endif 0409 } 0410 0411 void KGlobalShortcutTest::testComponentAssignment() 0412 { 0413 // We don't use them here 0414 // setupTest(); 0415 0416 QString otherComponent("test_component1"); 0417 QList<QKeySequence> cutB; 0418 /************************************************************ 0419 * Ensure that the actions get a correct component assigned * 0420 ************************************************************/ 0421 // Action without component name get the global component 0422 { 0423 QAction action("Text For Action A", nullptr); 0424 action.setObjectName("Action C"); 0425 0426 QCOMPARE(action.property("componentName").toString(), QString()); 0427 KGlobalAccel::self()->setShortcut(&action, cutB, KGlobalAccel::NoAutoloading); 0428 QCOMPARE(action.property("componentName").toString(), QString()); 0429 // cleanup 0430 KGlobalAccel::self()->removeAllShortcuts(&action); 0431 } 0432 0433 // Action with component name keeps its component name 0434 { 0435 QAction action("Text for Action C", nullptr); 0436 action.setObjectName("Action C"); 0437 action.setProperty("componentName", otherComponent); 0438 0439 QCOMPARE(action.property("componentName").toString(), otherComponent); 0440 KGlobalAccel::self()->setShortcut(&action, cutB, KGlobalAccel::NoAutoloading); 0441 QCOMPARE(action.property("componentName").toString(), otherComponent); 0442 // cleanup 0443 KGlobalAccel::self()->removeAllShortcuts(&action); 0444 } 0445 } 0446 0447 void KGlobalShortcutTest::testConfigurationActions() 0448 { 0449 setupTest("testConfigurationActions"); 0450 if (!m_daemonInstalled) { 0451 QSKIP("kglobalaccel not installed"); 0452 } 0453 0454 // Create a configuration action 0455 QAction cfg_action("Text For Action A", nullptr); 0456 cfg_action.setObjectName("Action A:testConfigurationActions"); 0457 cfg_action.setProperty("isConfigurationAction", true); 0458 cfg_action.setProperty("componentName", "qttest"); 0459 cfg_action.setProperty("componentDisplayName", "KDE Test Program"); 0460 KGlobalAccel::self()->setShortcut(&cfg_action, QList<QKeySequence>()); 0461 0462 // Check that the configuration action has the correct shortcuts 0463 QCOMPARE(KGlobalAccel::self()->shortcut(m_actionA), KGlobalAccel::self()->shortcut(&cfg_action)); 0464 0465 // TODO: 0466 // - change shortcut from configuration action and test for 0467 // yourShortcutGotChanged 0468 // - Ensure that the config action doesn't trigger(how?) 0469 // - Ensure that the original action is still working when the 0470 // configuration action is deleted 0471 } 0472 0473 void KGlobalShortcutTest::testOverrideMainComponentData() 0474 { 0475 setupTest("testOverrideMainComponentData"); 0476 0477 QString otherComponent("test_component1"); 0478 QList<QKeySequence> cutB; 0479 0480 // Action without component name 0481 QAction *action = new QAction("Text For Action A", this); 0482 QCOMPARE(action->property("componentName").toString(), QString()); 0483 action->setObjectName("Action A"); 0484 KGlobalAccel::self()->setShortcut(action, cutB, KGlobalAccel::NoAutoloading); 0485 QCOMPARE(action->property("componentName").toString(), QString()); 0486 0487 // Action with component name 0488 KGlobalAccel::self()->removeAllShortcuts(action); 0489 delete action; 0490 action = new QAction("Text For Action A", this); 0491 action->setObjectName("Action A"); 0492 action->setProperty("componentName", otherComponent); 0493 QCOMPARE(action->property("componentName").toString(), otherComponent); 0494 KGlobalAccel::self()->setShortcut(action, cutB, KGlobalAccel::NoAutoloading); 0495 QCOMPARE(action->property("componentName").toString(), otherComponent); 0496 0497 // cleanup 0498 KGlobalAccel::self()->removeAllShortcuts(action); 0499 } 0500 0501 void KGlobalShortcutTest::testNotification() 0502 { 0503 setupTest("testNotification"); 0504 0505 // Action without component name 0506 QAction *action = new QAction("Text For Action A", this); 0507 QCOMPARE(action->property("componentName").toString(), QString()); 0508 action->setObjectName("Action A"); 0509 QList<QKeySequence> cutB; 0510 KGlobalAccel::self()->setShortcut(action, cutB, KGlobalAccel::NoAutoloading); 0511 QCOMPARE(action->property("componentName").toString(), QString()); 0512 0513 // kglobalacceld collects registrations and shows the together. Give it 0514 // time to kick in. 0515 QThread::sleep(2); 0516 0517 KGlobalAccel::self()->removeAllShortcuts(action); 0518 } 0519 0520 void KGlobalShortcutTest::testGetGlobalShortcut() 0521 { 0522 setupTest("testLoadShortcutFromGlobalSettings"); // cleanup see testForgetGlobalShortcut 0523 if (!m_daemonInstalled) { 0524 QSKIP("kglobalaccel not installed"); 0525 } 0526 0527 // retrieve shortcut list 0528 auto shortcutList = KGlobalAccel::self()->globalShortcut("qttest", "Action A:testLoadShortcutFromGlobalSettings"); 0529 QCOMPARE(shortcutList.count(), 2); // see setupTest 0530 0531 // test for a real shortcut: 0532 // shortcutList = KGlobalAccel::self()->shortcut("kwin", "Kill Window"); 0533 // QCOMPARE(shortcutList.count(), 1); 0534 } 0535 0536 void KGlobalShortcutTest::testMangle() 0537 { 0538 setupTest("testMangle"); 0539 static const std::pair<QKeySequence, QKeySequence> values[] = { 0540 {QKeySequence('A', 'B', 'C', 'D'), QKeySequence('A', 'B', 'C', 'D')}, 0541 {QKeySequence("Shift+A,B,C,D"), QKeySequence("Shift+A,B,C,D")}, 0542 {QKeySequence("Shift+Tab,B,C,D"), QKeySequence("Shift+Tab,B,C,D")}, 0543 {QKeySequence("Ctrl+Shift+Tab,B,C,D"), QKeySequence("Ctrl+Shift+Tab,B,C,D")}, 0544 {QKeySequence("Shift+BackTab,B,C,D"), QKeySequence("Shift+Tab,B,C,D")}, 0545 {QKeySequence("Shift+BackTab,Shift+BackTab,Shift+BackTab,Shift+BackTab"), QKeySequence("Shift+Tab,Shift+Tab,Shift+Tab,Shift+Tab")}, 0546 {QKeySequence(), QKeySequence()}, 0547 }; 0548 0549 for (const auto &pair : values) { 0550 QCOMPARE(Utils::mangleKey(pair.first), pair.second); 0551 } 0552 } 0553 0554 void KGlobalShortcutTest::testCrop() 0555 { 0556 setupTest("testCrop"); 0557 0558 struct SeqInfo { 0559 QKeySequence seq; 0560 int count = 0; 0561 QKeySequence expected; 0562 }; 0563 0564 static const SeqInfo values[] = { 0565 {QKeySequence('A', 'B', 'C', 'D'), 1, QKeySequence('B', 'C', 'D')}, 0566 {QKeySequence('A', 'B', 'C', 'D'), 2, QKeySequence('C', 'D')}, 0567 {QKeySequence('A', 'B', 'C'), 1, QKeySequence('B', 'C')}, 0568 {QKeySequence('A', 'B', 'C'), 2, QKeySequence('C')}, 0569 {QKeySequence('A'), 1, QKeySequence()}, 0570 {QKeySequence('A'), 2, QKeySequence()}, 0571 {QKeySequence(), 1, QKeySequence()}, 0572 {QKeySequence(), 2, QKeySequence()}, 0573 }; 0574 0575 for (const auto &s : values) { 0576 QCOMPARE(Utils::cropKey(s.seq, s.count), s.expected); 0577 } 0578 } 0579 0580 void KGlobalShortcutTest::testReverse() 0581 { 0582 setupTest("testReverse"); 0583 static const std::pair<QKeySequence, QKeySequence> values[] = { 0584 {QKeySequence('A', 'B', 'C', 'D'), QKeySequence('D', 'C', 'B', 'A')}, 0585 {QKeySequence('A', 'B', 'C'), QKeySequence('C', 'B', 'A')}, 0586 {QKeySequence('A', 'B'), QKeySequence('B', 'A')}, 0587 {QKeySequence('A'), QKeySequence('A')}, 0588 {QKeySequence(), QKeySequence()}, 0589 }; 0590 0591 for (const auto &pair : values) { 0592 QCOMPARE(Utils::reverseKey(pair.first), pair.second); 0593 } 0594 } 0595 0596 void KGlobalShortcutTest::testMatch() 0597 { 0598 setupTest("Match"); 0599 0600 struct Info { 0601 QList<QKeySequence> seqList; 0602 bool isMatch = false; 0603 }; 0604 0605 static const Info values[] = { 0606 {QList{QKeySequence('A', 'B', 'C')}, true}, 0607 {QList{QKeySequence('A', 'B', 'D')}, false}, 0608 {QList{QKeySequence('D')}, false}, 0609 {QList{QKeySequence('A', 'B', 'C', 'D')}, true}, 0610 {QList{QKeySequence('D', 'A', 'B', 'C')}, true}, 0611 {QList{QKeySequence('D', 'A', 'B', 'D')}, false}, 0612 {QList{QKeySequence('A', 'B')}, true}, 0613 {QList{QKeySequence('B', 'C')}, true}, 0614 {QList{QKeySequence('A', 'C')}, false}, 0615 }; 0616 0617 const QKeySequence seq = QKeySequence('A', 'B', 'C'); 0618 for (const auto &i : values) { 0619 QCOMPARE(Utils::matchSequences(seq, i.seqList), i.isMatch); 0620 } 0621 } 0622 0623 void KGlobalShortcutTest::testForgetGlobalShortcut() 0624 { 0625 setupTest("testForgetGlobalShortcut"); 0626 0627 // Ensure that forgetGlobalShortcut can be called on any action. 0628 QAction a("Test", nullptr); 0629 KGlobalAccel::self()->removeAllShortcuts(&a); 0630 if (!m_daemonInstalled) { 0631 QSKIP("kglobalaccel not installed"); 0632 } 0633 0634 // We forget these two shortcuts and check that the component is gone 0635 // after that. If not it can mean the forgetGlobalShortcut() call is 0636 // broken OR someone messed up these tests to leave an additional global 0637 // shortcut behind. 0638 KGlobalAccel::self()->removeAllShortcuts(m_actionB); 0639 KGlobalAccel::self()->removeAllShortcuts(m_actionA); 0640 // kglobalaccel writes asynchronous. 0641 QThread::sleep(1); 0642 0643 #if KGLOBALACCEL_ENABLE_DEPRECATED_SINCE(4, 2) 0644 KGlobalAccel *kga = KGlobalAccel::self(); 0645 QList<QStringList> components = kga->allMainComponents(); 0646 QStringList componentId; 0647 componentId << "qttest" << QString() << "KDE Test Program" << QString(); 0648 QVERIFY(!components.contains(componentId)); 0649 #endif 0650 } 0651 0652 #include "moc_kglobalshortcuttest.cpp"