aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarek Kobus <jaroslaw.kobus@qt.io>2022-03-04 17:14:13 +0100
committerJarek Kobus <jaroslaw.kobus@qt.io>2022-03-09 12:17:14 +0000
commitbbb0270fb10032d94643438c9a10570503d4d09e (patch)
tree4e4dbf5aca4c1cd362093c918ed8f7473326252c
parent809b37110893bc88c229cd57108ad2db819154af (diff)
Simplify starting subCreator process
Introduce SubCreatorConfig class that helps setting up a Creator subprocess. This limits the code repetition. Introduce SUB_CREATOR_PROCESS(subProcess) macro to easily define environment variables referred to Creator subprocesses. This macro also forward declares the subProcessMain() function. Match names of main subprocess functions with their corresponding environment variables. Group subProcessMain() functions near the corresponding test function for clarity. Change-Id: Ib4365cf18fddc1527ebc99accee1fbb974bbf7a1 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
-rw-r--r--tests/auto/utils/qtcprocess/tst_qtcprocess.cpp339
1 files changed, 180 insertions, 159 deletions
diff --git a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp b/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
index f8422c053d4..2b9924ffa06 100644
--- a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
+++ b/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp
@@ -29,6 +29,7 @@
#include <utils/hostosinfo.h>
#include <utils/launcherinterface.h>
#include <utils/porting.h>
+#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/singleton.h>
#include <utils/stringutils.h>
@@ -46,21 +47,24 @@
#include <fcntl.h>
#endif
-
using namespace Utils;
+#define SUB_CREATOR_PROCESS(var)\
+const char var[] = "TST_QTC_PROCESS_" QTC_ASSERT_STRINGIFY(var);\
+static void var ## Main();
+
// Many tests in this file need to start a new subprocess with custom code.
// In order to simplify things we don't produce separate executables, but invoke
// the same test process recursively and prior to the execution we set one of the
// following environment variables:
-const char kExitCodeSubProcessCode[] = "QTC_TST_QTCPROCESS_EXITCODE_CODE";
-const char kRunBlockingStdOutSubProcessWithEndl[] = "QTC_TST_QTCPROCESS_RUNBLOCKINGSTDOUT_WITHENDL";
-const char kLineCallback[] = "QTC_TST_QTCPROCESS_LINECALLBACK";
-const char kTestProcess[] = "QTC_TST_TEST_PROCESS";
-const char kForwardProcess[] = "QTC_TST_FORWARD_PROCESS";
-const char kForwardSubProcess[] = "QTC_TST_FORWARD_SUB_PROCESS";
-const char kBlockingProcess[] = "QTC_TST_BLOCKING_PROCESS";
+SUB_CREATOR_PROCESS(subExitCode);
+SUB_CREATOR_PROCESS(subRunBlockingStdOut);
+SUB_CREATOR_PROCESS(subLineCallback);
+SUB_CREATOR_PROCESS(subSimpleTest);
+SUB_CREATOR_PROCESS(subProcessChannelForwarding);
+SUB_CREATOR_PROCESS(subSubProcessChannelForwarding);
+SUB_CREATOR_PROCESS(subKillBlockingProcess);
// The variables above are meant to be different custom executables. Inside initTestCase()
// we are detecting if one of these variables is set and invoke directly a respective custom
@@ -68,7 +72,7 @@ const char kBlockingProcess[] = "QTC_TST_BLOCKING_PROCESS";
// the recursion, as from the test point of view we meant to execute only our custom code
// without further execution of the test itself.
-const char testProcessData[] = "Test process successfully executed.";
+const char simpleTestData[] = "Test process successfully executed.";
const char forwardedOutputData[] = "This is the output message.";
const char forwardedErrorData[] = "This is the error message.";
const char runBlockingStdOutSubProcessMagicWord[] = "42";
@@ -81,84 +85,64 @@ const char lineCallbackData[] =
"Rebasing (1/10)\r| <delay> Rebasing (2/10)\r| <delay> ...\r\n|"
"And no end";
-static void exitCodeSubProcessMain()
+static Environment subEnvironment(const char *envVar, const QString &envVal)
{
- const int exitCode = qEnvironmentVariableIntValue(kExitCodeSubProcessCode);
- std::cout << "Exiting with code:" << exitCode << std::endl;
- 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 << runBlockingStdOutSubProcessMagicWord << "...Now wait for the question...";
- if (qEnvironmentVariable(kRunBlockingStdOutSubProcessWithEndl) == "true")
- std::cout << std::endl;
- QThread::msleep(5000);
- exit(0);
+ Environment env = Environment::systemEnvironment();
+ env.set(envVar, envVal);
+ return env;
}
-static void lineCallbackMain()
+static CommandLine subCommandLine()
{
-#ifdef Q_OS_WIN
- // Prevent \r\n -> \r\r\n translation.
- setmode(fileno(stderr), O_BINARY);
-#endif
- fprintf(stderr, "%s", QByteArray(lineCallbackData).replace('|', "").data());
- exit(0);
+ QStringList args = QCoreApplication::arguments();
+ const QString binary = args.takeFirst();
+ return CommandLine(FilePath::fromString(binary), args);
}
-static void testProcessSubProcessMain()
+class SubCreatorConfig
{
- std::cout << testProcessData << std::endl;
- exit(0);
-}
+public:
+ SubCreatorConfig(const char *envVar, const QString &envVal)
+ : m_environment(subEnvironment(envVar, envVal))
+ , m_commandLine(subCommandLine()) {}
-// Since we want to test whether the process forwards its channels or not, we can't just create
-// a process and start it, because in this case there is no way on how to check whether something
-// went into out output channels or not.
+ void setupSubProcess(QtcProcess *subProcess)
+ {
+ subProcess->setEnvironment(m_environment);
+ subProcess->setCommand(m_commandLine);
+ }
+private:
+ const Environment m_environment;
+ const CommandLine m_commandLine;
+};
-// So we start two processes in chain instead. On the beginning the processChannelForwarding()
-// test starts the "testForwardProcessMain" - this one will start another process
-// "testForwardSubProcessMain" with forwarding options. The "testForwardSubProcessMain"
-// is very simple - it just puts something to the output and the error channels.
-// Then "testForwardProcessMain" either forwards these channels or not - we check it in the outer
-// processChannelForwarding() test.
-static void testForwardProcessMain()
+static std::atomic_int s_processCounter = 0;
+static const bool s_removeSingletonsOnLastProcess = false;
+// The use of this class (when s_removeSingletonsOnLastProcess = true) guarantees that if all
+// instances of QtcProcess are destructed at some point (i.e. after each test function ends)
+// we also run process reaper's and process launcher's destructors. Otherwise
+// (when s_removeSingletonsOnLastProcess = true) this is a no-op wrapper around QtcProcess.
+class TestProcess : public QtcProcess
{
- Environment env = Environment::systemEnvironment();
- env.set(kForwardSubProcess, {});
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- const CommandLine command(FilePath::fromString(binary), args);
-
- QtcProcess process;
- const QProcess::ProcessChannelMode channelMode
- = QProcess::ProcessChannelMode(qEnvironmentVariableIntValue(kForwardProcess));
- process.setProcessChannelMode(channelMode);
- process.setCommand(command);
- process.setEnvironment(env);
- process.start();
- process.waitForFinished();
- exit(0);
-}
+public:
+ class TestProcessDeleter : public QObject
+ {
+ public:
+ TestProcessDeleter(QObject *parent) : QObject(parent) { s_processCounter.fetch_add(1); }
+ ~TestProcessDeleter() {
+ if ((s_processCounter.fetch_sub(1) == 1) && s_removeSingletonsOnLastProcess)
+ Utils::Singleton::deleteAll();
+ }
+ };
+ TestProcess() { new TestProcessDeleter(this); }
+};
-static void testForwardSubProcessMain()
+static void subSimpleTestMain()
{
- std::cout << forwardedOutputData << std::endl;
- std::cerr << forwardedErrorData << std::endl;
+ std::cout << simpleTestData << std::endl;
exit(0);
}
-static void blockingProcessSubProcessMain()
-{
- std::cout << "Blocking process successfully executed." << std::endl;
- while (true)
- ;
-}
-
class MacroMapExpander : public AbstractMacroExpander {
public:
virtual bool resolveMacro(const QString &name, QString *ret, QSet<AbstractMacroExpander*> &seen)
@@ -233,20 +217,20 @@ void tst_QtcProcess::initTestCase()
+ Core::Constants::IDE_CASED_ID + "-XXXXXX");
Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
- if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode))
- exitCodeSubProcessMain();
- if (qEnvironmentVariableIsSet(kRunBlockingStdOutSubProcessWithEndl))
- blockingStdOutSubProcessMain();
- if (qEnvironmentVariableIsSet(kLineCallback))
- lineCallbackMain();
- if (qEnvironmentVariableIsSet(kTestProcess))
- testProcessSubProcessMain();
- if (qEnvironmentVariableIsSet(kForwardSubProcess))
- testForwardSubProcessMain();
- else if (qEnvironmentVariableIsSet(kForwardProcess))
- testForwardProcessMain();
- if (qEnvironmentVariableIsSet(kBlockingProcess))
- blockingProcessSubProcessMain();
+ if (qEnvironmentVariableIsSet(subExitCode))
+ subExitCodeMain();
+ if (qEnvironmentVariableIsSet(subRunBlockingStdOut))
+ subRunBlockingStdOutMain();
+ if (qEnvironmentVariableIsSet(subLineCallback))
+ subLineCallbackMain();
+ if (qEnvironmentVariableIsSet(subSimpleTest))
+ subSimpleTestMain();
+ if (qEnvironmentVariableIsSet(subProcessChannelForwarding))
+ subProcessChannelForwardingMain();
+ if (qEnvironmentVariableIsSet(subSubProcessChannelForwarding))
+ subSubProcessChannelForwardingMain();
+ if (qEnvironmentVariableIsSet(subKillBlockingProcess))
+ subKillBlockingProcessMain();
homeStr = QLatin1String("@HOME@");
home = QDir::homePath();
@@ -907,6 +891,13 @@ void tst_QtcProcess::iteratorEditsLinux()
iteratorEditsHelper(OsTypeLinux);
}
+static void subExitCodeMain()
+{
+ const int exitCode = qEnvironmentVariableIntValue(subExitCode);
+ std::cout << "Exiting with code:" << exitCode << std::endl;
+ exit(exitCode);
+}
+
void tst_QtcProcess::exitCode_data()
{
QTest::addColumn<int>("exitCode");
@@ -925,34 +916,39 @@ void tst_QtcProcess::exitCode()
{
QFETCH(int, exitCode);
- Environment env = Environment::systemEnvironment();
- env.set(kExitCodeSubProcessCode, QString::number(exitCode));
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- const CommandLine command(FilePath::fromString(binary), args);
-
+ SubCreatorConfig subConfig(subExitCode, QString::number(exitCode));
{
- QtcProcess qtcP;
- qtcP.setCommand(command);
- qtcP.setEnvironment(env);
- qtcP.start();
- const bool finished = qtcP.waitForFinished();
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
+ process.start();
+ const bool finished = process.waitForFinished();
QVERIFY(finished);
- QCOMPARE(qtcP.exitCode(), exitCode);
- QCOMPARE(qtcP.exitCode() == 0, qtcP.result() == ProcessResult::FinishedWithSuccess);
+ QCOMPARE(process.exitCode(), exitCode);
+ QCOMPARE(process.exitCode() == 0, process.result() == ProcessResult::FinishedWithSuccess);
}
{
- QtcProcess sP;
- sP.setCommand(command);
- sP.setEnvironment(env);
- sP.runBlocking();
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
+ process.runBlocking();
- QCOMPARE(sP.exitCode(), exitCode);
- QCOMPARE(sP.exitCode() == 0, sP.result() == ProcessResult::FinishedWithSuccess);
+ QCOMPARE(process.exitCode(), exitCode);
+ QCOMPARE(process.exitCode() == 0, process.result() == ProcessResult::FinishedWithSuccess);
}
}
+static void subRunBlockingStdOutMain()
+{
+ std::cout << "Wait for the Answer to the Ultimate Question of Life, "
+ "The Universe, and Everything..." << std::endl;
+ QThread::msleep(300);
+ std::cout << runBlockingStdOutSubProcessMagicWord << "...Now wait for the question...";
+ if (qEnvironmentVariable(subRunBlockingStdOut) == "true")
+ std::cout << std::endl;
+ QThread::msleep(5000);
+ exit(0);
+}
+
void tst_QtcProcess::runBlockingStdOut_data()
{
QTest::addColumn<bool>("withEndl");
@@ -974,40 +970,44 @@ void tst_QtcProcess::runBlockingStdOut()
QFETCH(bool, withEndl);
QFETCH(int, timeOutS);
- QtcProcess sp;
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- sp.setCommand(CommandLine(FilePath::fromString(binary), args));
- Environment env = Environment::systemEnvironment();
- env.set(kRunBlockingStdOutSubProcessWithEndl, withEndl ? "true" : "false");
- sp.setEnvironment(env);
- sp.setTimeoutS(timeOutS);
+ SubCreatorConfig subConfig(subRunBlockingStdOut, withEndl ? "true" : "false");
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
+
+ process.setTimeoutS(timeOutS);
bool readLastLine = false;
- sp.setStdOutCallback([&readLastLine, &sp](const QString &out) {
+ process.setStdOutCallback([&readLastLine, &process](const QString &out) {
if (out.startsWith(runBlockingStdOutSubProcessMagicWord)) {
readLastLine = true;
- sp.kill();
+ process.kill();
}
});
- sp.runBlocking();
+ process.runBlocking();
// See also QTCREATORBUG-25667 for why it is a bad idea to use QtcProcess::runBlocking
// with interactive cli tools.
QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue);
- QVERIFY2(sp.result() != ProcessResult::Hang, "Process run did not time out.");
+ QVERIFY2(process.result() != ProcessResult::Hang, "Process run did not time out.");
QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue);
QVERIFY2(readLastLine, "Last line was read.");
}
+static void subLineCallbackMain()
+{
+#ifdef Q_OS_WIN
+ // Prevent \r\n -> \r\r\n translation.
+ setmode(fileno(stderr), O_BINARY);
+#endif
+ fprintf(stderr, "%s", QByteArray(lineCallbackData).replace('|', "").data());
+ exit(0);
+}
+
void tst_QtcProcess::lineCallback()
{
- QtcProcess process;
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- process.setCommand(CommandLine(FilePath::fromString(binary), args));
- Environment env = Environment::systemEnvironment();
- env.set(kLineCallback, "Yes");
- process.setEnvironment(env);
+ SubCreatorConfig subConfig(subLineCallback, {});
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
+
QStringList lines = QString(lineCallbackData).split('|');
int lineNumber = 0;
process.setStdErrLineCallback([lines, &lineNumber](const QString &actual) {
@@ -1027,7 +1027,7 @@ void tst_QtcProcess::lineCallback()
void tst_QtcProcess::lineCallbackIntern()
{
- QtcProcess process;
+ TestProcess process;
QStringList lines = QString(lineCallbackData).split('|');
int lineNumber = 0;
process.setStdOutLineCallback([lines, &lineNumber](const QString &actual) {
@@ -1048,15 +1048,10 @@ void tst_QtcProcess::lineCallbackIntern()
void tst_QtcProcess::waitForStartedAndFinished()
{
- Environment env = Environment::systemEnvironment();
- env.set(kTestProcess, {});
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- const CommandLine command(FilePath::fromString(binary), args);
+ SubCreatorConfig subConfig(subSimpleTest, {});
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
- QtcProcess process;
- process.setCommand(command);
- process.setEnvironment(env);
process.start();
QThread::msleep(1000); // long enough for process to finish
QVERIFY(process.waitForStarted());
@@ -1067,7 +1062,7 @@ void tst_QtcProcess::waitForStartedAndFinished()
void tst_QtcProcess::notRunningAfterStartingNonExistingProgram()
{
- QtcProcess process;
+ TestProcess process;
process.setCommand({ FilePath::fromString(
"there_is_a_big_chance_that_executable_with_that_name_does_not_exists"), {} });
@@ -1096,6 +1091,40 @@ void tst_QtcProcess::notRunningAfterStartingNonExistingProgram()
}
}
+// Since we want to test whether the process forwards its channels or not, we can't just create
+// a process and start it, because in this case there is no way on how to check whether something
+// went into our output channels or not.
+
+// So we start two processes in chain instead. On the beginning the processChannelForwarding()
+// test starts the "subProcessChannelForwardingMain" - this one will start another process
+// "subSubProcessChannelForwardingMain" with forwarding options.
+// The "subSubProcessChannelForwardingMain" is very simple - it just puts something to the output
+// and the error channels. Then "subProcessChannelForwardingMain" either forwards these channels
+// or not - we check it in the outer processChannelForwarding() test.
+
+static void subSubProcessChannelForwardingMain()
+{
+ std::cout << forwardedOutputData << std::endl;
+ std::cerr << forwardedErrorData << std::endl;
+ exit(0);
+}
+
+static void subProcessChannelForwardingMain()
+{
+ const QProcess::ProcessChannelMode channelMode
+ = QProcess::ProcessChannelMode(qEnvironmentVariableIntValue(subProcessChannelForwarding));
+ qunsetenv(subProcessChannelForwarding);
+
+ SubCreatorConfig subConfig(subSubProcessChannelForwarding, {});
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
+
+ process.setProcessChannelMode(channelMode);
+ process.start();
+ process.waitForFinished();
+ exit(0);
+}
+
void tst_QtcProcess::processChannelForwarding_data()
{
QTest::addColumn<QProcess::ProcessChannelMode>("channelMode");
@@ -1114,15 +1143,10 @@ void tst_QtcProcess::processChannelForwarding()
QFETCH(bool, outputForwarded);
QFETCH(bool, errorForwarded);
- Environment env = Environment::systemEnvironment();
- env.set(kForwardProcess, QString::number(int(channelMode)));
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- const CommandLine command(FilePath::fromString(binary), args);
+ SubCreatorConfig subConfig(subProcessChannelForwarding, QString::number(int(channelMode)));
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
- QtcProcess process;
- process.setCommand(command);
- process.setEnvironment(env);
process.start();
QVERIFY(process.waitForFinished());
@@ -1171,19 +1195,21 @@ protected:
bool MessageHandler::ok = true;
QtMessageHandler MessageHandler::oldMessageHandler = 0;
+static void subKillBlockingProcessMain()
+{
+ std::cout << "Blocking process successfully executed." << std::endl;
+ while (true)
+ ;
+}
+
void tst_QtcProcess::killBlockingProcess()
{
- Environment env = Environment::systemEnvironment();
- env.set(kBlockingProcess, {});
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- const CommandLine command(FilePath::fromString(binary), args);
+ SubCreatorConfig subConfig(subKillBlockingProcess, {});
MessageHandler msgHandler;
{
- QtcProcess process;
- process.setCommand(command);
- process.setEnvironment(env);
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
process.start();
QVERIFY(process.waitForStarted());
QVERIFY(!process.waitForFinished(1000));
@@ -1194,15 +1220,10 @@ void tst_QtcProcess::killBlockingProcess()
void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead()
{
- Environment env = Environment::systemEnvironment();
- env.set(kTestProcess, {});
- QStringList args = QCoreApplication::arguments();
- const QString binary = args.takeFirst();
- const CommandLine command(FilePath::fromString(binary), args);
+ SubCreatorConfig subConfig(subSimpleTest, {});
+ TestProcess process;
+ subConfig.setupSubProcess(&process);
- QtcProcess process;
- process.setCommand(command);
- process.setEnvironment(env);
process.start();
QVERIFY(process.waitForStarted());
@@ -1219,7 +1240,7 @@ void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead()
QCOMPARE(process.state(), QProcess::NotRunning);
QVERIFY(!timer.hasExpired());
- QVERIFY(reply.contains(testProcessData));
+ QVERIFY(reply.contains(simpleTestData));
}
QTEST_MAIN(tst_QtcProcess)