File indexing completed on 2024-04-21 16:17:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-or-later
0005  */
0006 
0007 #include <QCoreApplication>
0008 #include <QDBusConnectionInterface>
0009 #include <QObject>
0010 #include <QSignalSpy>
0011 #include <QtTest>
0012 
0013 #include "../src/backendmanager_p.h"
0014 #include "../src/config.h"
0015 #include "../src/configmonitor.h"
0016 #include "../src/getconfigoperation.h"
0017 #include "../src/mode.h"
0018 #include "../src/output.h"
0019 #include "../src/setconfigoperation.h"
0020 
0021 Q_LOGGING_CATEGORY(KSCREEN, "kscreen")
0022 
0023 using namespace KScreen;
0024 
0025 class TestInProcess : public QObject
0026 {
0027     Q_OBJECT
0028 
0029 public:
0030     explicit TestInProcess(QObject *parent = nullptr);
0031 
0032 private Q_SLOTS:
0033     void initTestCase();
0034 
0035     void init();
0036     void cleanup();
0037 
0038     void loadConfig();
0039 
0040     void testCreateJob();
0041     void testModeSwitching();
0042     void testBackendCaching();
0043 
0044     void testConfigApply();
0045     void testConfigMonitor();
0046 
0047 private:
0048     ConfigPtr m_config;
0049     bool m_backendServiceInstalled = false;
0050 };
0051 
0052 TestInProcess::TestInProcess(QObject *parent)
0053     : QObject(parent)
0054     , m_config(nullptr)
0055 {
0056 }
0057 
0058 void TestInProcess::initTestCase()
0059 {
0060     m_backendServiceInstalled = true;
0061 
0062     const QString kscreenServiceName = QStringLiteral("org.kde.KScreen");
0063     QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
0064     if (!bus->isServiceRegistered(kscreenServiceName)) {
0065         auto reply = bus->startService(kscreenServiceName);
0066         if (!reply.isValid()) {
0067             qDebug() << "D-Bus service org.kde.KScreen could not be started, skipping out-of-process tests";
0068             m_backendServiceInstalled = false;
0069         }
0070     }
0071 }
0072 
0073 void TestInProcess::init()
0074 {
0075     qputenv("KSCREEN_LOGGING", "false");
0076     // Make sure we do everything in-process
0077     qputenv("KSCREEN_BACKEND_INPROCESS", "1");
0078     // Use Fake backend with one of the json configs
0079     qputenv("KSCREEN_BACKEND", "Fake");
0080 
0081     KScreen::BackendManager::instance()->shutdownBackend();
0082     KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "multipleoutput.json"}});
0083 }
0084 
0085 void TestInProcess::cleanup()
0086 {
0087     KScreen::BackendManager::instance()->shutdownBackend();
0088 }
0089 
0090 void TestInProcess::loadConfig()
0091 {
0092     qputenv("KSCREEN_BACKEND_INPROCESS", "1");
0093     BackendManager::instance()->setMethod(BackendManager::InProcess);
0094 
0095     auto *op = new GetConfigOperation();
0096     QVERIFY(op->exec());
0097     m_config = op->config();
0098     QVERIFY(m_config);
0099     QVERIFY(m_config->isValid());
0100 }
0101 
0102 void TestInProcess::testModeSwitching()
0103 {
0104     KScreen::BackendManager::instance()->shutdownBackend();
0105     BackendManager::instance()->setMethod(BackendManager::InProcess);
0106     // Load QScreen backend in-process
0107     qDebug() << "TT qscreen in-process";
0108     qputenv("KSCREEN_BACKEND", "QScreen");
0109     auto op = new GetConfigOperation();
0110     QVERIFY(op->exec());
0111     auto oc = op->config();
0112     QVERIFY(oc != nullptr);
0113     QVERIFY(oc->isValid());
0114 
0115     qDebug() << "TT fake in-process";
0116     // Load the Fake backend in-process
0117     qputenv("KSCREEN_BACKEND", "Fake");
0118     auto ip = new GetConfigOperation();
0119     QVERIFY(ip->exec());
0120     auto ic = ip->config();
0121     QVERIFY(ic != nullptr);
0122     QVERIFY(ic->isValid());
0123     QVERIFY(ic->outputs().count());
0124 
0125     KScreen::ConfigPtr xc(nullptr);
0126     if (m_backendServiceInstalled) {
0127         qDebug() << "TT xrandr out-of-process";
0128         // Load the xrandr backend out-of-process
0129         qputenv("KSCREEN_BACKEND", "QScreen");
0130         qputenv("KSCREEN_BACKEND_INPROCESS", "0");
0131         BackendManager::instance()->setMethod(BackendManager::OutOfProcess);
0132         auto xp = new GetConfigOperation();
0133         QCOMPARE(BackendManager::instance()->method(), BackendManager::OutOfProcess);
0134         QVERIFY(xp->exec());
0135         xc = xp->config();
0136         QVERIFY(xc != nullptr);
0137         QVERIFY(xc->isValid());
0138         QVERIFY(xc->outputs().count());
0139     }
0140 
0141     qDebug() << "TT fake in-process";
0142 
0143     qputenv("KSCREEN_BACKEND_INPROCESS", "1");
0144     BackendManager::instance()->setMethod(BackendManager::InProcess);
0145     // Load the Fake backend in-process
0146     qputenv("KSCREEN_BACKEND", "Fake");
0147     auto fp = new GetConfigOperation();
0148     QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess);
0149     QVERIFY(fp->exec());
0150     auto fc = fp->config();
0151     QVERIFY(fc != nullptr);
0152     QVERIFY(fc->isValid());
0153     QVERIFY(fc->outputs().count());
0154 
0155     QVERIFY(oc->isValid());
0156     QVERIFY(ic->isValid());
0157     if (xc) {
0158         QVERIFY(xc->isValid());
0159     }
0160     QVERIFY(fc->isValid());
0161 }
0162 
0163 void TestInProcess::testBackendCaching()
0164 {
0165     KScreen::BackendManager::instance()->shutdownBackend();
0166     qputenv("KSCREEN_BACKEND", "Fake");
0167     QElapsedTimer t;
0168     BackendManager::instance()->setMethod(BackendManager::InProcess);
0169     QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess);
0170     int t_cold;
0171     int t_warm;
0172 
0173     {
0174         t.start();
0175         auto cp = new GetConfigOperation();
0176         cp->exec();
0177         auto cc = cp->config();
0178         t_cold = t.nsecsElapsed();
0179         QVERIFY(cc != nullptr);
0180         QVERIFY(cc->isValid());
0181         QVERIFY(cc->outputs().count());
0182     }
0183     {
0184         // KScreen::BackendManager::instance()->shutdownBackend();
0185         QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess);
0186         t.start();
0187         auto cp = new GetConfigOperation();
0188         cp->exec();
0189         auto cc = cp->config();
0190         t_warm = t.nsecsElapsed();
0191         QVERIFY(cc != nullptr);
0192         QVERIFY(cc->isValid());
0193         QVERIFY(cc->outputs().count());
0194     }
0195     {
0196         auto cp = new GetConfigOperation();
0197         QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess);
0198         cp->exec();
0199         auto cc = cp->config();
0200         QVERIFY(cc != nullptr);
0201         QVERIFY(cc->isValid());
0202         QVERIFY(cc->outputs().count());
0203     }
0204     // Check if all our configs are still valid after the backend is gone
0205     KScreen::BackendManager::instance()->shutdownBackend();
0206 
0207     if (m_backendServiceInstalled) {
0208         // qputenv("KSCREEN_BACKEND", "QScreen");
0209         qputenv("KSCREEN_BACKEND_INPROCESS", "0");
0210         BackendManager::instance()->setMethod(BackendManager::OutOfProcess);
0211         QCOMPARE(BackendManager::instance()->method(), BackendManager::OutOfProcess);
0212         int t_x_cold;
0213 
0214         {
0215             t.start();
0216             auto xp = new GetConfigOperation();
0217             xp->exec();
0218             t_x_cold = t.nsecsElapsed();
0219             auto xc = xp->config();
0220             QVERIFY(xc != nullptr);
0221         }
0222         t.start();
0223         auto xp = new GetConfigOperation();
0224         xp->exec();
0225         int t_x_warm = t.nsecsElapsed();
0226         auto xc = xp->config();
0227         QVERIFY(xc != nullptr);
0228 
0229         // Make sure in-process is faster
0230         QVERIFY(t_cold > t_warm);
0231         QVERIFY(t_x_cold > t_x_warm);
0232         QVERIFY(t_x_cold > t_cold);
0233         return;
0234         qDebug() << "ip  speedup for cached access:" << (qreal)((qreal)t_cold / (qreal)t_warm);
0235         qDebug() << "oop speedup for cached access:" << (qreal)((qreal)t_x_cold / (qreal)t_x_warm);
0236         qDebug() << "out-of vs. in-process speedup:" << (qreal)((qreal)t_x_warm / (qreal)t_warm);
0237         qDebug() << "cold oop:   " << ((qreal)t_x_cold / 1000000);
0238         qDebug() << "cached oop: " << ((qreal)t_x_warm / 1000000);
0239         qDebug() << "cold in process:   " << ((qreal)t_cold / 1000000);
0240         qDebug() << "cached in process: " << ((qreal)t_warm / 1000000);
0241     }
0242 }
0243 
0244 void TestInProcess::testCreateJob()
0245 {
0246     KScreen::BackendManager::instance()->shutdownBackend();
0247     {
0248         BackendManager::instance()->setMethod(BackendManager::InProcess);
0249         auto op = new GetConfigOperation();
0250         auto _op = qobject_cast<GetConfigOperation *>(op);
0251         QVERIFY(_op != nullptr);
0252         QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess);
0253         QVERIFY(op->exec());
0254         auto cc = op->config();
0255         QVERIFY(cc != nullptr);
0256         QVERIFY(cc->isValid());
0257     }
0258     if (m_backendServiceInstalled) {
0259         BackendManager::instance()->setMethod(BackendManager::OutOfProcess);
0260         auto op = new GetConfigOperation();
0261         auto _op = qobject_cast<GetConfigOperation *>(op);
0262         QVERIFY(_op != nullptr);
0263         QCOMPARE(BackendManager::instance()->method(), BackendManager::OutOfProcess);
0264         QVERIFY(op->exec());
0265         auto cc = op->config();
0266         QVERIFY(cc != nullptr);
0267         QVERIFY(cc->isValid());
0268     }
0269     KScreen::BackendManager::instance()->shutdownBackend();
0270     BackendManager::instance()->setMethod(BackendManager::InProcess);
0271 }
0272 
0273 void TestInProcess::testConfigApply()
0274 {
0275     qputenv("KSCREEN_BACKEND", "Fake");
0276     KScreen::BackendManager::instance()->shutdownBackend();
0277     BackendManager::instance()->setMethod(BackendManager::InProcess);
0278     auto op = new GetConfigOperation();
0279     op->exec();
0280     auto config = op->config();
0281     //     qDebug() << "op:" << config->outputs().count();
0282     auto output = config->outputs().first();
0283     //     qDebug() << "res:" << output->geometry();
0284     //     qDebug() << "modes:" << output->modes();
0285     auto m0 = output->modes().first();
0286     // qDebug() << "m0:" << m0->id() << m0;
0287     output->setCurrentModeId(m0->id());
0288     QVERIFY(Config::canBeApplied(config));
0289 
0290     // expected to fail, SetConfigOperation is out-of-process only
0291     auto setop = new SetConfigOperation(config);
0292     QVERIFY(!setop->hasError());
0293     QVERIFY(setop->exec());
0294 
0295     QVERIFY(!setop->hasError());
0296 }
0297 
0298 void TestInProcess::testConfigMonitor()
0299 {
0300     qputenv("KSCREEN_BACKEND", "Fake");
0301 
0302     KScreen::BackendManager::instance()->shutdownBackend();
0303     BackendManager::instance()->setMethod(BackendManager::InProcess);
0304     auto op = new GetConfigOperation();
0305     op->exec();
0306     auto config = op->config();
0307     //     qDebug() << "op:" << config->outputs().count();
0308     auto output = config->outputs().first();
0309     //     qDebug() << "res:" << output->geometry();
0310     //     qDebug() << "modes:" << output->modes();
0311     auto m0 = output->modes().first();
0312     // qDebug() << "m0:" << m0->id() << m0;
0313     output->setCurrentModeId(m0->id());
0314     QVERIFY(Config::canBeApplied(config));
0315 
0316     QSignalSpy monitorSpy(ConfigMonitor::instance(), &ConfigMonitor::configurationChanged);
0317     qDebug() << "MOnitorspy connencted.";
0318     ConfigMonitor::instance()->addConfig(config);
0319 
0320     auto setop = new SetConfigOperation(config);
0321     QVERIFY(!setop->hasError());
0322     // do not cal setop->exec(), this must not block as the signalspy already blocks
0323     QVERIFY(monitorSpy.wait(500));
0324 }
0325 
0326 QTEST_GUILESS_MAIN(TestInProcess)
0327 
0328 #include "testinprocess.moc"