File indexing completed on 2024-04-28 15:29:30
0001 /* 0002 This file is part of the KDE libraries 0003 0004 SPDX-FileCopyrightText: 2007 Oswald Buddenhagen <ossi@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "kptyprocesstest.h" 0010 0011 #include <QDebug> 0012 #include <QSignalSpy> 0013 #include <QStandardPaths> 0014 #include <QTest> 0015 #include <QThread> 0016 #include <kptydevice.h> 0017 0018 void KPtyProcessTest::test_suspend_pty() 0019 { 0020 KPtyProcess p; 0021 p.setPtyChannels(KPtyProcess::AllChannels); 0022 p.setProgram("/bin/sh", 0023 QStringList() << "-c" 0024 << "while true; do echo KPtyProcess_test; sleep 1; done"); 0025 p.start(); 0026 0027 // verify that data is available to read from the pty 0028 QVERIFY(p.pty()->waitForReadyRead(1500)); 0029 0030 // suspend the pty device and read all available data from it 0031 p.pty()->setSuspended(true); 0032 QVERIFY(p.pty()->isSuspended()); 0033 p.pty()->readAll(); 0034 0035 // verify that no data was read by the pty 0036 QVERIFY(!p.pty()->waitForReadyRead(1500)); 0037 0038 // allow process to write more data 0039 p.pty()->setSuspended(false); 0040 QVERIFY(!p.pty()->isSuspended()); 0041 0042 // verify that data is available once more 0043 QVERIFY(p.pty()->waitForReadyRead(2000)); 0044 p.pty()->readAll(); 0045 0046 p.terminate(); 0047 p.waitForFinished(); 0048 } 0049 0050 void KPtyProcessTest::test_shared_pty() 0051 { 0052 // start a first process 0053 KPtyProcess p; 0054 p.setProgram("cat"); 0055 p.setPtyChannels(KPtyProcess::AllChannels); 0056 p.pty()->setEcho(false); 0057 p.start(); 0058 0059 // start a second process using the first one's fd 0060 int fd = p.pty()->masterFd(); 0061 0062 KPtyProcess p2(fd); 0063 p2.setProgram("echo", QStringList() << "hello from me"); 0064 p2.setPtyChannels(KPtyProcess::AllChannels); 0065 p2.pty()->setEcho(false); 0066 p2.start(); 0067 0068 // read the second processes greeting from the first process' pty 0069 for (int i = 0; i < 5; ++i) { 0070 QVERIFY(p.pty()->waitForReadyRead(500)); 0071 if (p.pty()->canReadLine()) { 0072 break; 0073 } 0074 } 0075 QCOMPARE(p.pty()->readAll(), QByteArray("hello from me\r\n")); 0076 0077 // write to the second process' pty 0078 p2.pty()->write("hello from process 2\n"); 0079 QVERIFY(p2.pty()->waitForBytesWritten(1000)); 0080 0081 // read the result back from the first process' pty 0082 for (int i = 0; i < 5; ++i) { 0083 QVERIFY(p.pty()->waitForReadyRead(500)); 0084 if (p.pty()->canReadLine()) { 0085 break; 0086 } 0087 } 0088 QCOMPARE(p.pty()->readAll(), QByteArray("hello from process 2\r\n")); 0089 0090 // write to the first process' pty 0091 p.pty()->write("hi from process 1\n"); 0092 QVERIFY(p.pty()->waitForBytesWritten(1000)); 0093 0094 // read the result back from the second process' pty 0095 for (int i = 0; i < 5; ++i) { 0096 QVERIFY(p2.pty()->waitForReadyRead(500)); 0097 if (p2.pty()->canReadLine()) { 0098 break; 0099 } 0100 } 0101 QCOMPARE(p2.pty()->readAll(), QByteArray("hi from process 1\r\n")); 0102 0103 // cleanup 0104 p.terminate(); 0105 p2.terminate(); 0106 p.waitForFinished(1000); 0107 p2.waitForFinished(1000); 0108 } 0109 0110 void KPtyProcessTest::test_pty_basic() 0111 { 0112 const QString bash = QStandardPaths::findExecutable("bash"); 0113 if (bash.isEmpty()) { 0114 QSKIP("bash is not installed"); 0115 } 0116 0117 #ifdef Q_OS_FREEBSD 0118 QSKIP("This test fails on FreeBSD for some reason (waitForReadyRead(5000) times out)"); 0119 #endif 0120 KPtyProcess p; 0121 p.setProgram(bash, 0122 QStringList() << "-c" 0123 << "read -s VAL; echo \"1: $VAL\"; echo \"2: $VAL\" >&2"); 0124 p.setPtyChannels(KPtyProcess::AllChannels); 0125 p.pty()->setEcho(false); 0126 p.start(); 0127 p.pty()->write("test\n"); 0128 p.pty()->waitForBytesWritten(1000); 0129 QVERIFY(p.waitForFinished(5000)); 0130 while (p.pty()->bytesAvailable() < 18) { 0131 qDebug() << p.pty()->bytesAvailable() << "bytes available, waiting"; 0132 QVERIFY(p.pty()->waitForReadyRead(5000)); 0133 } 0134 QString output = p.pty()->readAll(); 0135 QCOMPARE(output, QLatin1String("1: test\r\n2: test\r\n")); 0136 } 0137 0138 void KPtyProcessTest::slotReadyRead() 0139 { 0140 delay.start(30); 0141 } 0142 0143 void KPtyProcessTest::slotDoRead() 0144 { 0145 while (sp.pty()->canReadLine()) { 0146 log.append('>').append(sp.pty()->readLine()).append("$\n"); 0147 } 0148 log.append("!\n"); 0149 } 0150 0151 void KPtyProcessTest::slotReadEof() 0152 { 0153 log.append('|').append(sp.pty()->readAll()).append("$\n"); 0154 } 0155 0156 void KPtyProcessTest::slotBytesWritten() 0157 { 0158 log.append('<'); 0159 } 0160 0161 static const char *const feeds[] = {"bla\n", "foo\x04", "bar\n", "fooish\nbar\n", "\x04", nullptr}; 0162 0163 static const char want[] = 0164 "<>bla\r\n$\n!\n" 0165 "<!\n<>foobar\r\n$\n!\n" 0166 "<>fooish\r\n$\n>bar\r\n$\n!\n" 0167 "<|$\n"; 0168 0169 void KPtyProcessTest::slotStep() 0170 { 0171 if (feeds[phase]) { 0172 sp.pty()->write(feeds[phase]); 0173 phase++; 0174 } 0175 } 0176 0177 Q_DECLARE_METATYPE(QProcess::ExitStatus) 0178 0179 void KPtyProcessTest::test_pty_signals() 0180 { 0181 sp.setShellCommand("cat; sleep .1"); 0182 sp.setPtyChannels(KPtyProcess::StdinChannel | KPtyProcess::StdoutChannel); 0183 sp.pty()->setEcho(false); 0184 connect(sp.pty(), &QIODevice::readyRead, this, &KPtyProcessTest::slotReadyRead); 0185 connect(sp.pty(), &KPtyDevice::readEof, this, &KPtyProcessTest::slotReadEof); 0186 connect(sp.pty(), &QIODevice::bytesWritten, this, &KPtyProcessTest::slotBytesWritten); 0187 QTimer timer; 0188 connect(&timer, &QTimer::timeout, this, &KPtyProcessTest::slotStep); 0189 timer.start(200); 0190 connect(&delay, &QTimer::timeout, this, &KPtyProcessTest::slotDoRead); 0191 delay.setSingleShot(true); 0192 sp.start(); 0193 sp.pty()->closeSlave(); 0194 phase = 0; 0195 qRegisterMetaType<QProcess::ExitStatus>(); 0196 QSignalSpy finishedSpy(&sp, &QProcess::finished); 0197 QVERIFY(finishedSpy.wait(2000)); 0198 log.replace("<<", "<"); // It's OK if bytesWritten is emitted multiple times... 0199 QCOMPARE(QLatin1String(log), QLatin1String(want)); 0200 } 0201 0202 void KPtyProcessTest::test_ctty() 0203 { 0204 #ifdef Q_OS_MAC 0205 QSKIP("This test currently hangs on OSX"); 0206 #endif 0207 #ifdef Q_OS_FREEBSD 0208 QSKIP("This test fails on FreeBSD for some reason (output is empty)"); 0209 #endif 0210 0211 KPtyProcess p; 0212 p.setShellCommand("echo this is a test > /dev/tty"); 0213 p.execute(1000); 0214 p.pty()->waitForReadyRead(1000); 0215 QString output = p.pty()->readAll(); 0216 QCOMPARE(output, QLatin1String("this is a test\r\n")); 0217 } 0218 0219 QTEST_MAIN(KPtyProcessTest) 0220 0221 #include "moc_kptyprocesstest.cpp"