diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index 00f3f0dc31d..b4af50e8a99 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -72,11 +72,66 @@ static QString ensureExeEnding(const QString& file) return Utils::HostOsInfo::withExecutableSuffix(file); } +void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguration *rc, + TestRunMode runMode) +{ + QTC_ASSERT(rc, return); + Project *project = SessionManager::startupProject(); + if (!project) + return; + + Target *target = project->activeTarget(); + if (!target) + return; + + if (!Utils::findOr(target->runConfigurations(), nullptr, + [&rc] (RunConfiguration *config) { return rc == config; })) { + return; + } + + Runnable runnable = rc->runnable(); + if (!runnable.is()) + return; + m_runnable = runnable.as(); + m_displayName = rc->displayName(); + m_project = rc->project(); + + const QString buildSystemTarget = rc->buildSystemTarget(); + BuildTargetInfo targetInfo + = Utils::findOrDefault(target->applicationTargets().list, + [&buildSystemTarget] (const BuildTargetInfo &bti) { + return bti.targetName == buildSystemTarget; + }); + if (!targetInfo.targetFilePath.isEmpty()) + m_runnable.executable = ensureExeEnding(targetInfo.targetFilePath.toString()); + + QString buildBase; + if (auto buildConfig = target->activeBuildConfiguration()) { + buildBase = buildConfig->buildDirectory().toString(); + const QString projBase = m_project->projectDirectory().toString(); + if (m_projectFile.startsWith(projBase)) + m_buildDir = QFileInfo(buildBase + m_projectFile.mid(projBase.length())).absolutePath(); + } + if (runMode == TestRunMode::Debug || runMode == TestRunMode::DebugWithoutDeploy) + m_runConfig = new TestRunConfiguration(rc->target(), this); +} + void TestConfiguration::completeTestInformation(TestRunMode runMode) { QTC_ASSERT(!m_projectFile.isEmpty(), return); QTC_ASSERT(!m_buildTargets.isEmpty(), return); + if (m_origRunConfig) { + qCDebug(LOG) << "Using run configuration specified by user or found by first call"; + completeTestInformation(m_origRunConfig, runMode); + if (hasExecutable()) { + qCDebug(LOG) << "Completed.\nRunnable:" << m_runnable.executable + << "\nArgs:" << m_runnable.commandLineArguments + << "\nWorking directory:" << m_runnable.workingDirectory; + return; + } + qCDebug(LOG) << "Failed to complete - using 'normal' way."; + } Project *project = SessionManager::startupProject(); if (!project) return; @@ -163,6 +218,7 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode) return b.startsWith(currentBST); }))) { qCDebug(LOG) << " Using this RunConfig."; + m_origRunConfig = runConfig; m_runnable = stdRunnable; m_runnable.executable = currentExecutable; m_displayName = runConfig->displayName(); @@ -177,9 +233,9 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode) // or we might have end up using the (wrong) path of a locally installed executable // for this case try the original executable path of the BuildTargetInfo (the executable // before installation) to have at least something to execute - if (m_runnable.executable.isEmpty() && !localExecutable.isEmpty()) + if (!hasExecutable() && !localExecutable.isEmpty()) m_runnable.executable = localExecutable; - if (m_displayName.isEmpty() && !m_runnable.executable.isEmpty()) { + if (m_displayName.isEmpty() && hasExecutable()) { qCDebug(LOG) << " Fallback"; // we failed to find a valid runconfiguration - but we've got the executable already if (auto rc = target->activeRunConfiguration()) { @@ -265,9 +321,14 @@ void TestConfiguration::setInternalTargets(const QSet &targets) m_buildTargets = targets; } +void TestConfiguration::setOriginalRunConfiguration(RunConfiguration *runConfig) +{ + m_origRunConfig = runConfig; +} + QString TestConfiguration::executableFilePath() const { - if (m_runnable.executable.isEmpty()) + if (!hasExecutable()) return QString(); QFileInfo commandFileInfo(m_runnable.executable); @@ -305,5 +366,10 @@ bool DebuggableTestConfiguration::isDebugRunMode() const return m_runMode == TestRunMode::Debug || m_runMode == TestRunMode::DebugWithoutDeploy; } +bool TestConfiguration::hasExecutable() const +{ + return !m_runnable.executable.isEmpty(); +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index 7ec208af2a9..e35b0f0dd21 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -56,6 +56,7 @@ public: virtual ~TestConfiguration(); void completeTestInformation(TestRunMode runMode); + void completeTestInformation(ProjectExplorer::RunConfiguration *rc, TestRunMode runMode); void setTestCases(const QStringList &testCases); void setTestCaseCount(int count); @@ -67,6 +68,7 @@ public: void setEnvironment(const Utils::Environment &env); void setProject(ProjectExplorer::Project *project); void setInternalTargets(const QSet &targets); + void setOriginalRunConfiguration(ProjectExplorer::RunConfiguration *runConfig); QStringList testCases() const { return m_testCases; } int testCaseCount() const { return m_testCaseCount; } @@ -77,7 +79,10 @@ public: QString displayName() const { return m_displayName; } Utils::Environment environment() const { return m_runnable.environment; } ProjectExplorer::Project *project() const { return m_project.data(); } + QSet internalTargets() const { return m_buildTargets; } + ProjectExplorer::RunConfiguration *originalRunConfiguration() const { return m_origRunConfig; } TestRunConfiguration *runConfiguration() const { return m_runConfig; } + bool hasExecutable() const; bool isGuessed() const { return m_guessedConfiguration; } QString runConfigDisplayName() const { return m_guessedConfiguration ? m_guessedFrom : m_displayName; } @@ -96,8 +101,9 @@ private: QString m_guessedFrom; QPointer m_project; bool m_guessedConfiguration = false; - TestRunConfiguration *m_runConfig = 0; + TestRunConfiguration *m_runConfig = nullptr; QSet m_buildTargets; + ProjectExplorer::RunConfiguration *m_origRunConfig = nullptr; ProjectExplorer::StandardRunnable m_runnable; }; diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 4c9ba7f645d..b263f44e557 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -32,6 +32,7 @@ #include "testsettings.h" #include "testoutputreader.h" +#include #include #include @@ -39,19 +40,28 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include +#include #include #include +#include +#include #include #include #include +#include + namespace Autotest { namespace Internal { @@ -284,8 +294,57 @@ void TestRunner::prepareToRunTests(TestRunMode mode) } } +static QString firstTestCaseTarget(const TestConfiguration *config) +{ + const QSet &internalTargets = config->internalTargets(); + int size = internalTargets.size(); + if (size) + return (*internalTargets.begin()).split('|').first(); + return TestRunner::tr(""); +} + +static bool askUserForRunConfiguration(TestConfiguration *config) +{ + using namespace ProjectExplorer; + RunConfigurationSelectionDialog dialog(firstTestCaseTarget(config), + Core::ICore::dialogParent()); + if (dialog.exec() == QDialog::Accepted) { + const QString dName = dialog.displayName(); + if (dName.isEmpty()) + return false; + // run configuration has been selected - fill config based on this one.. + const QString exe = dialog.executable(); + // paranoia... can the current startup project have changed meanwhile? + if (auto project = SessionManager::startupProject()) { + if (auto target = project->activeTarget()) { + RunConfiguration *runConfig + = Utils::findOr(target->runConfigurations(), nullptr, + [&dName, &exe] (const RunConfiguration *rc) { + if (rc->displayName() != dName) + return false; + if (!rc->runnable().is()) + return false; + StandardRunnable runnable = rc->runnable().as(); + return runnable.executable == exe; + }); + if (runConfig) { + config->setOriginalRunConfiguration(runConfig); + return true; + } + } + } + } + return false; +} + void TestRunner::runTests() { + for (TestConfiguration *config : m_selectedTests) { + config->completeTestInformation(TestRunMode::Run); + if (!config->hasExecutable()) + if (askUserForRunConfiguration(config)) + config->completeTestInformation(config->originalRunConfiguration(), TestRunMode::Run); + } QFuture future = Utils::runAsync(&performTestRun, m_selectedTests, *AutotestPlugin::instance()->settings()); m_futureWatcher.setFuture(future); @@ -327,6 +386,11 @@ void TestRunner::debugTests() TestConfiguration *config = m_selectedTests.first(); config->completeTestInformation(TestRunMode::Debug); + if (!config->hasExecutable()) { + if (askUserForRunConfiguration(config)) + config->completeTestInformation(config->originalRunConfiguration(), TestRunMode::Debug); + } + if (!config->runConfiguration()) { emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal, TestRunner::tr("Failed to get run configuration.")))); @@ -453,5 +517,90 @@ void TestRunner::onFinished() emit testRunFinished(); } +/*************************************************************************************************/ + +RunConfigurationSelectionDialog::RunConfigurationSelectionDialog(const QString &testsInfo, + QWidget *parent) + : QDialog(parent) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Select Run Configuration")); + + m_details = new QLabel(tr("Could not determine which run configuration to choose for running" + " tests (%1)").arg(testsInfo), this); + m_rcCombo = new QComboBox(this); + m_executable = new QLabel(this); + m_arguments = new QLabel(this); + m_workingDir = new QLabel(this); + m_buttonBox = new QDialogButtonBox(this); + m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + auto line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + + auto formLayout = new QFormLayout; + formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + formLayout->addRow(m_details); + formLayout->addRow(tr("Run Configuration:"), m_rcCombo); + formLayout->addRow(line); + formLayout->addRow(tr("Executable:"), m_executable); + formLayout->addRow(tr("Arguments:"), m_arguments); + formLayout->addRow(tr("Working Directory:"), m_workingDir); + // TODO Device support + auto vboxLayout = new QVBoxLayout(this); + vboxLayout->addLayout(formLayout); + vboxLayout->addStretch(); + vboxLayout->addWidget(line); + vboxLayout->addWidget(m_buttonBox); + + connect(m_rcCombo, &QComboBox::currentTextChanged, + this, &RunConfigurationSelectionDialog::updateLabels); + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + populate(); +} + +QString RunConfigurationSelectionDialog::displayName() const +{ + return m_rcCombo ? m_rcCombo->currentText() : QString(); +} + +QString RunConfigurationSelectionDialog::executable() const +{ + return m_executable ? m_executable->text() : QString(); +} + +void RunConfigurationSelectionDialog::populate() +{ + m_rcCombo->addItem(QString(), QStringList({QString(), QString(), QString()})); // empty default + + if (auto project = ProjectExplorer::SessionManager::startupProject()) { + if (auto target = project->activeTarget()) { + for (ProjectExplorer::RunConfiguration *rc : target->runConfigurations()) { + if (rc->runnable().is()) { + auto runnable = rc->runnable().as(); + const QStringList rcDetails = { runnable.executable, + runnable.commandLineArguments, + runnable.workingDirectory }; + m_rcCombo->addItem(rc->displayName(), rcDetails); + } + } + } + } +} + +void RunConfigurationSelectionDialog::updateLabels() +{ + int i = m_rcCombo->currentIndex(); + const QStringList values = m_rcCombo->itemData(i).toStringList(); + QTC_ASSERT(values.size() == 3, return); + m_executable->setText(values.at(0)); + m_arguments->setText(values.at(1)); + m_workingDir->setText(values.at(2)); +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h index b146d7fb95e..004c4f48313 100644 --- a/src/plugins/autotest/testrunner.h +++ b/src/plugins/autotest/testrunner.h @@ -28,10 +28,17 @@ #include "testconfiguration.h" #include "testresult.h" +#include #include #include #include +QT_BEGIN_NAMESPACE +class QComboBox; +class QDialogButtonBox; +class QLabel; +QT_END_NAMESPACE + namespace ProjectExplorer { class Project; } @@ -77,5 +84,23 @@ private: QMetaObject::Connection m_buildConnect; }; +class RunConfigurationSelectionDialog : public QDialog +{ + Q_OBJECT +public: + explicit RunConfigurationSelectionDialog(const QString &testsInfo, QWidget *parent = nullptr); + QString displayName() const; + QString executable() const; +private: + void populate(); + void updateLabels(); + QLabel *m_details; + QLabel *m_executable; + QLabel *m_arguments; + QLabel *m_workingDir; + QComboBox *m_rcCombo; + QDialogButtonBox *m_buttonBox; +}; + } // namespace Internal } // namespace Autotest