From 5988fd0f5cae56999aa389405e1fa3e0394962ed Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 15 Jun 2016 12:29:24 +0200 Subject: [PATCH] AutoTest: Add minimum support for debugging tests This adds another context menu entry for items on the test tree to allow debugging of a single test. Task-number: QTCREATORBUG-16070 Change-Id: I98f56b0f22c94ad71f0b91d690383043ed27f1c7 Reviewed-by: hjk Reviewed-by: David Schulz --- src/plugins/autotest/autotest.pro | 3 +- src/plugins/autotest/autotest.qbs | 4 +- .../autotest/autotest_dependencies.pri | 3 +- src/plugins/autotest/autotestplugin.cpp | 4 +- src/plugins/autotest/gtest/gtesttreeitem.cpp | 5 ++ src/plugins/autotest/gtest/gtesttreeitem.h | 2 + .../autotest/qtest/qttestconfiguration.cpp | 4 +- .../autotest/qtest/qttestconfiguration.h | 12 ++- src/plugins/autotest/qtest/qttesttreeitem.cpp | 12 +++ src/plugins/autotest/qtest/qttesttreeitem.h | 2 + src/plugins/autotest/testconfiguration.cpp | 8 +- src/plugins/autotest/testconfiguration.h | 5 +- src/plugins/autotest/testnavigationwidget.cpp | 41 ++++++++-- src/plugins/autotest/testnavigationwidget.h | 4 +- src/plugins/autotest/testresultspane.cpp | 4 +- src/plugins/autotest/testrunconfiguration.h | 48 ++++++++++++ src/plugins/autotest/testrunner.cpp | 78 ++++++++++++++++++- src/plugins/autotest/testrunner.h | 11 ++- src/plugins/autotest/testtreeitem.h | 2 + 19 files changed, 227 insertions(+), 25 deletions(-) create mode 100644 src/plugins/autotest/testrunconfiguration.h diff --git a/src/plugins/autotest/autotest.pro b/src/plugins/autotest/autotest.pro index 18d665b3758..a3061ccf6c8 100644 --- a/src/plugins/autotest/autotest.pro +++ b/src/plugins/autotest/autotest.pro @@ -91,7 +91,8 @@ HEADERS += \ quick/quicktest_utils.h \ quick/quicktestvisitors.h \ quick/quicktestframework.h \ - testframeworkmanager.h + testframeworkmanager.h \ + testrunconfiguration.h RESOURCES += \ autotest.qrc diff --git a/src/plugins/autotest/autotest.qbs b/src/plugins/autotest/autotest.qbs index b3b55a294a0..dd8070571a4 100644 --- a/src/plugins/autotest/autotest.qbs +++ b/src/plugins/autotest/autotest.qbs @@ -10,6 +10,7 @@ QtcPlugin { Depends { name: "QmlJS" } Depends { name: "QmlJSTools" } Depends { name: "Utils" } + Depends { name: "Debugger" } pluginTestDepends: [ "QbsProjectManager", @@ -71,7 +72,8 @@ QtcPlugin { "itestparser.h", "itestframework.h", "testframeworkmanager.cpp", - "testframeworkmanager.h" + "testframeworkmanager.h", + "testrunconfiguration.h" ] Group { diff --git a/src/plugins/autotest/autotest_dependencies.pri b/src/plugins/autotest/autotest_dependencies.pri index ab2e02f054d..1580d58e099 100644 --- a/src/plugins/autotest/autotest_dependencies.pri +++ b/src/plugins/autotest/autotest_dependencies.pri @@ -4,7 +4,8 @@ QTC_PLUGIN_DEPENDS += \ coreplugin \ projectexplorer \ cpptools \ - qmljstools + qmljstools \ + debugger QTC_LIB_DEPENDS += \ cplusplus \ diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp index 9e053f1b096..05764ab3c25 100644 --- a/src/plugins/autotest/autotestplugin.cpp +++ b/src/plugins/autotest/autotestplugin.cpp @@ -162,7 +162,7 @@ void AutotestPlugin::onRunAllTriggered() TestRunner *runner = TestRunner::instance(); TestTreeModel *model = TestTreeModel::instance(); runner->setSelectedTests(model->getAllTestCases()); - runner->prepareToRunTests(); + runner->prepareToRunTests(TestRunner::Run); } void AutotestPlugin::onRunSelectedTriggered() @@ -170,7 +170,7 @@ void AutotestPlugin::onRunSelectedTriggered() TestRunner *runner = TestRunner::instance(); TestTreeModel *model = TestTreeModel::instance(); runner->setSelectedTests(model->getSelectedTests()); - runner->prepareToRunTests(); + runner->prepareToRunTests(TestRunner::Run); } void AutotestPlugin::updateMenuItemsEnabledState() diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp index 99f81a83583..1ed9f2cf3fd 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.cpp +++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp @@ -133,6 +133,11 @@ TestConfiguration *GTestTreeItem::testConfiguration() const return config; } +TestConfiguration *GTestTreeItem::debugConfiguration() const +{ + return testConfiguration(); +} + // used as key inside getAllTestCases()/getSelectedTestCases() for Google Tests class ProFileWithDisplayName { diff --git a/src/plugins/autotest/gtest/gtesttreeitem.h b/src/plugins/autotest/gtest/gtesttreeitem.h index 41e188b27ac..0fabfa4bf9a 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.h +++ b/src/plugins/autotest/gtest/gtesttreeitem.h @@ -53,7 +53,9 @@ public: QVariant data(int column, int role) const override; bool canProvideTestConfiguration() const override { return type() != Root; } + bool canProvideDebugConfiguration() const override { return type() != Root; } TestConfiguration *testConfiguration() const override; + TestConfiguration *debugConfiguration() const override; QList getAllTestConfigurations() const override; QList getSelectedTestConfigurations() const override; TestTreeItem *find(const TestParseResult *result) override; diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp index 004e01b833b..bb726eab111 100644 --- a/src/plugins/autotest/qtest/qttestconfiguration.cpp +++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp @@ -38,7 +38,9 @@ TestOutputReader *QtTestConfiguration::outputReader(const QFutureInterface &fi, QProcess *app) const override; QStringList argumentsForTestRunner(const TestSettings &settings) const override; + void setRunMode(RunMode mode) { m_runMode = mode; } + +private: + RunMode m_runMode; }; } // namespace Internal diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp index df520b2f0e3..3a8c75b9e8b 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.cpp +++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp @@ -84,6 +84,11 @@ bool QtTestTreeItem::canProvideTestConfiguration() const } } +bool QtTestTreeItem::canProvideDebugConfiguration() const +{ + return canProvideTestConfiguration(); +} + TestConfiguration *QtTestTreeItem::testConfiguration() const { ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); @@ -128,6 +133,13 @@ TestConfiguration *QtTestTreeItem::testConfiguration() const return config; } +TestConfiguration *QtTestTreeItem::debugConfiguration() const +{ + QtTestConfiguration *config = static_cast(testConfiguration()); + config->setRunMode(QtTestConfiguration::Debug); + return config; +} + QList QtTestTreeItem::getAllTestConfigurations() const { QList result; diff --git a/src/plugins/autotest/qtest/qttesttreeitem.h b/src/plugins/autotest/qtest/qttesttreeitem.h index 929144fe452..a53f0befa9b 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.h +++ b/src/plugins/autotest/qtest/qttesttreeitem.h @@ -40,7 +40,9 @@ public: QVariant data(int column, int role) const override; bool canProvideTestConfiguration() const override; + bool canProvideDebugConfiguration() const override; TestConfiguration *testConfiguration() const override; + TestConfiguration *debugConfiguration() const override; QList getAllTestConfigurations() const override; QList getSelectedTestConfigurations() const override; TestTreeItem *find(const TestParseResult *result) override; diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index 0dd8d0f283c..f5c24c49bb4 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -25,6 +25,8 @@ #include "testconfiguration.h" #include "testoutputreader.h" +#include "testrunconfiguration.h" +#include "testrunner.h" #include "testsettings.h" #include @@ -84,7 +86,7 @@ static bool isLocal(RunConfiguration *runConfiguration) return DeviceTypeKitInformation::deviceTypeId(kit) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; } -void TestConfiguration::completeTestInformation() +void TestConfiguration::completeTestInformation(int runMode) { QTC_ASSERT(!m_proFile.isEmpty(), return); @@ -99,6 +101,7 @@ void TestConfiguration::completeTestInformation() QString buildDir; Project *targetProject = 0; Utils::Environment env; + Target *runConfigTarget = 0; bool hasDesktopTarget = false; bool guessedRunConfiguration = false; setProject(0); @@ -152,6 +155,7 @@ void TestConfiguration::completeTestInformation() workDir = Utils::FileUtils::normalizePathName(stdRunnable.workingDirectory); env = stdRunnable.environment; hasDesktopTarget = true; + runConfigTarget = rc->target(); break; } } @@ -182,6 +186,8 @@ void TestConfiguration::completeTestInformation() setEnvironment(env); setProject(project); setGuessedConfiguration(guessedRunConfiguration); + if (!guessedRunConfiguration && runMode == TestRunner::Debug) + m_runConfig = new TestRunConfiguration(runConfigTarget); } } diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index 05c9bde5995..2ea58b84cf6 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -44,6 +44,7 @@ namespace Internal { class TestOutputReader; class TestResult; +class TestRunConfiguration; struct TestSettings; using TestResultPtr = QSharedPointer; @@ -55,7 +56,7 @@ public: explicit TestConfiguration(); virtual ~TestConfiguration(); - void completeTestInformation(); + void completeTestInformation(int runMode); void setTestCases(const QStringList &testCases); void setTestCaseCount(int count); @@ -79,6 +80,7 @@ public: QString displayName() const { return m_displayName; } Utils::Environment environment() const { return m_environment; } ProjectExplorer::Project *project() const { return m_project.data(); } + TestRunConfiguration *runConfiguration() const { return m_runConfig; } bool guessedConfiguration() const { return m_guessedConfiguration; } virtual TestOutputReader *outputReader(const QFutureInterface &fi, @@ -97,6 +99,7 @@ private: Utils::Environment m_environment; QPointer m_project; bool m_guessedConfiguration = false; + TestRunConfiguration *m_runConfig = 0; }; } // namespace Internal diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp index 92182cc04a4..39f616b1c2d 100644 --- a/src/plugins/autotest/testnavigationwidget.cpp +++ b/src/plugins/autotest/testnavigationwidget.cpp @@ -120,6 +120,7 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) QMenu menu; QAction *runThisTest = 0; + QAction *debugThisTest = 0; const QModelIndexList list = m_view->selectionModel()->selectedIndexes(); if (list.size() == 1) { const QModelIndex index = list.first(); @@ -131,7 +132,17 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) runThisTest = new QAction(tr("Run This Test"), &menu); runThisTest->setEnabled(enabled); connect(runThisTest, &QAction::triggered, - this, &TestNavigationWidget::onRunThisTestTriggered); + this, [this] () { + onRunThisTestTriggered(TestRunner::Run); + }); + } + if (item->canProvideDebugConfiguration()) { + debugThisTest = new QAction(tr("Debug This Test"), &menu); + debugThisTest->setEnabled(enabled); + connect(debugThisTest, &QAction::triggered, + this, [this] () { + onRunThisTestTriggered(TestRunner::Debug); + }); } } } @@ -152,10 +163,13 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) deselectAll->setEnabled(enabled && hasTests); rescan->setEnabled(enabled); - if (runThisTest) { + if (runThisTest) menu.addAction(runThisTest); + if (debugThisTest) + menu.addAction(debugThisTest); + if (runThisTest || debugThisTest) menu.addSeparator(); - } + menu.addAction(runAll); menu.addAction(runSelected); menu.addSeparator(); @@ -260,21 +274,32 @@ void TestNavigationWidget::initializeFilterMenu() m_filterMenu->addAction(action); } -void TestNavigationWidget::onRunThisTestTriggered() +void TestNavigationWidget::onRunThisTestTriggered(TestRunner::Mode runMode) { const QModelIndexList selected = m_view->selectionModel()->selectedIndexes(); - // paranoia if (selected.isEmpty()) return; - const QModelIndex sourceIndex = m_sortFilterModel->mapToSource(selected.first()); + const QModelIndex &sourceIndex = m_sortFilterModel->mapToSource(selected.first()); if (!sourceIndex.isValid()) return; TestTreeItem *item = static_cast(sourceIndex.internalPointer()); - if (TestConfiguration *configuration = item->testConfiguration()) { + TestConfiguration *configuration; + switch (runMode) { + case TestRunner::Run: + configuration = item->testConfiguration(); + break; + case TestRunner::Debug: + configuration = item->debugConfiguration(); + break; + default: + configuration = 0; + } + + if (configuration) { TestRunner *runner = TestRunner::instance(); runner->setSelectedTests( {configuration} ); - runner->prepareToRunTests(); + runner->prepareToRunTests(runMode); } } diff --git a/src/plugins/autotest/testnavigationwidget.h b/src/plugins/autotest/testnavigationwidget.h index 7bc900e2ce3..d64bf661e95 100644 --- a/src/plugins/autotest/testnavigationwidget.h +++ b/src/plugins/autotest/testnavigationwidget.h @@ -25,6 +25,8 @@ #pragma once +#include "testrunner.h" + #include #include @@ -74,7 +76,7 @@ private slots: private: void initializeFilterMenu(); - void onRunThisTestTriggered(); + void onRunThisTestTriggered(TestRunner::Mode runMode); TestTreeModel *m_model; TestTreeSortFilterModel *m_sortFilterModel; diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp index 71e15940f18..78b8a8971aa 100644 --- a/src/plugins/autotest/testresultspane.cpp +++ b/src/plugins/autotest/testresultspane.cpp @@ -397,14 +397,14 @@ void TestResultsPane::onRunAllTriggered() { TestRunner *runner = TestRunner::instance(); runner->setSelectedTests(TestTreeModel::instance()->getAllTestCases()); - runner->prepareToRunTests(); + runner->prepareToRunTests(TestRunner::Run); } void TestResultsPane::onRunSelectedTriggered() { TestRunner *runner = TestRunner::instance(); runner->setSelectedTests(TestTreeModel::instance()->getSelectedTests()); - runner->prepareToRunTests(); + runner->prepareToRunTests(TestRunner::Run); } void TestResultsPane::initializeFilterMenu() diff --git a/src/plugins/autotest/testrunconfiguration.h b/src/plugins/autotest/testrunconfiguration.h new file mode 100644 index 00000000000..ef471fc2369 --- /dev/null +++ b/src/plugins/autotest/testrunconfiguration.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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. +** +****************************************************************************/ + +#pragma once + +#include + +namespace Autotest { +namespace Internal { + +class TestRunConfiguration : public ProjectExplorer::RunConfiguration +{ +public: + TestRunConfiguration(ProjectExplorer::Target *parent) + : ProjectExplorer::RunConfiguration(parent, "AutoTest.TestRunConfig") + { + setDefaultDisplayName(tr("AutoTest Debug")); + addExtraAspects(); + } + +private: + QWidget *createConfigurationWidget() { return 0; } +}; + +} // namespace Internal +} // namespace Autotest diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 78990a9ab88..1802d278bf4 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -28,6 +28,7 @@ #include "autotestconstants.h" #include "autotestplugin.h" #include "testresultspane.h" +#include "testrunconfiguration.h" #include "testsettings.h" #include "testoutputreader.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include @@ -45,6 +47,9 @@ #include #include +#include +#include + namespace Autotest { namespace Internal { @@ -125,7 +130,7 @@ static void performTestRun(QFutureInterface &futureInterface, QEventLoop eventLoop; int testCaseCount = 0; foreach (TestConfiguration *config, selectedTests) { - config->completeTestInformation(); + config->completeTestInformation(TestRunner::Run); if (config->project()) { testCaseCount += config->testCaseCount(); } else { @@ -208,8 +213,9 @@ static void performTestRun(QFutureInterface &futureInterface, futureInterface.setProgressValue(testCaseCount); } -void TestRunner::prepareToRunTests() +void TestRunner::prepareToRunTests(Mode mode) { + m_runMode = mode; ProjectExplorer::Internal::ProjectExplorerSettings projectExplorerSettings = ProjectExplorer::ProjectExplorerPlugin::projectExplorerSettings(); if (projectExplorerSettings.buildBeforeDeploy && !projectExplorerSettings.saveBeforeBuild) { @@ -251,7 +257,7 @@ void TestRunner::prepareToRunTests() } if (!projectExplorerSettings.buildBeforeDeploy) { - runTests(); + runOrDebugTests(); } else { if (project->hasActiveBuildSettings()) { buildProject(project); @@ -272,6 +278,70 @@ void TestRunner::runTests() Core::ProgressManager::addTask(future, tr("Running Tests"), Autotest::Constants::TASK_INDEX); } +void TestRunner::debugTests() +{ + // TODO improve to support more than one test configuration + QTC_ASSERT(m_selectedTests.size() == 1, onFinished();return); + + TestConfiguration *config = m_selectedTests.first(); + config->completeTestInformation(Debug); + if (!config->runConfiguration()) { + emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal, + TestRunner::tr("Failed to get run configuration.")))); + onFinished(); + return; + } + + const QString &commandFilePath = executableFilePath(config->targetFile(), + config->environment().toProcessEnvironment()); + if (commandFilePath.isEmpty()) { + emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal, + TestRunner::tr("Could not find command \"%1\". (%2)") + .arg(config->targetFile()) + .arg(config->displayName())))); + onFinished(); + return; + } + + Debugger::DebuggerStartParameters sp; + sp.inferior.executable = commandFilePath; + sp.inferior.commandLineArguments = config->argumentsForTestRunner( + *AutotestPlugin::instance()->settings()).join(' '); + sp.inferior.environment = config->environment(); + sp.inferior.workingDirectory = config->workingDirectory(); + sp.displayName = config->displayName(); + + QString errorMessage; + Debugger::DebuggerRunControl *runControl = Debugger::createDebuggerRunControl( + sp, config->runConfiguration(), &errorMessage); + + if (!runControl) { + emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal, + TestRunner::tr("Failed to create run configuration.\n%1").arg(errorMessage)))); + onFinished(); + return; + } + + connect(runControl, &Debugger::DebuggerRunControl::finished, this, &TestRunner::onFinished); + ProjectExplorer::ProjectExplorerPlugin::startRunControl( + runControl, ProjectExplorer::Constants::DEBUG_RUN_MODE); + +} + +void TestRunner::runOrDebugTests() +{ + switch (m_runMode) { + case Run: + runTests(); + break; + case Debug: + debugTests(); + break; + default: + QTC_ASSERT(false, return); // unexpected run mode + } +} + void TestRunner::buildProject(ProjectExplorer::Project *project) { ProjectExplorer::BuildManager *buildManager = ProjectExplorer::BuildManager::instance(); @@ -290,7 +360,7 @@ void TestRunner::buildFinished(bool success) this, &TestRunner::buildFinished); if (success) { - runTests(); + runOrDebugTests(); } else { emit testResultReady(TestResultPtr(new FaultyTestResult(Result::MessageFatal, tr("Build failed. Canceling test run.")))); diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h index aca76ac5ced..b79840b2653 100644 --- a/src/plugins/autotest/testrunner.h +++ b/src/plugins/autotest/testrunner.h @@ -44,6 +44,12 @@ class TestRunner : public QObject Q_OBJECT public: + enum Mode + { + Run, + Debug + }; + static TestRunner* instance(); ~TestRunner(); @@ -57,7 +63,7 @@ signals: void testResultReady(const TestResultPtr &result); public slots: - void prepareToRunTests(); + void prepareToRunTests(Mode mode); private slots: void buildProject(ProjectExplorer::Project *project); @@ -66,11 +72,14 @@ private slots: private: void runTests(); + void debugTests(); + void runOrDebugTests(); explicit TestRunner(QObject *parent = 0); QFutureWatcher m_futureWatcher; QList m_selectedTests; bool m_executingTests; + Mode m_runMode = Run; // temporarily used if building before running is necessary QMetaObject::Connection m_buildConnect; diff --git a/src/plugins/autotest/testtreeitem.h b/src/plugins/autotest/testtreeitem.h index 325898c595d..0404bb46dcf 100644 --- a/src/plugins/autotest/testtreeitem.h +++ b/src/plugins/autotest/testtreeitem.h @@ -102,7 +102,9 @@ public: TestTreeItem *findChildByNameAndFile(const QString &name, const QString &filePath); virtual bool canProvideTestConfiguration() const { return false; } + virtual bool canProvideDebugConfiguration() const { return false; } virtual TestConfiguration *testConfiguration() const { return 0; } + virtual TestConfiguration *debugConfiguration() const { return 0; } virtual QList getAllTestConfigurations() const; virtual QList getSelectedTestConfigurations() const; virtual bool lessThan(const TestTreeItem *other, SortMode mode) const;