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"