forked from qt-creator/qt-creator
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:
@@ -35,6 +35,8 @@
|
|||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
const char kExitCodeSubProcessCode[] = "QTC_TST_QTCPROCESS_EXITCODE_CODE";
|
const char kExitCodeSubProcessCode[] = "QTC_TST_QTCPROCESS_EXITCODE_CODE";
|
||||||
|
const char kRunBlockingStdOutSubProcessMagicWord[] = "42";
|
||||||
|
const char kRunBlockingStdOutSubProcessWithEndl[] = "QTC_TST_QTCPROCESS_RUNBLOCKINGSTDOUT_WITHENDL";
|
||||||
|
|
||||||
static void exitCodeSubProcessMain()
|
static void exitCodeSubProcessMain()
|
||||||
{
|
{
|
||||||
@@ -43,6 +45,18 @@ static void exitCodeSubProcessMain()
|
|||||||
exit(exitCode);
|
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 {
|
class MacroMapExpander : public AbstractMacroExpander {
|
||||||
public:
|
public:
|
||||||
virtual bool resolveMacro(const QString &name, QString *ret, QSet<AbstractMacroExpander*> &seen)
|
virtual bool resolveMacro(const QString &name, QString *ret, QSet<AbstractMacroExpander*> &seen)
|
||||||
@@ -86,6 +100,8 @@ private slots:
|
|||||||
void iteratorEditsLinux();
|
void iteratorEditsLinux();
|
||||||
void exitCode_data();
|
void exitCode_data();
|
||||||
void exitCode();
|
void exitCode();
|
||||||
|
void runBlockingStdOut_data();
|
||||||
|
void runBlockingStdOut();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void iteratorEditsHelper(OsType osType);
|
void iteratorEditsHelper(OsType osType);
|
||||||
@@ -103,6 +119,8 @@ void tst_QtcProcess::initTestCase()
|
|||||||
{
|
{
|
||||||
if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode))
|
if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode))
|
||||||
exitCodeSubProcessMain();
|
exitCodeSubProcessMain();
|
||||||
|
if (qEnvironmentVariableIsSet(kRunBlockingStdOutSubProcessWithEndl))
|
||||||
|
blockingStdOutSubProcessMain();
|
||||||
|
|
||||||
homeStr = QLatin1String("@HOME@");
|
homeStr = QLatin1String("@HOME@");
|
||||||
home = QDir::homePath();
|
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)
|
QTEST_MAIN(tst_QtcProcess)
|
||||||
|
|
||||||
#include "tst_qtcprocess.moc"
|
#include "tst_qtcprocess.moc"
|
||||||
|
Reference in New Issue
Block a user