1 /*
2 SPDX-FileCopyrightText: 2014 Kurt Hindenburg <kurt.hindenburg@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 // Own
8 #include "TerminalInterfaceTest.h"
9 #include "../profile/Profile.h"
10 #include "../profile/ProfileManager.h"
11 #include "config-konsole.h"
12
13 // Qt
14 #include <QDebug>
15 #include <QDir>
16 #include <QSignalSpy>
17
18 // KDE
19 #include <KPluginFactory>
20 #include <KPluginLoader>
21 #include <qtest.h>
22
23 using namespace Konsole;
24
25 /* TerminalInterface found in KParts/kde_terminal_interface.h
26 *
27 * void startProgram(const QString &program,
28 * const QStringList &args)
29 * void showShellInDir(const QString &dir)
30 * void sendInput(const QString &text)
31 * int terminalProcessId()
32 * int foregroundProcessId()
33 * QString foregroundProcessName()
34 * QString currentWorkingDirectory() const
35 */
36
initTestCase()37 void TerminalInterfaceTest::initTestCase()
38 {
39 /* Try to test against build konsolepart, so move directory containing
40 executable to front of libraryPaths. KPluginLoader should find the
41 part first in the build dir over the system installed ones.
42 I believe the CI installs first and then runs the test so the other
43 paths can not be removed.
44 */
45 const auto libraryPaths = QCoreApplication::libraryPaths();
46 auto buildPath = libraryPaths.last();
47 QCoreApplication::removeLibraryPath(buildPath);
48 // konsolepart.so is in ../autotests/
49 if (buildPath.endsWith(QStringLiteral("/autotests"))) {
50 buildPath.chop(10);
51 }
52 QCoreApplication::addLibraryPath(buildPath);
53 }
54
55 // Test with no shell running
testTerminalInterfaceNoShell()56 void TerminalInterfaceTest::testTerminalInterfaceNoShell()
57 {
58 // create a Konsole part and attempt to connect to it
59 _terminalPart = createPart();
60 if (_terminalPart == nullptr) {
61 QFAIL("konsolepart not found.");
62 }
63
64 TerminalInterface *terminal = qobject_cast<TerminalInterface *>(_terminalPart);
65 QVERIFY(terminal);
66
67 #if !defined(Q_OS_FREEBSD)
68 // Skip this for now on FreeBSD
69 // -1 is current foreground process and name for process 0 is "kernel"
70
71 // Verify results when no shell running
72 int terminalProcessId = terminal->terminalProcessId();
73 QCOMPARE(terminalProcessId, 0);
74 int foregroundProcessId = terminal->foregroundProcessId();
75 QCOMPARE(foregroundProcessId, -1);
76 QString foregroundProcessName = terminal->foregroundProcessName();
77 QCOMPARE(foregroundProcessName, QString());
78 const QString currentWorkingDirectory = terminal->currentWorkingDirectory();
79 QCOMPARE(currentWorkingDirectory, QString());
80
81 #endif
82 delete _terminalPart;
83 }
84
85 // Test with default shell running
testTerminalInterface()86 void TerminalInterfaceTest::testTerminalInterface()
87 {
88 QString currentDirectory;
89
90 // create a Konsole part and attempt to connect to it
91 _terminalPart = createPart();
92 if (_terminalPart == nullptr) {
93 QFAIL("konsolepart not found.");
94 }
95
96 TerminalInterface *terminal = qobject_cast<TerminalInterface *>(_terminalPart);
97 QVERIFY(terminal);
98
99 // Start a shell in given directory
100 terminal->showShellInDir(QDir::home().path());
101
102 // After fa398f56, the CI test failed; also the KF was updated on that build.
103 // TODO: research this more
104 #if defined(Q_OS_FREEBSD)
105 return;
106 #endif
107 // Skip this for now on FreeBSD
108 // -1 is current foreground process and name for process 0 is "kernel"
109
110 int foregroundProcessId = terminal->foregroundProcessId();
111 QCOMPARE(foregroundProcessId, -1);
112 QString foregroundProcessName = terminal->foregroundProcessName();
113 QCOMPARE(foregroundProcessName, QString());
114
115 // terminalProcessId() is the user's default shell
116 // FIXME: find a way to verify this
117 // int terminalProcessId = terminal->terminalProcessId();
118
119 // Let's try using QSignalSpy
120 // https://community.kde.org/Guidelines_and_HOWTOs/UnitTests
121 // QSignalSpy is really a QList of QLists, so we take the first
122 // list, which corresponds to the arguments for the first signal
123 // we caught.
124
125 QSignalSpy stateSpy(_terminalPart, SIGNAL(currentDirectoryChanged(QString)));
126 QVERIFY(stateSpy.isValid());
127
128 // Now we check to make sure we don't have any signals already
129 QCOMPARE(stateSpy.count(), 0);
130
131 // Let's trigger some signals
132
133 // #1A - Test signal currentDirectoryChanged(QString)
134 currentDirectory = QStringLiteral("/tmp");
135 terminal->sendInput(QStringLiteral("cd ") + currentDirectory + QLatin1Char('\n'));
136 stateSpy.wait(2000);
137 QCOMPARE(stateSpy.count(), 1);
138
139 // Correct result?
140 QList<QVariant> firstSignalArgs = stateSpy.takeFirst();
141
142 // Actual: /Users/kurthindenburg
143 // Expected: /tmp
144 #if !defined(Q_OS_MACOS)
145 QString firstSignalState = firstSignalArgs.at(0).toString();
146 QCOMPARE(firstSignalState, currentDirectory);
147
148 const QString currentWorkingDirectory = terminal->currentWorkingDirectory();
149 QCOMPARE(currentWorkingDirectory, currentDirectory);
150
151 // #1B - Test signal currentDirectoryChanged(QString)
152 // Invalid directory - no signal should be emitted
153 terminal->sendInput(QStringLiteral("cd /usrADADFASDF\n"));
154 stateSpy.wait(2000);
155 QCOMPARE(stateSpy.count(), 0);
156
157 // Should be no change since the above cd didn't work
158 const QString currentWorkingDirectory2 = terminal->currentWorkingDirectory();
159 QCOMPARE(currentWorkingDirectory2, currentDirectory);
160
161 // Test starting a new program
162 QString command = QStringLiteral("top");
163 terminal->sendInput(command + QLatin1Char('\n'));
164 stateSpy.wait(2000);
165 // FIXME: find a good way to validate process id of 'top'
166 foregroundProcessId = terminal->foregroundProcessId();
167 QVERIFY(foregroundProcessId != -1);
168 foregroundProcessName = terminal->foregroundProcessName();
169 QCOMPARE(foregroundProcessName, command);
170
171 terminal->sendInput(QStringLiteral("q"));
172 stateSpy.wait(2000);
173
174 // Nothing running in foreground
175 foregroundProcessId = terminal->foregroundProcessId();
176 QCOMPARE(foregroundProcessId, -1);
177 foregroundProcessName = terminal->foregroundProcessName();
178 QCOMPARE(foregroundProcessName, QString());
179 #endif
180
181 // Test destroyed()
182 QSignalSpy destroyedSpy(_terminalPart, SIGNAL(destroyed()));
183 QVERIFY(destroyedSpy.isValid());
184
185 // Now we check to make sure we don't have any signals already
186 QCOMPARE(destroyedSpy.count(), 0);
187
188 delete _terminalPart;
189 QCOMPARE(destroyedSpy.count(), 1);
190 }
191
testTerminalInterfaceV2()192 void TerminalInterfaceTest::testTerminalInterfaceV2()
193 {
194 #ifdef USE_TERMINALINTERFACEV2
195 Profile::Ptr testProfile(new Profile);
196 testProfile->useFallback();
197 ProfileManager::instance()->addProfile(testProfile);
198
199 _terminalPart = createPart();
200 if (_terminalPart == nullptr) {
201 QFAIL("konsolepart not found.");
202 }
203
204 TerminalInterfaceV2 *terminal = qobject_cast<TerminalInterfaceV2 *>(_terminalPart);
205
206 QVERIFY(terminal);
207 QVERIFY(terminal->setCurrentProfile(testProfile->name()));
208 QCOMPARE(terminal->currentProfileName(), testProfile->name());
209
210 QCOMPARE(terminal->profileProperty(QStringLiteral("Path")), testProfile->path());
211 QCOMPARE(terminal->profileProperty(QStringLiteral("SilenceSeconds")), testProfile->silenceSeconds());
212 QCOMPARE(terminal->profileProperty(QStringLiteral("Icon")), testProfile->icon());
213 QCOMPARE(terminal->profileProperty(QStringLiteral("ShowTerminalSizeHint")), testProfile->showTerminalSizeHint());
214 QCOMPARE(terminal->profileProperty(QStringLiteral("Environment")), testProfile->environment());
215 QCOMPARE(terminal->profileProperty(QStringLiteral("BellMode")), testProfile->property<QVariant>(Profile::Property::BellMode));
216 #else
217 QSKIP("TerminalInterfaceV2 not enabled", SkipSingle);
218 #endif
219 }
220
createPart()221 KParts::Part *TerminalInterfaceTest::createPart()
222 {
223 auto konsolePartPlugin = KPluginLoader::findPlugin(QStringLiteral("konsolepart"));
224 if (konsolePartPlugin.isNull()) {
225 return nullptr;
226 }
227
228 KPluginFactory *factory = KPluginLoader(konsolePartPlugin).factory();
229 if (factory == nullptr) { // not found
230 return nullptr;
231 }
232
233 auto *terminalPart = factory->create<KParts::Part>(this);
234
235 return terminalPart;
236 }
237
238 QTEST_MAIN(TerminalInterfaceTest)
239