From 8cb4ab733a57cef61437636fa8af3c50bb974a5a Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 26 May 2021 03:56:03 +0200 Subject: [PATCH] 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 --- tests/auto/qtcprocess/tst_qtcprocess.cpp | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/auto/qtcprocess/tst_qtcprocess.cpp b/tests/auto/qtcprocess/tst_qtcprocess.cpp index ad1d95debe3..af5bc3cd3e9 100644 --- a/tests/auto/qtcprocess/tst_qtcprocess.cpp +++ b/tests/auto/qtcprocess/tst_qtcprocess.cpp @@ -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 &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("withEndl"); + QTest::addColumn("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"