Utils: Add auto test for SynchronousProcess::runBlocking

QTCREATORBUG-25667 appeared a few Months back in corellation to a
seemingly unrelated change, and I was much later scratching my head a
day or two.

Therefore, it should be fine to have a test for the usage of
SynchronousProcess::runBlocking with interactive cli processes. Those
cli processes might output lines without terminating \n or \r and thus
hang until timeout.

We are IMHO lacking process IO tests, anyways.

Task-number: QTCREATORBUG-25667
Change-Id: I3b7ea471b2ac9fa4554f0ce51752ce54c4a7d304
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Alessandro Portale
2021-05-26 03:56:03 +02:00
parent 642318c325
commit 8cb4ab733a

View File

@@ -35,6 +35,8 @@
using namespace Utils;
const char kExitCodeSubProcessCode[] = "QTC_TST_QTCPROCESS_EXITCODE_CODE";
const char kRunBlockingStdOutSubProcessMagicWord[] = "42";
const char kRunBlockingStdOutSubProcessWithEndl[] = "QTC_TST_QTCPROCESS_RUNBLOCKINGSTDOUT_WITHENDL";
static void exitCodeSubProcessMain()
{
@@ -43,6 +45,18 @@ static void exitCodeSubProcessMain()
exit(exitCode);
}
static void blockingStdOutSubProcessMain()
{
std::cout << "Wait for the Answer to the Ultimate Question of Life, "
"The Universe, and Everything..." << std::endl;
QThread::msleep(300);
std::cout << kRunBlockingStdOutSubProcessMagicWord << "...Now wait for the question...";
if (qEnvironmentVariable(kRunBlockingStdOutSubProcessWithEndl) == "true")
std::cout << std::endl;
QThread::msleep(5000);
exit(0);
}
class MacroMapExpander : public AbstractMacroExpander {
public:
virtual bool resolveMacro(const QString &name, QString *ret, QSet<AbstractMacroExpander*> &seen)
@@ -86,6 +100,8 @@ private slots:
void iteratorEditsLinux();
void exitCode_data();
void exitCode();
void runBlockingStdOut_data();
void runBlockingStdOut();
private:
void iteratorEditsHelper(OsType osType);
@@ -103,6 +119,8 @@ void tst_QtcProcess::initTestCase()
{
if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode))
exitCodeSubProcessMain();
if (qEnvironmentVariableIsSet(kRunBlockingStdOutSubProcessWithEndl))
blockingStdOutSubProcessMain();
homeStr = QLatin1String("@HOME@");
home = QDir::homePath();
@@ -803,6 +821,54 @@ void tst_QtcProcess::exitCode()
}
}
void tst_QtcProcess::runBlockingStdOut_data()
{
QTest::addColumn<bool>("withEndl");
QTest::addColumn<int>("timeOutS");
QTest::newRow("Terminated stdout delivered instantly")
<< true
<< 2;
QTest::newRow("Unterminated stdout lost: early timeout")
<< false
<< 2;
QTest::newRow("Unterminated stdout lost: hanging")
<< false
<< 20;
}
void tst_QtcProcess::runBlockingStdOut()
{
QFETCH(bool, withEndl);
QFETCH(int, timeOutS);
SynchronousProcess sp;
QStringList args = QCoreApplication::arguments();
const QString binary = args.takeFirst();
sp.setCommand(CommandLine(binary, args));
Environment env = Environment::systemEnvironment();
env.set(kRunBlockingStdOutSubProcessWithEndl, withEndl ? "true" : "false");
sp.setEnvironment(env);
sp.setTimeoutS(timeOutS);
bool readLastLine = false;
sp.setStdOutCallback([&readLastLine, &sp](const QString &out) {
if (out.startsWith(kRunBlockingStdOutSubProcessMagicWord)) {
readLastLine = true;
sp.kill();
}
});
sp.runBlocking();
// See also QTCREATORBUG-25667 for why it is a bad idea to use SynchronousProcess::runBlocking
// with interactive cli tools.
QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue);
QVERIFY2(sp.result() != QtcProcess::Hang, "Process run did not time out.");
QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue);
QEXPECT_FAIL("Unterminated stdout lost: hanging", "", Continue);
QVERIFY2(readLastLine, "Last line was read.");
}
QTEST_MAIN(tst_QtcProcess)
#include "tst_qtcprocess.moc"