Warning, file /frameworks/kauth/autotests/HelperTest.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2012 Dario Freddi <drf@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "BackendsManager.h" 0008 #include "TestHelper.h" 0009 0010 #include <kauth/actionreply.h> 0011 #include <kauth/executejob.h> 0012 0013 #include <QRandomGenerator> 0014 #include <QSignalSpy> 0015 #include <QTest> 0016 #include <QThread> 0017 #include <QTimer> 0018 0019 #include "../src/backends/dbus/DBusHelperProxy.h" 0020 0021 Q_DECLARE_METATYPE(KAuth::Action::AuthStatus) 0022 0023 class HelperHandler : public QObject 0024 { 0025 Q_OBJECT 0026 public: 0027 HelperHandler(); 0028 0029 private Q_SLOTS: 0030 void init(); 0031 0032 Q_SIGNALS: 0033 void ready(); 0034 0035 private: 0036 DBusHelperProxy *m_helperProxy; 0037 TestHelper *m_helper; 0038 QThread *m_thread; 0039 }; 0040 0041 class HelperTest : public QObject 0042 { 0043 Q_OBJECT 0044 0045 public: 0046 HelperTest(QObject *parent = nullptr) 0047 : QObject(parent) 0048 { 0049 } 0050 0051 private Q_SLOTS: 0052 void initTestCase(); 0053 void init() 0054 { 0055 } 0056 0057 void testBasicActionExecution(); 0058 void testExecuteJobSignals(); 0059 void testTwoCalls(); 0060 void testActionData(); 0061 void testHelperFailure(); 0062 0063 void cleanup() 0064 { 0065 } 0066 void cleanupTestCase() 0067 { 0068 } 0069 0070 Q_SIGNALS: 0071 void changeCapabilities(KAuth::AuthBackend::Capabilities capabilities); 0072 0073 private: 0074 HelperHandler *m_handler; 0075 }; 0076 0077 HelperHandler::HelperHandler() 0078 : QObject(nullptr) 0079 { 0080 /* Hello adventurer. What you see here might hurt your eyes, but let me explain why you don't want 0081 to touch this code. We are dealing with same-process async DBus requests, and if this seems obscure 0082 to you already please give up and forget about this function. 0083 Qt's local loop optimizations at the moment make it impossible to stream an async request to a process 0084 living on the same thread. So that's what we do: we instantiate a separate helperProxy and move it to 0085 a different thread - afterwards we can do everything as if we were in a separate process. 0086 0087 If you are wondering if this means we'll have two helper proxies, you are right my friend. But please 0088 remember that helperProxy acts both as a client and as a server, so it makes total sense. 0089 0090 tl;dr: Don't touch this and forget the weird questions in your head: it actually works. 0091 */ 0092 0093 m_thread = new QThread(this); 0094 moveToThread(m_thread); 0095 connect(m_thread, &QThread::started, this, &HelperHandler::init); 0096 m_thread->start(); 0097 } 0098 0099 void HelperHandler::init() 0100 { 0101 qDebug() << "Initializing helper handler"; 0102 // Set up our Helper - of course, it is not in a separate process here so we need to copy what 0103 // HelperProxy::helperMain() does 0104 m_helperProxy = new DBusHelperProxy(QDBusConnection::sessionBus()); 0105 m_helper = new TestHelper; 0106 // The timer is here just to prevent the app from crashing. 0107 QTimer *timer = new QTimer(nullptr); 0108 0109 QVERIFY(m_helperProxy->initHelper(QLatin1String("org.kde.kf5auth.autotest"))); 0110 0111 m_helperProxy->setHelperResponder(m_helper); 0112 0113 m_helper->setProperty("__KAuth_Helper_Shutdown_Timer", QVariant::fromValue(timer)); 0114 timer->setInterval(10000); 0115 timer->start(); 0116 0117 // Make BackendsManager aware 0118 BackendsManager::setProxyForThread(m_thread, m_helperProxy); 0119 0120 Q_EMIT ready(); 0121 } 0122 0123 void HelperTest::initTestCase() 0124 { 0125 connect(this, 0126 SIGNAL(changeCapabilities(KAuth::AuthBackend::Capabilities)), 0127 KAuth::BackendsManager::authBackend(), 0128 SLOT(setNewCapabilities(KAuth::AuthBackend::Capabilities))); 0129 0130 qRegisterMetaType<KAuth::Action::AuthStatus>(); 0131 qRegisterMetaType<KJob *>(); 0132 0133 // Set up our HelperHandler 0134 m_handler = new HelperHandler; 0135 QEventLoop eventLoop; 0136 connect(m_handler, &HelperHandler::ready, &eventLoop, &QEventLoop::quit); 0137 qDebug() << "Waiting for HelperHandler to be initialized"; 0138 eventLoop.exec(); 0139 } 0140 0141 void HelperTest::testBasicActionExecution() 0142 { 0143 Q_EMIT changeCapabilities(KAuth::AuthBackend::AuthorizeFromHelperCapability | KAuth::AuthBackend::CheckActionExistenceCapability); 0144 0145 KAuth::Action action(QLatin1String("org.kde.kf5auth.autotest.standardaction")); 0146 action.setHelperId(QLatin1String("org.kde.kf5auth.autotest")); 0147 QVERIFY(action.isValid()); 0148 0149 QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus); 0150 KAuth::ExecuteJob *job = action.execute(); 0151 0152 QVERIFY(job->exec()); 0153 0154 QVERIFY(!job->error()); 0155 QVERIFY(job->data().isEmpty()); 0156 } 0157 0158 void HelperTest::testExecuteJobSignals() 0159 { 0160 KAuth::Action action(QLatin1String("org.kde.kf5auth.autotest.longaction")); 0161 action.setHelperId(QLatin1String("org.kde.kf5auth.autotest")); 0162 QVERIFY(action.isValid()); 0163 0164 QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus); 0165 0166 KAuth::ExecuteJob *job = action.execute(); 0167 0168 QSignalSpy finishedSpy(job, &KJob::result); 0169 QSignalSpy newDataSpy(job, &KAuth::ExecuteJob::newData); 0170 QSignalSpy percentSpy(job, &KJob::percentChanged); 0171 QSignalSpy statusChangedSpy(job, &KAuth::ExecuteJob::statusChanged); 0172 0173 QVERIFY(job->exec()); 0174 0175 QCOMPARE(finishedSpy.size(), 1); 0176 QCOMPARE(qobject_cast<KAuth::ExecuteJob *>(finishedSpy.first().first().value<KJob *>()), job); 0177 QCOMPARE(statusChangedSpy.size(), 1); 0178 QCOMPARE(statusChangedSpy.first().first().value<KAuth::Action::AuthStatus>(), KAuth::Action::AuthorizedStatus); 0179 QCOMPARE(percentSpy.size(), 100); 0180 for (ulong i = 1; i <= 100; ++i) { 0181 QCOMPARE((unsigned long)percentSpy.at(i - 1).last().toLongLong(), i); 0182 QCOMPARE(qobject_cast<KAuth::ExecuteJob *>(percentSpy.at(i - 1).first().value<KJob *>()), job); 0183 } 0184 QCOMPARE(newDataSpy.size(), 1); 0185 QCOMPARE(newDataSpy.first().first().value<QVariantMap>().value(QLatin1String("Answer")).toInt(), 42); 0186 0187 QVERIFY(!job->error()); 0188 QVERIFY(job->data().isEmpty()); 0189 } 0190 0191 void HelperTest::testTwoCalls() 0192 { 0193 KAuth::Action action(QLatin1String("org.kde.kf5auth.autotest.standardaction")); 0194 action.setHelperId(QLatin1String("org.kde.kf5auth.autotest")); 0195 QVERIFY(action.isValid()); 0196 0197 QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus); 0198 KAuth::ExecuteJob *job = action.execute(); 0199 QVERIFY(job->exec()); 0200 0201 job = action.execute(); 0202 QVERIFY(job->exec()); 0203 } 0204 0205 void HelperTest::testActionData() 0206 { 0207 KAuth::Action action(QLatin1String("org.kde.kf5auth.autotest.echoaction")); 0208 action.setHelperId(QLatin1String("org.kde.kf5auth.autotest")); 0209 0210 QVariantMap args; 0211 // Fill with random data (and test heavy structures while we're at it) 0212 auto *generator = QRandomGenerator::global(); 0213 for (int i = 0; i < 150; ++i) { 0214 args.insert(QUuid::createUuid().toString(), generator->generate()); 0215 } 0216 action.setArguments(args); 0217 0218 QVERIFY(action.isValid()); 0219 0220 QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus); 0221 KAuth::ExecuteJob *job = action.execute(); 0222 0223 QVERIFY(job->exec()); 0224 0225 QVERIFY(!job->error()); 0226 QCOMPARE(job->data(), args); 0227 } 0228 0229 void HelperTest::testHelperFailure() 0230 { 0231 KAuth::Action action(QLatin1String("org.kde.kf5auth.autotest.failingaction")); 0232 action.setHelperId(QLatin1String("org.kde.kf5auth.autotest")); 0233 QVERIFY(action.isValid()); 0234 QCOMPARE(action.status(), KAuth::Action::AuthRequiredStatus); 0235 KAuth::ExecuteJob *job = action.execute(); 0236 QVERIFY(!job->exec()); 0237 QVERIFY(job->error()); 0238 } 0239 0240 QTEST_MAIN(HelperTest) 0241 #include "HelperTest.moc"