diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index 764cf543a1b..912a563fa83 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.cpp +++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp @@ -74,13 +74,13 @@ void GTestOutputReader::processOutput(const QByteArray &outputLine) m_description = line; if (m_iteration > 1) m_description.append(' ' + tr("(iteration %1)").arg(m_iteration)); - TestResultPtr testResult = TestResultPtr(new GTestResult); + TestResultPtr testResult = TestResultPtr(new GTestResult(m_projectFile)); testResult->setResult(Result::MessageInternal); testResult->setDescription(m_description); m_futureInterface.reportResult(testResult); m_description.clear(); } else if (disabledTests.exactMatch(line)) { - TestResultPtr testResult = TestResultPtr(new GTestResult); + TestResultPtr testResult = TestResultPtr(new GTestResult(m_projectFile)); testResult->setResult(Result::MessageDisabledTests); int disabled = disabledTests.cap(1).toInt(); testResult->setDescription(tr("You have %n disabled test(s).", 0, disabled)); @@ -98,7 +98,6 @@ void GTestOutputReader::processOutput(const QByteArray &outputLine) m_futureInterface.reportResult(TestResultPtr(testResult)); m_currentTestName.clear(); m_currentTestSet.clear(); - m_normalizedCurrentTestSet.clear(); } else if (newTestStarts.exactMatch(line)) { setCurrentTestName(newTestStarts.cap(1)); TestResultPtr testResult = TestResultPtr(createDefaultResult()); @@ -112,7 +111,7 @@ void GTestOutputReader::processOutput(const QByteArray &outputLine) m_futureInterface.reportResult(testResult); } else if (newTestSetStarts.exactMatch(line)) { setCurrentTestSet(newTestSetStarts.cap(1)); - TestResultPtr testResult = TestResultPtr(new GTestResult); + TestResultPtr testResult = TestResultPtr(new GTestResult(m_projectFile)); testResult->setResult(Result::MessageCurrentTest); testResult->setDescription(tr("Entering test set %1").arg(m_currentTestSet)); m_futureInterface.reportResult(testResult); @@ -163,39 +162,21 @@ void GTestOutputReader::processOutput(const QByteArray &outputLine) void GTestOutputReader::setCurrentTestSet(const QString &testSet) { m_currentTestSet = testSet; - m_normalizedCurrentTestSet = normalizeName(testSet); } void GTestOutputReader::setCurrentTestName(const QString &testName) { m_currentTestName = testName; - m_normalizedTestName = normalizeTestName(testName); -} - -QString GTestOutputReader::normalizeName(const QString &name) const -{ - static QRegExp parameterIndex("/\\d+"); - - QString nameWithoutParameterIndices = name; - nameWithoutParameterIndices.remove(parameterIndex); - - return nameWithoutParameterIndices.split('/').last(); -} - -QString GTestOutputReader::normalizeTestName(const QString &testname) const -{ - QString nameWithoutTypeParam = testname.split(',').first(); - - return normalizeName(nameWithoutTypeParam); } GTestResult *GTestOutputReader::createDefaultResult() const { - GTestResult *result = new GTestResult(m_executable, m_currentTestName); + GTestResult *result = new GTestResult(m_executable, m_projectFile, m_currentTestName); result->setTestSetName(m_currentTestSet); result->setIteration(m_iteration); - const TestTreeItem *testItem = findTestTreeItemForCurrentLine(); + const TestTreeItem *testItem = result->findTestTreeItem(); + if (testItem && testItem->line()) { result->setFileName(testItem->filePath()); result->setLine(static_cast(testItem->line())); @@ -204,43 +185,5 @@ GTestResult *GTestOutputReader::createDefaultResult() const return result; } -const TestTreeItem *GTestOutputReader::findTestTreeItemForCurrentLine() const -{ - const auto item = TestTreeModel::instance()->findNonRootItem([&](const Utils::TreeItem *item) { - const TestTreeItem &treeItem = static_cast(*item); - return matches(treeItem); - }); - - return static_cast(item); -} - -bool GTestOutputReader::matches(const TestTreeItem &treeItem) const -{ - if (treeItem.proFile() != m_projectFile) - return false; - - if (m_currentTestSet.isEmpty()) - return matchesTestCase(treeItem); - - return matchesTestFunctionOrSet(treeItem); -} - -bool GTestOutputReader::matchesTestFunctionOrSet(const TestTreeItem &treeItem) const -{ - if (treeItem.type() != TestTreeItem::TestFunctionOrSet) - return false; - - const QString testItemTestSet = treeItem.parentItem()->name() + '.' + treeItem.name(); - return testItemTestSet == m_normalizedCurrentTestSet; -} - -bool GTestOutputReader::matchesTestCase(const TestTreeItem &treeItem) const -{ - if (treeItem.type() != TestTreeItem::TestCase) - return false; - - return treeItem.name() == m_normalizedTestName; -} - } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/gtest/gtestoutputreader.h b/src/plugins/autotest/gtest/gtestoutputreader.h index e46e29fcd3f..ebe59cd64d4 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.h +++ b/src/plugins/autotest/gtest/gtestoutputreader.h @@ -50,20 +50,12 @@ protected: private: void setCurrentTestSet(const QString &testSet); void setCurrentTestName(const QString &testName); - QString normalizeName(const QString &name) const; - QString normalizeTestName(const QString &testname) const; GTestResult *createDefaultResult() const; - const TestTreeItem *findTestTreeItemForCurrentLine() const; - bool matches(const TestTreeItem &treeItem) const; - bool matchesTestFunctionOrSet(const TestTreeItem &treeItem) const; - bool matchesTestCase(const TestTreeItem &treeItem) const; QString m_executable; QString m_projectFile; QString m_currentTestName; - QString m_normalizedTestName; QString m_currentTestSet; - QString m_normalizedCurrentTestSet; QString m_description; int m_iteration = 1; }; diff --git a/src/plugins/autotest/gtest/gtestresult.cpp b/src/plugins/autotest/gtest/gtestresult.cpp index cbccaea837e..5945ebf5aa9 100644 --- a/src/plugins/autotest/gtest/gtestresult.cpp +++ b/src/plugins/autotest/gtest/gtestresult.cpp @@ -24,17 +24,20 @@ ****************************************************************************/ #include "gtestresult.h" +#include "../testtreemodel.h" +#include "../testtreeitem.h" namespace Autotest { namespace Internal { -GTestResult::GTestResult(const QString &name) - : TestResult(name) +GTestResult::GTestResult(const QString &projectFile, const QString &name) + : TestResult(name), m_projectFile(projectFile) { } -GTestResult::GTestResult(const QString &executable, const QString &name) - : TestResult(executable, name) +GTestResult::GTestResult(const QString &executable, const QString &projectFile, + const QString &name) + : TestResult(executable, name), m_projectFile(projectFile) { } @@ -68,5 +71,60 @@ bool GTestResult::isDirectParentOf(const TestResult *other, bool *needsIntermedi return isTest() && gtOther->isTestSet(); } +static QString normalizeName(const QString &name) +{ + static QRegExp parameterIndex("/\\d+"); + + QString nameWithoutParameterIndices = name; + nameWithoutParameterIndices.remove(parameterIndex); + + return nameWithoutParameterIndices.split('/').last(); +} + +static QString normalizeTestName(const QString &testname) +{ + QString nameWithoutTypeParam = testname.split(',').first(); + + return normalizeName(nameWithoutTypeParam); +} + +const TestTreeItem *GTestResult::findTestTreeItem() const +{ + const auto item = TestTreeModel::instance()->findNonRootItem([this](const Utils::TreeItem *item) { + const TestTreeItem &treeItem = static_cast(*item); + return matches(treeItem); + }); + + return static_cast(item); +} + +bool GTestResult::matches(const TestTreeItem &treeItem) const +{ + if (treeItem.proFile() != m_projectFile) + return false; + + if (isTest()) + return matchesTestCase(treeItem); + + return matchesTestFunctionOrSet(treeItem); +} + +bool GTestResult::matchesTestFunctionOrSet(const TestTreeItem &treeItem) const +{ + if (treeItem.type() != TestTreeItem::TestFunctionOrSet) + return false; + + const QString testItemTestSet = treeItem.parentItem()->name() + '.' + treeItem.name(); + return testItemTestSet == normalizeName(m_testSetName); +} + +bool GTestResult::matchesTestCase(const TestTreeItem &treeItem) const +{ + if (treeItem.type() != TestTreeItem::TestCase) + return false; + + return treeItem.name() == normalizeTestName(name()); +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/gtest/gtestresult.h b/src/plugins/autotest/gtest/gtestresult.h index 491fe41662f..88e5476085c 100644 --- a/src/plugins/autotest/gtest/gtestresult.h +++ b/src/plugins/autotest/gtest/gtestresult.h @@ -33,17 +33,25 @@ namespace Internal { class GTestResult : public TestResult { public: - explicit GTestResult(const QString &name = QString()); - GTestResult(const QString &executable, const QString &name); + GTestResult(const QString &projectFile, const QString &name = QString()); + GTestResult(const QString &executable, const QString &projectFile, const QString &name); const QString outputString(bool selected) const override; void setTestSetName(const QString &testSetName) { m_testSetName = testSetName; } void setIteration(int iteration) { m_iteration = iteration; } bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const override; + virtual const TestTreeItem *findTestTreeItem() const override; + private: bool isTest() const { return m_testSetName.isEmpty(); } bool isTestSet() const { return !m_testSetName.isEmpty(); } + + bool matches(const TestTreeItem &item) const; + bool matchesTestFunctionOrSet(const TestTreeItem &treeItem) const; + bool matchesTestCase(const TestTreeItem &treeItem) const; + QString m_testSetName; + QString m_projectFile; int m_iteration = 1; }; diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp index ddc010ef0d2..aaeef64a1a6 100644 --- a/src/plugins/autotest/testnavigationwidget.cpp +++ b/src/plugins/autotest/testnavigationwidget.cpp @@ -301,25 +301,7 @@ void TestNavigationWidget::onRunThisTestTriggered(TestRunMode runMode) return; TestTreeItem *item = static_cast(sourceIndex.internalPointer()); - TestConfiguration *configuration; - switch (runMode) { - case TestRunMode::Run: - case TestRunMode::RunWithoutDeploy: - configuration = item->testConfiguration(); - break; - case TestRunMode::Debug: - case TestRunMode::DebugWithoutDeploy: - configuration = item->debugConfiguration(); - break; - default: - configuration = nullptr; - } - - if (configuration) { - TestRunner *runner = TestRunner::instance(); - runner->setSelectedTests({configuration}); - runner->prepareToRunTests(runMode); - } + TestRunner::instance()->runTest(runMode, item); } TestNavigationWidgetFactory::TestNavigationWidgetFactory() diff --git a/src/plugins/autotest/testresult.cpp b/src/plugins/autotest/testresult.cpp index 2dcd9dda70a..96db2b10c63 100644 --- a/src/plugins/autotest/testresult.cpp +++ b/src/plugins/autotest/testresult.cpp @@ -58,6 +58,11 @@ const QString TestResult::outputString(bool selected) const return selected ? m_description : m_description.split('\n').first(); } +const TestTreeItem *TestResult::findTestTreeItem() const +{ + return nullptr; +} + Result::Type TestResult::resultFromString(const QString &resultString) { if (resultString == "pass") diff --git a/src/plugins/autotest/testresult.h b/src/plugins/autotest/testresult.h index a7414f85d36..d094c345ed3 100644 --- a/src/plugins/autotest/testresult.h +++ b/src/plugins/autotest/testresult.h @@ -35,6 +35,8 @@ namespace Autotest { namespace Internal { +class TestTreeItem; + namespace Result{ enum Type { Pass, FIRST_TYPE = Pass, @@ -76,6 +78,7 @@ public: virtual ~TestResult() {} virtual const QString outputString(bool selected) const; + virtual const TestTreeItem *findTestTreeItem() const; QString executable() const { return m_executable; } QString name() const { return m_name; } diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp index 332a518d94f..c3317fdfe96 100644 --- a/src/plugins/autotest/testresultspane.cpp +++ b/src/plugins/autotest/testresultspane.cpp @@ -144,7 +144,7 @@ TestResultsPane::TestResultsPane(QObject *parent) : connect(m_treeView, &Utils::TreeView::customContextMenuRequested, this, &TestResultsPane::onCustomContextMenuRequested); connect(m_treeView, &ResultsTreeView::copyShortcutTriggered, [this] () { - onCopyItemTriggered(m_treeView->currentIndex()); + onCopyItemTriggered(getTestResult(m_treeView->currentIndex())); }); connect(m_model, &TestResultModel::requestExpansion, [this] (QModelIndex idx) { m_treeView->expand(m_filterModel->mapFromSource(idx)); @@ -562,11 +562,12 @@ void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos) { const bool resultsAvailable = m_filterModel->hasResults(); const bool enabled = !m_testRunning && resultsAvailable; - const QModelIndex clicked = m_treeView->indexAt(pos); + const TestResult *clicked = getTestResult(m_treeView->indexAt(pos)); QMenu menu; + QAction *action = new QAction(tr("Copy"), &menu); action->setShortcut(QKeySequence(QKeySequence::Copy)); - action->setEnabled(resultsAvailable); + action->setEnabled(resultsAvailable && clicked); connect(action, &QAction::triggered, [this, clicked] () { onCopyItemTriggered(clicked); }); @@ -582,14 +583,36 @@ void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos) connect(action, &QAction::triggered, this, &TestResultsPane::onSaveWholeTriggered); menu.addAction(action); + action = new QAction(tr("Run This Test"), &menu); + action->setEnabled(clicked && clicked->findTestTreeItem()); + connect(action, &QAction::triggered, this, [this, clicked] { + onRunThisTestTriggered(TestRunMode::Run, clicked); + }); + menu.addAction(action); + + action = new QAction(tr("Debug This Test"), &menu); + action->setEnabled(clicked && clicked->findTestTreeItem()); + connect(action, &QAction::triggered, this, [this, clicked] { + onRunThisTestTriggered(TestRunMode::Debug, clicked); + }); + menu.addAction(action); + menu.exec(m_treeView->mapToGlobal(pos)); } -void TestResultsPane::onCopyItemTriggered(const QModelIndex &idx) +const TestResult *TestResultsPane::getTestResult(const QModelIndex &idx) { if (!idx.isValid()) - return; + return nullptr; + const TestResult *result = m_filterModel->testResult(idx); + QTC_CHECK(result); + + return result; +} + +void TestResultsPane::onCopyItemTriggered(const TestResult *result) +{ QTC_ASSERT(result, return); QApplication::clipboard()->setText(result->outputString(true)); } @@ -614,6 +637,16 @@ void TestResultsPane::onSaveWholeTriggered() } } +void TestResultsPane::onRunThisTestTriggered(TestRunMode runMode, const TestResult *result) +{ + QTC_ASSERT(result, return); + + const TestTreeItem *item = result->findTestTreeItem(); + + if (item) + TestRunner::instance()->runTest(runMode, item); +} + void TestResultsPane::toggleOutputStyle() { const bool displayText = m_outputWidget->currentIndex() == 0; diff --git a/src/plugins/autotest/testresultspane.h b/src/plugins/autotest/testresultspane.h index 6b837055672..911396b1939 100644 --- a/src/plugins/autotest/testresultspane.h +++ b/src/plugins/autotest/testresultspane.h @@ -52,6 +52,7 @@ namespace Internal { class TestResultModel; class TestResultFilterModel; +class TestResult; class ResultsTreeView : public Utils::TreeView { @@ -109,9 +110,11 @@ private: void onScrollBarRangeChanged(int, int max); void updateRunActions(); void onCustomContextMenuRequested(const QPoint &pos); - void onCopyItemTriggered(const QModelIndex &idx); + const TestResult *getTestResult(const QModelIndex &idx); + void onCopyItemTriggered(const TestResult *result); void onCopyWholeTriggered(); void onSaveWholeTriggered(); + void onRunThisTestTriggered(TestRunMode runMode, const TestResult *result); void toggleOutputStyle(); QString getWholeOutput(const QModelIndex &parent = QModelIndex()); diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index b263f44e557..81bc2c7451d 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -31,6 +31,7 @@ #include "testrunconfiguration.h" #include "testsettings.h" #include "testoutputreader.h" +#include "testtreeitem.h" #include #include @@ -106,6 +107,28 @@ void TestRunner::setSelectedTests(const QList &selected) m_selectedTests = selected; } +void TestRunner::runTest(TestRunMode mode, const TestTreeItem *item) +{ + TestConfiguration *configuration; + switch (mode) { + case TestRunMode::Run: + case TestRunMode::RunWithoutDeploy: + configuration = item->testConfiguration(); + break; + case TestRunMode::Debug: + case TestRunMode::DebugWithoutDeploy: + configuration = item->debugConfiguration(); + break; + default: + configuration = nullptr; + } + + if (configuration) { + setSelectedTests({configuration}); + prepareToRunTests(mode); + } +} + static QString processInformation(const QProcess &proc) { QString information("\nCommand line: " + proc.program() + ' ' + proc.arguments().join(' ')); diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h index 004c4f48313..0638401ca08 100644 --- a/src/plugins/autotest/testrunner.h +++ b/src/plugins/autotest/testrunner.h @@ -55,6 +55,7 @@ public: ~TestRunner(); void setSelectedTests(const QList &selected); + void runTest(TestRunMode mode, const TestTreeItem *item); bool isTestRunning() const { return m_executingTests; } void prepareToRunTests(TestRunMode mode);