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; 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 Internal
} // namespace Autotest } // namespace Autotest

View File

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

View File

@@ -25,7 +25,9 @@
#include "qttest_utils.h" #include "qttest_utils.h"
#include "qttesttreeitem.h" #include "qttesttreeitem.h"
#include "../autotestplugin.h"
#include "../testframeworkmanager.h" #include "../testframeworkmanager.h"
#include "../testsettings.h"
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -140,6 +142,17 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted,
return allowed; 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 QTestUtils
} // namespace Internal } // namespace Internal
} // namespace Autotest } // namespace Autotest

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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