forked from qt-creator/qt-creator
AutoTest: Fix handling of test runs when project changes
If the startup project or the current active target changes while tests are running strange things can happen as we rely on these both being unchanged from triggering the run. Explicitly check for these being unchanged and cancel the test run if necessary. Change-Id: I506c7b1c0ca4b6ea31559556f6141fe9276d0ad0 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -75,12 +75,14 @@ void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguratio
|
|||||||
TestRunMode runMode)
|
TestRunMode runMode)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(rc, return);
|
QTC_ASSERT(rc, return);
|
||||||
|
QTC_ASSERT(m_project, return);
|
||||||
|
|
||||||
if (hasExecutable()) {
|
if (hasExecutable()) {
|
||||||
qCDebug(LOG) << "Executable has been set already - not completing configuration again.";
|
qCDebug(LOG) << "Executable has been set already - not completing configuration again.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Project *project = SessionManager::startupProject();
|
Project *project = SessionManager::startupProject();
|
||||||
if (!project)
|
if (!project || project != m_project)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Target *target = project->activeTarget();
|
Target *target = project->activeTarget();
|
||||||
@@ -94,7 +96,6 @@ void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguratio
|
|||||||
|
|
||||||
m_runnable = rc->runnable();
|
m_runnable = rc->runnable();
|
||||||
m_displayName = rc->displayName();
|
m_displayName = rc->displayName();
|
||||||
m_project = rc->project();
|
|
||||||
|
|
||||||
const QString buildKey = rc->buildKey();
|
const QString buildKey = rc->buildKey();
|
||||||
BuildTargetInfo targetInfo = target->applicationTargets().buildTargetInfo(buildKey);
|
BuildTargetInfo targetInfo = target->applicationTargets().buildTargetInfo(buildKey);
|
||||||
@@ -116,6 +117,7 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(!m_projectFile.isEmpty(), return);
|
QTC_ASSERT(!m_projectFile.isEmpty(), return);
|
||||||
QTC_ASSERT(!m_buildTargets.isEmpty(), return);
|
QTC_ASSERT(!m_buildTargets.isEmpty(), return);
|
||||||
|
QTC_ASSERT(m_project, return);
|
||||||
|
|
||||||
if (m_origRunConfig) {
|
if (m_origRunConfig) {
|
||||||
qCDebug(LOG) << "Using run configuration specified by user or found by first call";
|
qCDebug(LOG) << "Using run configuration specified by user or found by first call";
|
||||||
@@ -129,8 +131,10 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
|
|||||||
qCDebug(LOG) << "Failed to complete - using 'normal' way.";
|
qCDebug(LOG) << "Failed to complete - using 'normal' way.";
|
||||||
}
|
}
|
||||||
Project *project = SessionManager::startupProject();
|
Project *project = SessionManager::startupProject();
|
||||||
if (!project)
|
if (!project || project != m_project) {
|
||||||
|
m_project = nullptr;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Target *target = project->activeTarget();
|
Target *target = project->activeTarget();
|
||||||
if (!target)
|
if (!target)
|
||||||
@@ -204,7 +208,6 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
|
|||||||
m_runnable = runnable;
|
m_runnable = runnable;
|
||||||
m_runnable.executable = currentExecutable;
|
m_runnable.executable = currentExecutable;
|
||||||
m_displayName = runConfig->displayName();
|
m_displayName = runConfig->displayName();
|
||||||
m_project = project;
|
|
||||||
if (runMode == TestRunMode::Debug || runMode == TestRunMode::DebugWithoutDeploy)
|
if (runMode == TestRunMode::Debug || runMode == TestRunMode::DebugWithoutDeploy)
|
||||||
m_runConfig = new TestRunConfiguration(runConfig->target(), this);
|
m_runConfig = new TestRunConfiguration(runConfig->target(), this);
|
||||||
break;
|
break;
|
||||||
@@ -224,7 +227,6 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
|
|||||||
if (isLocal(rc)) { // FIXME for now only Desktop support
|
if (isLocal(rc)) { // FIXME for now only Desktop support
|
||||||
const Runnable runnable = rc->runnable();
|
const Runnable runnable = rc->runnable();
|
||||||
m_runnable.environment = runnable.environment;
|
m_runnable.environment = runnable.environment;
|
||||||
m_project = project;
|
|
||||||
m_guessedConfiguration = true;
|
m_guessedConfiguration = true;
|
||||||
m_guessedFrom = rc->displayName();
|
m_guessedFrom = rc->displayName();
|
||||||
if (runMode == TestRunMode::Debug)
|
if (runMode == TestRunMode::Debug)
|
||||||
|
@@ -154,6 +154,7 @@ void TestRunner::scheduleNext()
|
|||||||
QTC_ASSERT(!m_selectedTests.isEmpty(), onFinished(); return);
|
QTC_ASSERT(!m_selectedTests.isEmpty(), onFinished(); return);
|
||||||
QTC_ASSERT(!m_currentConfig && !m_currentProcess, resetInternalPointers());
|
QTC_ASSERT(!m_currentConfig && !m_currentProcess, resetInternalPointers());
|
||||||
QTC_ASSERT(m_fakeFutureInterface, onFinished(); return);
|
QTC_ASSERT(m_fakeFutureInterface, onFinished(); return);
|
||||||
|
QTC_ASSERT(!m_canceled, onFinished(); return);
|
||||||
|
|
||||||
m_currentConfig = m_selectedTests.dequeue();
|
m_currentConfig = m_selectedTests.dequeue();
|
||||||
|
|
||||||
@@ -215,10 +216,16 @@ void TestRunner::scheduleNext()
|
|||||||
|
|
||||||
void TestRunner::cancelCurrent(TestRunner::CancelReason reason)
|
void TestRunner::cancelCurrent(TestRunner::CancelReason reason)
|
||||||
{
|
{
|
||||||
|
m_canceled = true;
|
||||||
if (reason == UserCanceled) {
|
if (reason == UserCanceled) {
|
||||||
// when using the stop button we need to report, for progress bar this happens automatically
|
// when using the stop button we need to report, for progress bar this happens automatically
|
||||||
if (m_fakeFutureInterface && !m_fakeFutureInterface->isCanceled())
|
if (m_fakeFutureInterface && !m_fakeFutureInterface->isCanceled())
|
||||||
m_fakeFutureInterface->reportCanceled();
|
m_fakeFutureInterface->reportCanceled();
|
||||||
|
} else if (reason == KitChanged) {
|
||||||
|
if (m_fakeFutureInterface)
|
||||||
|
m_fakeFutureInterface->reportCanceled();
|
||||||
|
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
|
||||||
|
tr("Current kit has changed. Canceling test run."))));
|
||||||
}
|
}
|
||||||
if (m_currentProcess && m_currentProcess->state() != QProcess::NotRunning) {
|
if (m_currentProcess && m_currentProcess->state() != QProcess::NotRunning) {
|
||||||
m_currentProcess->kill();
|
m_currentProcess->kill();
|
||||||
@@ -279,6 +286,7 @@ void TestRunner::prepareToRunTests(TestRunMode mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_executingTests = true;
|
m_executingTests = true;
|
||||||
|
m_canceled = false;
|
||||||
emit testRunStarted();
|
emit testRunStarted();
|
||||||
|
|
||||||
// clear old log and output pane
|
// clear old log and output pane
|
||||||
@@ -301,6 +309,9 @@ void TestRunner::prepareToRunTests(TestRunMode mode)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_targetConnect = connect(project, &ProjectExplorer::Project::activeTargetChanged,
|
||||||
|
[this]() { cancelCurrent(KitChanged); });
|
||||||
|
|
||||||
if (!projectExplorerSettings.buildBeforeDeploy || mode == TestRunMode::DebugWithoutDeploy
|
if (!projectExplorerSettings.buildBeforeDeploy || mode == TestRunMode::DebugWithoutDeploy
|
||||||
|| mode == TestRunMode::RunWithoutDeploy) {
|
|| mode == TestRunMode::RunWithoutDeploy) {
|
||||||
runOrDebugTests();
|
runOrDebugTests();
|
||||||
@@ -386,9 +397,13 @@ int TestRunner::precheckTestConfigurations()
|
|||||||
void TestRunner::runTests()
|
void TestRunner::runTests()
|
||||||
{
|
{
|
||||||
QList<TestConfiguration *> toBeRemoved;
|
QList<TestConfiguration *> toBeRemoved;
|
||||||
|
bool projectChanged = false;
|
||||||
for (TestConfiguration *config : m_selectedTests) {
|
for (TestConfiguration *config : m_selectedTests) {
|
||||||
config->completeTestInformation(TestRunMode::Run);
|
config->completeTestInformation(TestRunMode::Run);
|
||||||
if (!config->hasExecutable()) {
|
if (!config->project()) {
|
||||||
|
projectChanged = true;
|
||||||
|
toBeRemoved.append(config);
|
||||||
|
} else if (!config->hasExecutable()) {
|
||||||
if (auto rc = getRunConfiguration(firstTestCaseTarget(config)))
|
if (auto rc = getRunConfiguration(firstTestCaseTarget(config)))
|
||||||
config->setOriginalRunConfiguration(rc);
|
config->setOriginalRunConfiguration(rc);
|
||||||
else
|
else
|
||||||
@@ -400,8 +415,10 @@ void TestRunner::runTests()
|
|||||||
qDeleteAll(toBeRemoved);
|
qDeleteAll(toBeRemoved);
|
||||||
toBeRemoved.clear();
|
toBeRemoved.clear();
|
||||||
if (m_selectedTests.isEmpty()) {
|
if (m_selectedTests.isEmpty()) {
|
||||||
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
|
QString mssg = projectChanged ? tr("Startup project has changed. Canceling test run.")
|
||||||
tr("No test cases left for execution. Canceling test run."))));
|
: tr("No test cases left for execution. Canceling test run.");
|
||||||
|
|
||||||
|
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn, mssg)));
|
||||||
onFinished();
|
onFinished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -454,6 +471,12 @@ void TestRunner::debugTests()
|
|||||||
|
|
||||||
TestConfiguration *config = m_selectedTests.first();
|
TestConfiguration *config = m_selectedTests.first();
|
||||||
config->completeTestInformation(TestRunMode::Debug);
|
config->completeTestInformation(TestRunMode::Debug);
|
||||||
|
if (!config->project()) {
|
||||||
|
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
|
||||||
|
TestRunner::tr("Startup project has changed. Canceling test run."))));
|
||||||
|
onFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!config->hasExecutable()) {
|
if (!config->hasExecutable()) {
|
||||||
if (auto *rc = getRunConfiguration(firstTestCaseTarget(config)))
|
if (auto *rc = getRunConfiguration(firstTestCaseTarget(config)))
|
||||||
config->completeTestInformation(rc, TestRunMode::Debug);
|
config->completeTestInformation(rc, TestRunMode::Debug);
|
||||||
@@ -573,7 +596,10 @@ void TestRunner::buildFinished(bool success)
|
|||||||
this, &TestRunner::buildFinished);
|
this, &TestRunner::buildFinished);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
if (!m_canceled)
|
||||||
runOrDebugTests();
|
runOrDebugTests();
|
||||||
|
else if (m_executingTests)
|
||||||
|
onFinished();
|
||||||
} else {
|
} else {
|
||||||
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal,
|
emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal,
|
||||||
tr("Build failed. Canceling test run."))));
|
tr("Build failed. Canceling test run."))));
|
||||||
@@ -587,6 +613,7 @@ void TestRunner::onFinished()
|
|||||||
qDeleteAll(m_selectedTests);
|
qDeleteAll(m_selectedTests);
|
||||||
m_selectedTests.clear();
|
m_selectedTests.clear();
|
||||||
|
|
||||||
|
disconnect(m_targetConnect);
|
||||||
m_fakeFutureInterface = nullptr;
|
m_fakeFutureInterface = nullptr;
|
||||||
m_executingTests = false;
|
m_executingTests = false;
|
||||||
emit testRunFinished();
|
emit testRunFinished();
|
||||||
|
@@ -54,7 +54,7 @@ class AUTOTESTSHARED_EXPORT TestRunner : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum CancelReason { UserCanceled, Timeout };
|
enum CancelReason { UserCanceled, Timeout, KitChanged };
|
||||||
|
|
||||||
static TestRunner* instance();
|
static TestRunner* instance();
|
||||||
~TestRunner();
|
~TestRunner();
|
||||||
@@ -91,6 +91,7 @@ private:
|
|||||||
QFutureInterface<TestResultPtr> *m_fakeFutureInterface = nullptr;
|
QFutureInterface<TestResultPtr> *m_fakeFutureInterface = nullptr;
|
||||||
QQueue<TestConfiguration *> m_selectedTests;
|
QQueue<TestConfiguration *> m_selectedTests;
|
||||||
bool m_executingTests = false;
|
bool m_executingTests = false;
|
||||||
|
bool m_canceled = false;
|
||||||
TestConfiguration *m_currentConfig = nullptr;
|
TestConfiguration *m_currentConfig = nullptr;
|
||||||
QProcess *m_currentProcess = nullptr;
|
QProcess *m_currentProcess = nullptr;
|
||||||
TestOutputReader *m_currentOutputReader = nullptr;
|
TestOutputReader *m_currentOutputReader = nullptr;
|
||||||
@@ -98,6 +99,8 @@ private:
|
|||||||
|
|
||||||
// temporarily used if building before running is necessary
|
// temporarily used if building before running is necessary
|
||||||
QMetaObject::Connection m_buildConnect;
|
QMetaObject::Connection m_buildConnect;
|
||||||
|
// temporarily used for handling of switching the current target
|
||||||
|
QMetaObject::Connection m_targetConnect;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RunConfigurationSelectionDialog : public QDialog
|
class RunConfigurationSelectionDialog : public QDialog
|
||||||
|
Reference in New Issue
Block a user