File indexing completed on 2024-04-28 11:37:11

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"