tst_QtcProcess: Move subprocesses into a separate executable

Don't invoke subcreator test in order to run a custom process.
Run a separate executable instead. The advantages are:
1. Faster test running (no need for QTest specific code path
   when running subprocess).
2. Don't use std::exit() from subprocesses as it doesn't
   clean up properly. Use qApp->exit() instead.
3. This should support returning NormalExit and proper
   exit code of subprocess in case the code wasn't 0.

Change-Id: I1395bd8a7873c95a594c3e054087f00c55a15376
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Jarek Kobus
2022-04-01 14:15:30 +02:00
parent 8fd5c9d6c0
commit 8f427c1c95
8 changed files with 486 additions and 251 deletions

View File

@@ -1,8 +1,13 @@
add_subdirectory(processtestapp)
file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}")
add_qtc_test(tst_qtcprocess
DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\""
"PROCESS_TESTAPP=\"${CMAKE_CURRENT_BINARY_DIR}/processtestapp\""
DEPENDS Utils app_version
SOURCES tst_qtcprocess.cpp
processtestapp/processtestapp.h
processtestapp/processtestapp.cpp
)

View File

@@ -0,0 +1,17 @@
file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}")
add_qtc_executable(processtestapp
DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\""
"PROCESS_TESTAPP=\"${CMAKE_CURRENT_BINARY_DIR}\""
DEPENDS Utils app_version
SOURCES main.cpp processtestapp.h processtestapp.cpp
SKIP_INSTALL
INTERNAL_ONLY
)
set_target_properties(processtestapp PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)

View File

@@ -0,0 +1,64 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "processtestapp.h"
#include <app/app_version.h>
#include <utils/launcherinterface.h>
#include <utils/qtcprocess.h>
#include <utils/temporarydirectory.h>
#include <QCoreApplication>
#include <QScopeGuard>
#ifdef Q_OS_WIN
#include <crtdbg.h>
#include <cstdlib>
#endif
using namespace Utils;
int main(int argc, char **argv)
{
#ifdef Q_OS_WIN
// avoid crash reporter dialog
_set_error_mode(_OUT_TO_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
#endif
QCoreApplication app(argc, argv);
auto cleanup = qScopeGuard([] { Singleton::deleteAll(); });
TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/"
+ Core::Constants::IDE_CASED_ID + "-XXXXXX");
const QString libExecPath(qApp->applicationDirPath() + '/'
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
LauncherInterface::setPathToLauncher(libExecPath);
SubProcessConfig::setPathToProcessTestApp(QLatin1String(PROCESS_TESTAPP));
QMetaObject::invokeMethod(&app, [] { ProcessTestApp::invokeSubProcess(); }, Qt::QueuedConnection);
return app.exec();
}

View File

@@ -0,0 +1,208 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "processtestapp.h"
#include <utils/qtcprocess.h>
#include <QDebug>
#include <QHash>
#include <QMutex>
#include <QScopeGuard>
#include <QThread>
#include <iostream>
#ifdef Q_OS_WIN
#include <fcntl.h>
#include <io.h>
#endif
using namespace Utils;
static QHash<const char *, ProcessTestApp::SubProcessMain> s_subProcesses = {};
ProcessTestApp::ProcessTestApp() = default;
void ProcessTestApp::invokeSubProcess()
{
ProcessTestApp processTestApp;
int returnValue = 1;
auto cleanup = qScopeGuard([&returnValue] {
QMetaObject::invokeMethod(qApp, [returnValue] {
qApp->exit(returnValue);
}, Qt::QueuedConnection);
});
for (auto it = s_subProcesses.constBegin(); it != s_subProcesses.constEnd(); ++it) {
if (qEnvironmentVariableIsSet(it.key())) {
returnValue = it.value()();
return;
}
}
qWarning() << "No test was run!";
}
void ProcessTestApp::registerSubProcess(const char *envVar, const SubProcessMain &main)
{
s_subProcesses.insert(envVar, main);
}
void ProcessTestApp::unregisterSubProcess(const char *envVar)
{
s_subProcesses.remove(envVar);
}
static QString s_pathToProcessTestApp;
static Environment subEnvironment(const char *envVar, const QString &envVal)
{
Environment env = Environment::systemEnvironment();
env.set(QString::fromLatin1(envVar), envVal);
return env;
}
void SubProcessConfig::setPathToProcessTestApp(const QString &path)
{
s_pathToProcessTestApp = path;
}
SubProcessConfig::SubProcessConfig(const char *envVar, const QString &envVal)
: m_environment(subEnvironment(envVar, envVal))
{
}
void SubProcessConfig::setupSubProcess(QtcProcess *subProcess)
{
subProcess->setEnvironment(m_environment);
const FilePath filePath = FilePath::fromString(s_pathToProcessTestApp
+ QLatin1String("/processtestapp")).withExecutableSuffix();
subProcess->setCommand(CommandLine(filePath, {}));
}
static void doCrash()
{
qFatal("The application has crashed purposefully!");
}
int ProcessTestApp::SimpleTest::main()
{
std::cout << s_simpleTestData << std::endl;
return 0;
}
int ProcessTestApp::ExitCode::main()
{
const int exitCode = qEnvironmentVariableIntValue(envVar());
std::cout << "Exiting with code:" << exitCode << std::endl;
return exitCode;
}
int ProcessTestApp::RunBlockingStdOut::main()
{
std::cout << "Wait for the Answer to the Ultimate Question of Life, "
"The Universe, and Everything..." << std::endl;
QThread::msleep(300);
std::cout << s_runBlockingStdOutSubProcessMagicWord << "...Now wait for the question...";
if (qEnvironmentVariable(envVar()) == "true")
std::cout << std::endl;
else
std::cout << std::flush; // otherwise it won't reach the original process (will be buffered)
QThread::msleep(5000);
return 0;
}
int ProcessTestApp::LineCallback::main()
{
#ifdef Q_OS_WIN
// Prevent \r\n -> \r\r\n translation.
_setmode(_fileno(stderr), O_BINARY);
#endif
fprintf(stderr, "%s", QByteArray(s_lineCallbackData).replace('|', "").data());
return 0;
}
int ProcessTestApp::StandardOutputAndErrorWriter::main()
{
std::cout << s_outputData << std::endl;
std::cerr << s_errorData << std::endl;
return 0;
}
int ProcessTestApp::ChannelForwarding::main()
{
const QProcess::ProcessChannelMode channelMode
= QProcess::ProcessChannelMode(qEnvironmentVariableIntValue(envVar()));
qunsetenv(envVar());
SubProcessConfig subConfig(StandardOutputAndErrorWriter::envVar(), {});
QtcProcess process;
subConfig.setupSubProcess(&process);
process.setProcessChannelMode(channelMode);
process.start();
process.waitForFinished();
return 0;
}
int ProcessTestApp::KillBlockingProcess::main()
{
std::cout << "Blocking process successfully executed." << std::endl;
const BlockType blockType = BlockType(qEnvironmentVariableIntValue(envVar()));
switch (blockType) {
case BlockType::EndlessLoop:
while (true)
;
break;
case BlockType::InfiniteSleep:
QThread::sleep(INT_MAX);
break;
case BlockType::MutexDeadlock: {
QMutex mutex;
mutex.lock();
mutex.lock();
break;
}
case BlockType::EventLoop: {
QEventLoop loop;
loop.exec();
break;
}
}
return 1;
}
int ProcessTestApp::EmitOneErrorOnCrash::main()
{
doCrash();
return 1;
}
int ProcessTestApp::CrashAfterOneSecond::main()
{
QThread::sleep(1);
doCrash();
return 1;
}

View File

@@ -0,0 +1,120 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include <functional>
#include <utils/commandline.h>
#include <utils/environment.h>
#include <utils/qtcassert.h>
namespace Utils { class QtcProcess; }
#define SUB_PROCESS(SubProcessClass)\
class SubProcessClass\
{\
public:\
static const char *envVar() { return m_envVar; }\
private:\
SubProcessClass() { registerSubProcess(envVar(), &SubProcessClass::main); }\
~SubProcessClass() { unregisterSubProcess(envVar()); }\
static int main();\
static constexpr char m_envVar[] = "TST_QTC_PROCESS_" QTC_ASSERT_STRINGIFY(SubProcessClass);\
friend class ProcessTestApp;\
};\
\
SubProcessClass m_ ## SubProcessClass
class ProcessTestApp
{
public:
using SubProcessMain = std::function<int ()>;
static void invokeSubProcess();
// Many tests inside tst_qtcprocess need to start a new subprocess with custom code.
// In order to simplify things we produce just one separate executable - processtestapp.
// We embed all our custom subprocesses in processtestapp and enclose them in separate
// classes. We select desired process to run by setting the relevant environment variable.
// Test classes are defined by the SUB_PROCESS macro. The macro defines a class
// alongside of the corresponding environment variable which is set prior to the execution
// of the subprocess. The following subprocess classes are defined:
SUB_PROCESS(SimpleTest);
SUB_PROCESS(ExitCode);
SUB_PROCESS(RunBlockingStdOut);
SUB_PROCESS(LineCallback);
SUB_PROCESS(StandardOutputAndErrorWriter);
SUB_PROCESS(ChannelForwarding);
SUB_PROCESS(KillBlockingProcess);
SUB_PROCESS(EmitOneErrorOnCrash);
SUB_PROCESS(CrashAfterOneSecond);
// In order to get a value associated with the certain subprocess use SubProcessClass::envVar().
// The classes above define different custom executables. Inside invokeSubProcess(), called
// by processtestapp, we are detecting if one of these variables is set and invoke a respective
// custom executable code directly. The exit code of the process is reported to the caller
// by the return value of SubProcessClass::main().
private:
ProcessTestApp();
static void registerSubProcess(const char *envVar, const SubProcessMain &main);
static void unregisterSubProcess(const char *envVar);
};
class SubProcessConfig
{
public:
SubProcessConfig(const char *envVar, const QString &envVal);
void setupSubProcess(Utils::QtcProcess *subProcess);
static void setPathToProcessTestApp(const QString &path);
private:
const Utils::Environment m_environment;
};
static const char s_simpleTestData[] = "Test process successfully executed.";
static const char s_runBlockingStdOutSubProcessMagicWord[] = "42";
// Expect ending lines detected at '|':
const char s_lineCallbackData[] =
"This is the first line\r\n|"
"Here comes the second one\r\n|"
"And a line without LF\n|"
"Rebasing (1/10)\r| <delay> Rebasing (2/10)\r| <delay> ...\r\n|"
"And no end";
static const char s_outputData[] = "This is the output message.";
static const char s_errorData[] = "This is the error message.";
enum class BlockType {
EndlessLoop,
InfiniteSleep,
MutexDeadlock,
EventLoop
};
Q_DECLARE_METATYPE(BlockType)

View File

@@ -0,0 +1,30 @@
import qbs.FileInfo
QtApplication {
name: "processtestapp"
Depends { name: "app_version_header" }
Depends { name: "qtc" }
Depends { name: "Utils" }
consoleApplication: true
cpp.cxxLanguageVersion: "c++17"
cpp.defines: {
var defines = base;
var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix,
qtc.ide_libexec_path);
var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath);
defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"');
defines.push('PROCESS_TESTAPP="' + destinationDirectory + '"');
return defines;
}
install: false
destinationDirectory: project.buildDirectory + '/'
+ FileInfo.relativePath(project.ide_source_tree, sourceDirectory)
files: [
"main.cpp",
"processtestapp.cpp",
"processtestapp.h",
]
}

View File

@@ -1,19 +1,29 @@
import qbs.FileInfo
QtcAutotest {
name: "QtcProcess autotest"
Depends { name: "Utils" }
Depends { name: "app_version_header" }
Project {
QtcAutotest {
name: "QtcProcess autotest"
files: "tst_qtcprocess.cpp"
cpp.defines: {
var defines = base;
if (qbs.targetOS === "windows")
defines.push("_CRT_SECURE_NO_WARNINGS");
var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix,
qtc.ide_libexec_path);
var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath);
defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"');
return defines;
Depends { name: "Utils" }
Depends { name: "app_version_header" }
files: [
"processtestapp/processtestapp.cpp",
"processtestapp/processtestapp.h",
"tst_qtcprocess.cpp",
]
cpp.defines: {
var defines = base;
if (qbs.targetOS === "windows")
defines.push("_CRT_SECURE_NO_WARNINGS");
var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix,
qtc.ide_libexec_path);
var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath);
defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"');
defines.push('PROCESS_TESTAPP="'
+ FileInfo.joinPaths(destinationDirectory, "processtestapp") + '"');
return defines;
}
}
references: "processtestapp/processtestapp.qbs"
}

View File

@@ -23,6 +23,8 @@
**
****************************************************************************/
#include "processtestapp/processtestapp.h"
#include <app/app_version.h>
#include <utils/environment.h>
@@ -42,11 +44,6 @@
#include <iostream>
#include <fstream>
#ifdef Q_OS_WIN
#include <io.h>
#include <fcntl.h>
#endif
using namespace Utils;
// This handler is inspired by the one used in qtbase/tests/auto/corelib/io/qfile/tst_qfile.cpp
@@ -86,70 +83,6 @@ protected:
int MessageHandler::s_destroyCount = 0;
QtMessageHandler MessageHandler::s_oldMessageHandler = 0;
using SubProcessMain = std::function<void ()>;
static QMap<const char *, SubProcessMain> s_subProcesses = {};
static void invokeSubProcessIfRequired()
{
for (auto it = s_subProcesses.constBegin(); it != s_subProcesses.constEnd(); ++it) {
if (qEnvironmentVariableIsSet(it.key()))
it.value()();
}
}
static void registerSubProcess(const char *envVar, const SubProcessMain &main)
{
s_subProcesses.insert(envVar, main);
}
#define SUB_CREATOR_PROCESS(SubProcessClass)\
class SubProcessClass\
{\
public:\
SubProcessClass()\
{\
registerSubProcess(envVar(), &SubProcessClass::main);\
}\
static const char *envVar() { return m_envVar; }\
private:\
static void main();\
static constexpr char m_envVar[] = "TST_QTC_PROCESS_" QTC_ASSERT_STRINGIFY(SubProcessClass);\
};\
\
SubProcessClass m_ ## SubProcessClass
static Environment subEnvironment(const char *envVar, const QString &envVal)
{
Environment env = Environment::systemEnvironment();
env.set(envVar, envVal);
return env;
}
static CommandLine subCommandLine()
{
QStringList args = QCoreApplication::arguments();
const QString binary = args.takeFirst();
const FilePath filePath = FilePath::fromString(QDir::currentPath()).resolvePath(binary);
return CommandLine(filePath, args);
}
class SubCreatorConfig
{
public:
SubCreatorConfig(const char *envVar, const QString &envVal)
: m_environment(subEnvironment(envVar, envVal))
, m_commandLine(subCommandLine()) {}
void setupSubProcess(QtcProcess *subProcess)
{
subProcess->setEnvironment(m_environment);
subProcess->setCommand(m_commandLine);
}
private:
const Environment m_environment;
const CommandLine m_commandLine;
};
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
@@ -193,11 +126,6 @@ private:
QHash<QString, QString> m_map;
};
static void doCrash()
{
qFatal("The application has crashed purposefully!");
}
class tst_QtcProcess : public QObject
{
Q_OBJECT
@@ -238,31 +166,6 @@ private slots:
void cleanupTestCase();
private:
// 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. These tests are embedded in classes,
// defined by the SUB_CREATOR_PROCESS macro. The macro defines a class alongside of the
// corresponding environment variable which is set prior to the execution of the subprocess.
// The following subprocess classes are defined:
SUB_CREATOR_PROCESS(SimpleTest);
SUB_CREATOR_PROCESS(ExitCode);
SUB_CREATOR_PROCESS(RunBlockingStdOut);
SUB_CREATOR_PROCESS(LineCallback);
SUB_CREATOR_PROCESS(ChannelForwarding);
SUB_CREATOR_PROCESS(StandardOutputAndErrorWriter);
SUB_CREATOR_PROCESS(KillBlockingProcess);
SUB_CREATOR_PROCESS(EmitOneErrorOnCrash);
SUB_CREATOR_PROCESS(CrashAfterOneSecond);
// In order to get a value associated with the certain subprocess use SubProcessClass::envVar().
// The classes above define different custom executables. Inside initTestCase()
// we are detecting if one of these variables is set (by a call to
// invokeSubProcessIfRequired()) and invoke directly a respective custom
// executable code, which always ends up with a call to exit(). In this way (by calling exit()
// by the end of each subprocess executable) we stop the recursion, as from the test point of
// view we meant to execute only our custom code without further execution of the test itself.
void iteratorEditsHelper(OsType osType);
Environment envWindows;
@@ -276,23 +179,15 @@ private:
MessageHandler *msgHandler = nullptr;
};
static const char s_simpleTestData[] = "Test process successfully executed.";
void tst_QtcProcess::SimpleTest::main()
{
std::cout << s_simpleTestData << std::endl;
exit(0);
}
void tst_QtcProcess::initTestCase()
{
msgHandler = new MessageHandler;
Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/"
+ Core::Constants::IDE_CASED_ID + "-XXXXXX");
Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/'
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
invokeSubProcessIfRequired();
const QString libExecPath(qApp->applicationDirPath() + '/'
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
LauncherInterface::setPathToLauncher(libExecPath);
SubProcessConfig::setPathToProcessTestApp(QLatin1String(PROCESS_TESTAPP));
homeStr = QLatin1String("@HOME@");
home = QDir::homePath();
@@ -959,13 +854,6 @@ void tst_QtcProcess::iteratorEditsLinux()
iteratorEditsHelper(OsTypeLinux);
}
void tst_QtcProcess::ExitCode::main()
{
const int exitCode = qEnvironmentVariableIntValue(envVar());
std::cout << "Exiting with code:" << exitCode << std::endl;
exit(exitCode);
}
void tst_QtcProcess::exitCode_data()
{
QTest::addColumn<int>("exitCode");
@@ -984,7 +872,7 @@ void tst_QtcProcess::exitCode()
{
QFETCH(int, exitCode);
SubCreatorConfig subConfig(ExitCode::envVar(), QString::number(exitCode));
SubProcessConfig subConfig(ProcessTestApp::ExitCode::envVar(), QString::number(exitCode));
{
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1005,22 +893,6 @@ void tst_QtcProcess::exitCode()
}
}
static const char s_runBlockingStdOutSubProcessMagicWord[] = "42";
void tst_QtcProcess::RunBlockingStdOut::main()
{
std::cout << "Wait for the Answer to the Ultimate Question of Life, "
"The Universe, and Everything..." << std::endl;
QThread::msleep(300);
std::cout << s_runBlockingStdOutSubProcessMagicWord << "...Now wait for the question...";
if (qEnvironmentVariable(envVar()) == "true")
std::cout << std::endl;
else
std::cout << std::flush; // otherwise it won't reach the original process (will be buffered)
QThread::msleep(5000);
exit(0);
}
void tst_QtcProcess::runBlockingStdOut_data()
{
QTest::addColumn<bool>("withEndl");
@@ -1048,7 +920,7 @@ void tst_QtcProcess::runBlockingStdOut()
QFETCH(int, timeOutS);
QFETCH(ProcessResult, expectedResult);
SubCreatorConfig subConfig(RunBlockingStdOut::envVar(), withEndl ? "true" : "false");
SubProcessConfig subConfig(ProcessTestApp::RunBlockingStdOut::envVar(), withEndl ? "true" : "false");
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1068,28 +940,9 @@ void tst_QtcProcess::runBlockingStdOut()
QVERIFY2(readLastLine, "Last line was read.");
}
// Expect ending lines detected at '|':
const char s_lineCallbackData[] =
"This is the first line\r\n|"
"Here comes the second one\r\n|"
"And a line without LF\n|"
"Rebasing (1/10)\r| <delay> Rebasing (2/10)\r| <delay> ...\r\n|"
"And no end";
void tst_QtcProcess::LineCallback::main()
{
#ifdef Q_OS_WIN
// Prevent \r\n -> \r\r\n translation.
_setmode(_fileno(stderr), O_BINARY);
#endif
fprintf(stderr, "%s", QByteArray(s_lineCallbackData).replace('|', "").data());
exit(0);
}
void tst_QtcProcess::lineCallback()
{
SubCreatorConfig subConfig(LineCallback::envVar(), {});
SubProcessConfig subConfig(ProcessTestApp::LineCallback::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1133,7 +986,7 @@ void tst_QtcProcess::lineCallbackIntern()
void tst_QtcProcess::waitForStartedAndFinished()
{
SubCreatorConfig subConfig(SimpleTest::envVar(), {});
SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1187,32 +1040,6 @@ void tst_QtcProcess::notRunningAfterStartingNonExistingProgram()
// and the error channels. Then ChannelForwarding::main() either forwards these channels
// or not - we check it in the outer channelForwarding() test.
static const char s_outputData[] = "This is the output message.";
static const char s_errorData[] = "This is the error message.";
void tst_QtcProcess::StandardOutputAndErrorWriter::main()
{
std::cout << s_outputData << std::endl;
std::cerr << s_errorData << std::endl;
exit(0);
}
void tst_QtcProcess::ChannelForwarding::main()
{
const QProcess::ProcessChannelMode channelMode
= QProcess::ProcessChannelMode(qEnvironmentVariableIntValue(envVar()));
qunsetenv(envVar());
SubCreatorConfig subConfig(StandardOutputAndErrorWriter::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);
process.setProcessChannelMode(channelMode);
process.start();
process.waitForFinished();
exit(0);
}
void tst_QtcProcess::channelForwarding_data()
{
QTest::addColumn<QProcess::ProcessChannelMode>("channelMode");
@@ -1232,7 +1059,8 @@ void tst_QtcProcess::channelForwarding()
QFETCH(bool, outputForwarded);
QFETCH(bool, errorForwarded);
SubCreatorConfig subConfig(ChannelForwarding::envVar(), QString::number(int(channelMode)));
SubProcessConfig subConfig(ProcessTestApp::ChannelForwarding::envVar(),
QString::number(int(channelMode)));
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1275,7 +1103,7 @@ void tst_QtcProcess::mergedChannels()
QFETCH(bool, errorOnOutput);
QFETCH(bool, errorOnError);
SubCreatorConfig subConfig(StandardOutputAndErrorWriter::envVar(), {});
SubProcessConfig subConfig(ProcessTestApp::StandardOutputAndErrorWriter::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1292,43 +1120,6 @@ void tst_QtcProcess::mergedChannels()
QCOMPARE(error.contains(QByteArray(s_errorData)), errorOnError);
}
enum class BlockType {
EndlessLoop,
InfiniteSleep,
MutexDeadlock,
EventLoop
};
Q_DECLARE_METATYPE(BlockType)
void tst_QtcProcess::KillBlockingProcess::main()
{
std::cout << "Blocking process successfully executed." << std::endl;
const BlockType blockType = BlockType(qEnvironmentVariableIntValue(envVar()));
switch (blockType) {
case BlockType::EndlessLoop:
while (true)
;
break;
case BlockType::InfiniteSleep:
QThread::sleep(INT_MAX);
break;
case BlockType::MutexDeadlock: {
QMutex mutex;
mutex.lock();
mutex.lock();
break;
}
case BlockType::EventLoop: {
QEventLoop loop;
loop.exec();
break;
}
}
exit(1);
}
void tst_QtcProcess::killBlockingProcess_data()
{
QTest::addColumn<BlockType>("blockType");
@@ -1343,7 +1134,8 @@ void tst_QtcProcess::killBlockingProcess()
{
QFETCH(BlockType, blockType);
SubCreatorConfig subConfig(KillBlockingProcess::envVar(), QString::number(int(blockType)));
SubProcessConfig subConfig(ProcessTestApp::KillBlockingProcess::envVar(),
QString::number(int(blockType)));
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1354,7 +1146,7 @@ void tst_QtcProcess::killBlockingProcess()
void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead()
{
SubCreatorConfig subConfig(SimpleTest::envVar(), {});
SubProcessConfig subConfig(ProcessTestApp::SimpleTest::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1377,14 +1169,9 @@ void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead()
QVERIFY(reply.contains(s_simpleTestData));
}
void tst_QtcProcess::EmitOneErrorOnCrash::main()
{
doCrash();
}
void tst_QtcProcess::emitOneErrorOnCrash()
{
SubCreatorConfig subConfig(EmitOneErrorOnCrash::envVar(), {});
SubProcessConfig subConfig(ProcessTestApp::EmitOneErrorOnCrash::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);
@@ -1401,15 +1188,9 @@ void tst_QtcProcess::emitOneErrorOnCrash()
QCOMPARE(process.error(), QProcess::Crashed);
}
void tst_QtcProcess::CrashAfterOneSecond::main()
{
QThread::sleep(1);
doCrash();
}
void tst_QtcProcess::crashAfterOneSecond()
{
SubCreatorConfig subConfig(CrashAfterOneSecond::envVar(), {});
SubProcessConfig subConfig(ProcessTestApp::CrashAfterOneSecond::envVar(), {});
TestProcess process;
subConfig.setupSubProcess(&process);