AutoTest: Filter out interfering environment variables

If there are environment variables that might interfere with
the test process filter them out before setting the environment
for the test process as they can - in the worst case - even
lead to being not able to execute the test application at all.

Task-number: QTCREATORBUG-21012
Change-Id: I4a2059cf527395498824a0914fd6c3203eca9bcf
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2018-08-29 12:33:15 +02:00
parent d20b907b94
commit d058dcc326
10 changed files with 74 additions and 9 deletions

View File

@@ -107,5 +107,17 @@ QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) con
return arguments;
}
Utils::Environment GTestConfiguration::filteredEnvironment(const Utils::Environment &original) const
{
const QStringList interfering{"GTEST_FILTER", "GTEST_COLOR", "GTEST_ALSO_RUN_DISABLED_TESTS",
"GTEST_REPEAT", "GTEST_SHUFFLE", "GTEST_RANDOM_SEED",
"GTEST_OUTPUT", "GTEST_BREAK_ON_FAILURE", "GTEST_PRINT_TIME",
"GTEST_CATCH_EXCEPTIONS"};
Utils::Environment result = original;
for (const QString &key : interfering)
result.unset(key);
return result;
}
} // namespace Internal
} // namespace Autotest

View File

@@ -37,6 +37,7 @@ public:
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
} // namespace Internal

View File

@@ -25,7 +25,9 @@
#include "qttest_utils.h"
#include "qttesttreeitem.h"
#include "../autotestplugin.h"
#include "../testframeworkmanager.h"
#include "../testsettings.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -140,6 +142,17 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted,
return allowed;
}
Utils::Environment prepareBasicEnvironment(const Utils::Environment &env)
{
Utils::Environment result(env);
if (Utils::HostOsInfo::isWindowsHost())
result.set("QT_LOGGING_TO_CONSOLE", "1");
const int timeout = AutotestPlugin::settings()->timeout;
if (timeout > 5 * 60 * 1000) // Qt5.5 introduced hard limit, Qt5.6.1 added env var to raise this
result.set("QTEST_FUNCTION_TIMEOUT", QString::number(timeout));
return result;
}
} // namespace QTestUtils
} // namespace Internal
} // namespace Autotest

View File

@@ -28,6 +28,7 @@
#include <QHash>
namespace Core { class Id; }
namespace Utils { class Environment; }
namespace Autotest {
namespace Internal {
@@ -37,6 +38,7 @@ bool isQTestMacro(const QByteArray &macro);
QHash<QString, QString> testCaseNamesForFiles(const Core::Id &id, const QStringList &files);
QMultiHash<QString, QString> alternativeFiles(const Core::Id &id, const QStringList &files);
QStringList filterInterfering(const QStringList &provided, QStringList *omitted, bool isQuickTest);
Utils::Environment prepareBasicEnvironment(const Utils::Environment &env);
} // namespace QTestUtils
} // namespace Internal

View File

@@ -84,5 +84,10 @@ QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) co
return arguments;
}
Utils::Environment QtTestConfiguration::filteredEnvironment(const Utils::Environment &original) const
{
return QTestUtils::prepareBasicEnvironment(original);
}
} // namespace Internal
} // namespace Autotest

View File

@@ -37,6 +37,7 @@ public:
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};
} // namespace Internal

View File

@@ -86,6 +86,11 @@ QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted)
return arguments;
}
Utils::Environment QuickTestConfiguration::filteredEnvironment(const Utils::Environment &original) const
{
return QTestUtils::prepareBasicEnvironment(original);
}
void QuickTestConfiguration::setUnnamedOnly(bool unnamedOnly)
{
m_unnamedOnly = unnamedOnly;

View File

@@ -37,7 +37,7 @@ public:
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
void setUnnamedOnly(bool unnamedOnly);
bool unnamedOnly() const { return m_unnamedOnly; }

View File

@@ -92,7 +92,7 @@ public:
virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const = 0;
virtual QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const = 0;
virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const = 0;
private:
QStringList m_testCases;
int m_testCaseCount = 0;

View File

@@ -148,6 +148,15 @@ static QString constructOmittedDetailsString(const QStringList &omitted)
"configuration page for \"%1\":") + '\n' + omitted.join('\n');
}
static QString constructOmittedVariablesDetailsString(const QList<Utils::EnvironmentItem> &diff)
{
auto removedVars = Utils::transform<QStringList>(diff, [](const Utils::EnvironmentItem &it) {
return it.name;
});
return TestRunner::tr("Omitted the following environment variables for \"%1\":")
+ '\n' + removedVars.join('\n');
}
void TestRunner::scheduleNext()
{
QTC_ASSERT(!m_selectedTests.isEmpty(), onFinished(); return);
@@ -192,17 +201,23 @@ void TestRunner::scheduleNext()
details.arg(m_currentConfig->displayName()))));
}
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
QProcessEnvironment environment = m_currentConfig->environment().toProcessEnvironment();
if (Utils::HostOsInfo::isWindowsHost())
environment.insert("QT_LOGGING_TO_CONSOLE", "1");
const int timeout = AutotestPlugin::settings()->timeout;
if (timeout > 5 * 60 * 1000) // Qt5.5 introduced hard limit, Qt5.6.1 added env var to raise this
environment.insert("QTEST_FUNCTION_TIMEOUT", QString::number(timeout));
m_currentProcess->setProcessEnvironment(environment);
const Utils::Environment &original = m_currentConfig->environment();
Utils::Environment environment = m_currentConfig->filteredEnvironment(original);
const QList<Utils::EnvironmentItem> removedVariables
= Utils::filtered(original.diff(environment), [](const Utils::EnvironmentItem &it) {
return it.operation == Utils::EnvironmentItem::Unset;
});
if (!removedVariables.isEmpty()) {
const QString &details = constructOmittedVariablesDetailsString(removedVariables)
.arg(m_currentConfig->displayName());
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn, details)));
}
m_currentProcess->setProcessEnvironment(environment.toProcessEnvironment());
connect(m_currentProcess,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &TestRunner::onProcessFinished);
const int timeout = AutotestPlugin::settings()->timeout;
QTimer::singleShot(timeout, m_currentProcess, [this]() { cancelCurrent(Timeout); });
m_currentProcess->start();
@@ -521,6 +536,17 @@ void TestRunner::debugTests()
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
details.arg(config->displayName()))));
}
Utils::Environment original(inferior.environment);
inferior.environment = config->filteredEnvironment(original);
const QList<Utils::EnvironmentItem> removedVariables
= Utils::filtered(original.diff(inferior.environment), [](const Utils::EnvironmentItem &it) {
return it.operation == Utils::EnvironmentItem::Unset;
});
if (!removedVariables.isEmpty()) {
const QString &details = constructOmittedVariablesDetailsString(removedVariables)
.arg(config->displayName());
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn, details)));
}
auto debugger = new Debugger::DebuggerRunTool(runControl);
debugger->setInferior(inferior);
debugger->setRunControlName(config->displayName());