File indexing completed on 2024-04-14 14:17:57

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"