File indexing completed on 2024-04-28 05:50:34

0001 /*
0002     SPDX-FileCopyrightText: 2010 Kurt Hindenburg <kurt.hindenburg@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "DBusTest.h"
0009 #include "../profile/Profile.h"
0010 #include "../profile/ProfileWriter.h"
0011 #include "../session/Session.h"
0012 
0013 // Qt
0014 #include <QProcess>
0015 #include <QRandomGenerator>
0016 #include <QSignalSpy>
0017 #include <QStandardPaths>
0018 
0019 using namespace Konsole;
0020 
0021 /* Exec a new Konsole and grab its dbus */
0022 void DBusTest::initTestCase()
0023 {
0024     qRegisterMetaType<QProcess::ExitStatus>();
0025 
0026     const QString interfaceName = QStringLiteral("org.kde.konsole");
0027     QDBusConnectionInterface *bus = nullptr;
0028     QStringList konsoleServices;
0029 
0030     if (!QDBusConnection::sessionBus().isConnected() || ((bus = QDBusConnection::sessionBus().interface()) == nullptr)) {
0031         QFAIL("Session bus not found");
0032     }
0033 
0034     QDBusReply<QStringList> serviceReply = bus->registeredServiceNames();
0035     QVERIFY2(serviceReply.isValid(), "SessionBus interfaces not available");
0036 
0037     // Find all current Konsoles' services running
0038     QStringList allServices = serviceReply;
0039     for (QStringList::const_iterator it = allServices.constBegin(), end = allServices.constEnd(); it != end; ++it) {
0040         const QString service = *it;
0041         if (service.startsWith(interfaceName)) {
0042             konsoleServices << service;
0043         }
0044     }
0045 
0046     // Create test profile
0047     auto profile = Profile::Ptr(new Profile());
0048     ProfileWriter profileWriter;
0049 
0050     do {
0051         _testProfileName = QStringLiteral("konsole-dbus-test-profile-%1").arg(QRandomGenerator::global()->generate());
0052         profile->setProperty(Profile::UntranslatedName, _testProfileName);
0053         profile->setProperty(Profile::Name, _testProfileName);
0054         _testProfilePath = profileWriter.getPath(profile);
0055     } while (QFile::exists(_testProfilePath));
0056     _testProfileEnv = QStringLiteral("TEST_PROFILE=%1").arg(_testProfileName);
0057     profile->setProperty(Profile::Environment, QStringList(_testProfileEnv));
0058     // %D = Current Directory (Long) - hacky way to check current directory
0059     profile->setProperty(Profile::LocalTabTitleFormat, QStringLiteral("%D"));
0060     profileWriter.writeProfile(_testProfilePath, profile);
0061 
0062     // Create a new Konsole with a separate process id
0063     _process = new QProcess;
0064     _process->setProcessChannelMode(QProcess::ForwardedChannels);
0065     _process->start(QStringLiteral("konsole"), QStringList(QStringLiteral("--separate")));
0066 
0067     if (!_process->waitForStarted()) {
0068         QFAIL(QStringLiteral("Unable to exec a new Konsole").toLatin1().data());
0069     }
0070 
0071     // Wait for above Konsole to finish starting
0072     QThread::sleep(5);
0073 
0074     serviceReply = bus->registeredServiceNames();
0075     QVERIFY2(serviceReply.isValid(), "SessionBus interfaces not available");
0076 
0077     // Find dbus service of above Konsole
0078     allServices = serviceReply;
0079     for (QStringList::const_iterator it = allServices.constBegin(), end = allServices.constEnd(); it != end; ++it) {
0080         const QString service = *it;
0081         if (service.startsWith(interfaceName)) {
0082             if (!konsoleServices.contains(service)) {
0083                 _interfaceName = service;
0084             }
0085         }
0086     }
0087 
0088     QVERIFY2(!_interfaceName.isEmpty(), "This test will only work in a Konsole window with a new PID.  A new Konsole PID can't be found.");
0089 
0090     QDBusInterface iface(_interfaceName, QStringLiteral("/Windows/1"), QStringLiteral("org.kde.konsole.Konsole"));
0091     QVERIFY(iface.isValid());
0092 }
0093 
0094 /* Close the Konsole window that was opened to test the DBus interface
0095  */
0096 void DBusTest::cleanupTestCase()
0097 {
0098     // Remove test profile
0099     QFile::remove(_testProfilePath);
0100 
0101     // Need to take care of when user has CloseAllTabs=False otherwise
0102     // they will get a popup dialog when we try to close this.
0103     QSignalSpy quitSpy(_process, SIGNAL(finished(int, QProcess::ExitStatus)));
0104 
0105     // Do not use QWidget::close(), as it shows question popup when
0106     // CloseAllTabs is set to false (default).
0107     QDBusInterface iface(_interfaceName, QStringLiteral("/MainApplication"), QStringLiteral("org.qtproject.Qt.QCoreApplication"));
0108     QVERIFY2(iface.isValid(), "Unable to get a dbus interface to Konsole!");
0109 
0110     QDBusReply<void> instanceReply = iface.call(QStringLiteral("quit"));
0111     if (!instanceReply.isValid()) {
0112         QFAIL(QStringLiteral("Unable to close Konsole: %1").arg(instanceReply.error().message()).toLatin1().data());
0113     }
0114     quitSpy.wait();
0115     _process->kill();
0116     _process->deleteLater();
0117 }
0118 
0119 void DBusTest::testSessions()
0120 {
0121     QDBusReply<void> voidReply;
0122     QDBusReply<bool> boolReply;
0123     QDBusReply<QByteArray> arrayReply;
0124     QDBusReply<QString> stringReply;
0125     QDBusReply<QStringList> listReply;
0126 
0127     QDBusInterface iface(_interfaceName, QStringLiteral("/Sessions/1"), QStringLiteral("org.kde.konsole.Session"));
0128     QVERIFY(iface.isValid());
0129 
0130     //****************** Test is/set MonitorActivity
0131     voidReply = iface.call(QStringLiteral("setMonitorActivity"), false);
0132     QVERIFY(voidReply.isValid());
0133 
0134     boolReply = iface.call(QStringLiteral("isMonitorActivity"));
0135     QVERIFY(boolReply.isValid());
0136     QCOMPARE(boolReply.value(), false);
0137 
0138     voidReply = iface.call(QStringLiteral("setMonitorActivity"), true);
0139     QVERIFY(voidReply.isValid());
0140 
0141     boolReply = iface.call(QStringLiteral("isMonitorActivity"));
0142     QVERIFY(boolReply.isValid());
0143     QCOMPARE(boolReply.value(), true);
0144 
0145     //****************** Test is/set MonitorSilence
0146     voidReply = iface.call(QStringLiteral("setMonitorSilence"), false);
0147     QVERIFY(voidReply.isValid());
0148 
0149     boolReply = iface.call(QStringLiteral("isMonitorSilence"));
0150     QVERIFY(boolReply.isValid());
0151     QCOMPARE(boolReply.value(), false);
0152 
0153     voidReply = iface.call(QStringLiteral("setMonitorSilence"), true);
0154     QVERIFY(voidReply.isValid());
0155 
0156     boolReply = iface.call(QStringLiteral("isMonitorSilence"));
0157     QVERIFY(boolReply.isValid());
0158     QCOMPARE(boolReply.value(), true);
0159 
0160     //****************** Test codec and setCodec
0161     arrayReply = iface.call(QStringLiteral("codec"));
0162     QVERIFY(arrayReply.isValid());
0163 
0164     // Obtain a list of system's Codecs
0165     QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
0166     for (int i = 0; i < availableCodecs.count(); ++i) {
0167         boolReply = iface.call(QStringLiteral("setCodec"), availableCodecs[i]);
0168         QVERIFY(boolReply.isValid());
0169         QCOMPARE(boolReply.value(), true);
0170 
0171         arrayReply = iface.call(QStringLiteral("codec"));
0172         QVERIFY(arrayReply.isValid());
0173         // Compare result with name due to aliases issue
0174         // Better way to do this?
0175         QCOMPARE((QTextCodec::codecForName(arrayReply.value()))->name(), (QTextCodec::codecForName(availableCodecs[i]))->name());
0176     }
0177 
0178     //****************** Test is/set flowControlEnabled
0179     voidReply = iface.call(QStringLiteral("setFlowControlEnabled"), true);
0180     QVERIFY(voidReply.isValid());
0181 
0182     boolReply = iface.call(QStringLiteral("flowControlEnabled"));
0183     QVERIFY(boolReply.isValid());
0184     QCOMPARE(boolReply.value(), true);
0185 
0186     voidReply = iface.call(QStringLiteral("setFlowControlEnabled"), false);
0187     QVERIFY(voidReply.isValid());
0188 
0189     boolReply = iface.call(QStringLiteral("flowControlEnabled"));
0190     QVERIFY(boolReply.isValid());
0191     QCOMPARE(boolReply.value(), false);
0192 
0193     //****************** Test is/set environment
0194     listReply = iface.call(QStringLiteral("environment"));
0195     QVERIFY(listReply.isValid());
0196 
0197     QStringList prevEnv = listReply.value();
0198     // for (int i = 0; i < prevEnv.size(); ++i)
0199     //    qDebug()<< prevEnv.at(i).toLocal8Bit().constData() << endl;
0200 
0201     voidReply = iface.call(QStringLiteral("setEnvironment"), QStringList());
0202     QVERIFY(voidReply.isValid());
0203 
0204     listReply = iface.call(QStringLiteral("environment"));
0205     QVERIFY(listReply.isValid());
0206     QCOMPARE(listReply.value(), QStringList());
0207 
0208     voidReply = iface.call(QStringLiteral("setEnvironment"), prevEnv);
0209     QVERIFY(voidReply.isValid());
0210 
0211     listReply = iface.call(QStringLiteral("environment"));
0212     QVERIFY(listReply.isValid());
0213     QCOMPARE(listReply.value(), prevEnv);
0214 
0215     //****************** Test is/set title
0216     // TODO: Consider checking what is in Profile
0217     stringReply = iface.call(QStringLiteral("title"), Session::NameRole);
0218     QVERIFY(stringReply.isValid());
0219 
0220     // set title to,  what title should be
0221     QMap<QString, QString> titleMap;
0222     titleMap[QStringLiteral("Shell")] = QStringLiteral("Shell");
0223 
0224     // BUG: It appears that Session::LocalTabTitle is set to Shell and
0225     // doesn't change.  While RemoteTabTitle is actually the LocalTabTitle
0226     // TODO: Figure out what's going on...
0227     QMapIterator<QString, QString> i(titleMap);
0228     while (i.hasNext()) {
0229         i.next();
0230         voidReply = iface.call(QStringLiteral("setTitle"), Session::NameRole, i.key());
0231         QVERIFY(voidReply.isValid());
0232 
0233         stringReply = iface.call(QStringLiteral("title"), Session::NameRole);
0234         QVERIFY(stringReply.isValid());
0235 
0236         QCOMPARE(stringReply.value(), i.value());
0237     }
0238 }
0239 
0240 void DBusTest::testWindows()
0241 {
0242     // Tested functions:
0243     // [+] int sessionCount();
0244     // [+] QStringList sessionList();
0245     // [+] int currentSession();
0246     // [+] void setCurrentSession(int sessionId);
0247     // [+] int newSession();
0248     // [+] int newSession(const QString &profile);
0249     // [+] int newSession(const QString &profile, const QString &directory);
0250     // [ ] QString defaultProfile();
0251     // [ ] QStringList profileList();
0252     // [ ] void nextSession();
0253     // [ ] void prevSession();
0254     // [ ] void moveSessionLeft();
0255     // [ ] void moveSessionRight();
0256     // [ ] void setTabWidthToText(bool);
0257 
0258     QDBusReply<int> intReply;
0259     QDBusReply<QStringList> listReply;
0260     QDBusReply<void> voidReply;
0261     QDBusReply<QString> stringReply;
0262 
0263     int sessionCount = -1;
0264     int initialSessionId = -1;
0265 
0266     QDBusInterface iface(_interfaceName, QStringLiteral("/Windows/1"), QStringLiteral("org.kde.konsole.Window"));
0267     QVERIFY(iface.isValid());
0268 
0269     intReply = iface.call(QStringLiteral("sessionCount"));
0270     QVERIFY(intReply.isValid());
0271     sessionCount = intReply.value();
0272     QVERIFY(sessionCount > 0);
0273 
0274     intReply = iface.call(QStringLiteral("currentSession"));
0275     QVERIFY(intReply.isValid());
0276     initialSessionId = intReply.value();
0277 
0278     intReply = iface.call(QStringLiteral("newSession"));
0279     QVERIFY(intReply.isValid());
0280     ++sessionCount;
0281     int newSessionId = intReply.value();
0282     QVERIFY(newSessionId != initialSessionId);
0283 
0284     listReply = iface.call(QStringLiteral("sessionList"));
0285     QVERIFY(listReply.isValid());
0286 
0287     QStringList sessions = listReply.value();
0288     QVERIFY(sessions.contains(QString::number(initialSessionId)));
0289     QVERIFY(sessions.contains(QString::number(newSessionId)));
0290     QVERIFY(sessions.size() == sessionCount);
0291 
0292     intReply = iface.call(QStringLiteral("newSession"), _testProfileName);
0293     QVERIFY(intReply.isValid());
0294     ++sessionCount;
0295     newSessionId = intReply.value();
0296     QVERIFY(newSessionId != initialSessionId);
0297     {
0298         QDBusInterface sessionIface(_interfaceName, QStringLiteral("/Sessions/%1").arg(newSessionId), QStringLiteral("org.kde.konsole.Session"));
0299         QVERIFY(iface.isValid());
0300 
0301         listReply = sessionIface.call(QStringLiteral("environment"));
0302         QVERIFY(listReply.isValid());
0303         QStringList env = listReply.value();
0304         QVERIFY(env.contains(_testProfileEnv));
0305     }
0306 
0307     const auto tempDirectories = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
0308     const QString sessionDirectory = tempDirectories.empty() ? QStringLiteral("/") : tempDirectories.constFirst();
0309     intReply = iface.call(QStringLiteral("newSession"), _testProfileName, sessionDirectory);
0310     QVERIFY(intReply.isValid());
0311     ++sessionCount;
0312     newSessionId = intReply.value();
0313     QVERIFY(newSessionId != initialSessionId);
0314     {
0315         QDBusInterface sessionIface(_interfaceName, QStringLiteral("/Sessions/%1").arg(newSessionId), QStringLiteral("org.kde.konsole.Session"));
0316         QVERIFY(iface.isValid());
0317 
0318         listReply = sessionIface.call(QStringLiteral("environment"));
0319         QVERIFY(listReply.isValid());
0320         QStringList env = listReply.value();
0321         QVERIFY(env.contains(_testProfileEnv));
0322 
0323         // Apparently there's no function for checking CWD.
0324         // The profile uses "%D" as title format, so check the title
0325         stringReply = sessionIface.call(QStringLiteral("title"), Session::DisplayedTitleRole);
0326         QVERIFY(stringReply.isValid());
0327         QVERIFY(QDir(stringReply.value()) == QDir(sessionDirectory));
0328     }
0329 
0330     voidReply = iface.call(QStringLiteral("setCurrentSession"), initialSessionId);
0331     QVERIFY(voidReply.isValid());
0332 
0333     intReply = iface.call(QStringLiteral("currentSession"));
0334     QVERIFY(intReply.isValid());
0335     QVERIFY(intReply.value() == initialSessionId);
0336 
0337     intReply = iface.call(QStringLiteral("sessionCount"));
0338     QVERIFY(intReply.isValid());
0339     QVERIFY(intReply.value() == sessionCount);
0340 }
0341 
0342 QTEST_MAIN(DBusTest)
0343 
0344 #include "moc_DBusTest.cpp"