diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp index bb8f62aacfb..157c9873e58 100644 --- a/src/plugins/autotest/qtest/qttestconfiguration.cpp +++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp @@ -46,9 +46,9 @@ TestOutputReader *QtTestConfiguration::outputReader(const QFutureInterfaceuseXMLOutput) - return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::XML); + return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), QtTestOutputReader::XML); else - return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::PlainText); + return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), QtTestOutputReader::PlainText); } QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) const diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp index 5ffc4f2db9b..dcbcc1215c0 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.cpp +++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp @@ -25,6 +25,7 @@ #include "qttestoutputreader.h" #include "qttestresult.h" +#include "../testtreeitem.h" #include @@ -130,9 +131,10 @@ static QString constructSourceFilePath(const QString &path, const QString &fileP QtTestOutputReader::QtTestOutputReader(const QFutureInterface &futureInterface, QProcess *testApplication, const QString &buildDirectory, - OutputMode mode) + const QString &projectFile, OutputMode mode) : TestOutputReader(futureInterface, testApplication, buildDirectory) , m_executable(testApplication ? testApplication->program() : QString()) + , m_projectFile(projectFile) , m_mode(mode) { } @@ -269,12 +271,7 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine) } else if (currentTag == QStringLiteral("TestCase")) { sendFinishMessage(false); } else if (validEndTags.contains(currentTag.toString())) { - QtTestResult *testResult = createDefaultResult(); - testResult->setResult(m_result); - testResult->setFileName(m_file); - testResult->setLine(m_lineNumber); - testResult->setDescription(m_description); - reportResult(TestResultPtr(testResult)); + sendCompleteInformation(); if (currentTag == QStringLiteral("Incident")) m_dataTag.clear(); } @@ -420,7 +417,7 @@ void QtTestOutputReader::processSummaryFinishOutput() QtTestResult *QtTestOutputReader::createDefaultResult() const { - QtTestResult *result = new QtTestResult(m_executable, m_className); + QtTestResult *result = new QtTestResult(m_executable, m_projectFile, m_className); result->setFunctionName(m_testCase); result->setDataTag(m_dataTag); return result; @@ -430,15 +427,24 @@ void QtTestOutputReader::sendCompleteInformation() { TestResultPtr testResult = TestResultPtr(createDefaultResult()); testResult->setResult(m_result); - testResult->setFileName(m_file); - testResult->setLine(m_lineNumber); + + if (m_lineNumber) { + testResult->setFileName(m_file); + testResult->setLine(m_lineNumber); + } else { + const TestTreeItem *testItem = testResult->findTestTreeItem(); + if (testItem && testItem->line()) { + testResult->setFileName(testItem->filePath()); + testResult->setLine(static_cast(testItem->line())); + } + } testResult->setDescription(m_description); reportResult(testResult); } void QtTestOutputReader::sendMessageCurrentTest() { - TestResultPtr testResult = TestResultPtr(new QtTestResult); + TestResultPtr testResult = TestResultPtr(new QtTestResult(m_projectFile)); testResult->setResult(Result::MessageCurrentTest); testResult->setDescription(tr("Entering test function %1::%2").arg(m_className, m_testCase)); reportResult(testResult); @@ -450,6 +456,11 @@ void QtTestOutputReader::sendStartMessage(bool isFunction) testResult->setResult(Result::MessageTestCaseStart); testResult->setDescription(isFunction ? tr("Executing test function %1").arg(m_testCase) : tr("Executing test case %1").arg(m_className)); + const TestTreeItem *testItem = testResult->findTestTreeItem(); + if (testItem && testItem->line()) { + testResult->setFileName(testItem->filePath()); + testResult->setLine(static_cast(testItem->line())); + } reportResult(testResult); } diff --git a/src/plugins/autotest/qtest/qttestoutputreader.h b/src/plugins/autotest/qtest/qttestoutputreader.h index 75f0b9a42ea..f5aafc467de 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.h +++ b/src/plugins/autotest/qtest/qttestoutputreader.h @@ -48,7 +48,7 @@ public: QtTestOutputReader(const QFutureInterface &futureInterface, QProcess *testApplication, const QString &buildDirectory, - OutputMode mode); + const QString &projectFile, OutputMode mode); protected: void processOutput(const QByteArray &outputLine) override; @@ -79,6 +79,7 @@ private: CDATAMode m_cdataMode = None; QString m_executable; + QString m_projectFile; QString m_className; QString m_testCase; QString m_formerTestCase; diff --git a/src/plugins/autotest/qtest/qttestresult.cpp b/src/plugins/autotest/qtest/qttestresult.cpp index 68c9f2bf3f3..944ede2da4f 100644 --- a/src/plugins/autotest/qtest/qttestresult.cpp +++ b/src/plugins/autotest/qtest/qttestresult.cpp @@ -24,19 +24,22 @@ ****************************************************************************/ #include "qttestresult.h" +#include "../testtreemodel.h" #include +#include namespace Autotest { namespace Internal { -QtTestResult::QtTestResult(const QString &className) - : TestResult(className) +QtTestResult::QtTestResult(const QString &projectFile, const QString &className) + : TestResult(className), m_projectFile(projectFile) { } -QtTestResult::QtTestResult(const QString &executable, const QString &className) - : TestResult(executable, className) +QtTestResult::QtTestResult(const QString &executable, const QString &projectFile, + const QString &className) + : TestResult(executable, className), m_projectFile(projectFile) { } @@ -106,20 +109,96 @@ bool QtTestResult::isIntermediateFor(const TestResult *other) const QTC_ASSERT(other, return false); const QtTestResult *qtOther = static_cast(other); return m_dataTag == qtOther->m_dataTag && m_function == qtOther->m_function - && name() == qtOther->name() && executable() == qtOther->executable(); + && name() == qtOther->name() && executable() == qtOther->executable() + && m_projectFile == qtOther->m_projectFile; } TestResult *QtTestResult::createIntermediateResultFor(const TestResult *other) { QTC_ASSERT(other, return nullptr); const QtTestResult *qtOther = static_cast(other); - QtTestResult *intermediate = new QtTestResult(qtOther->executable(), qtOther->name()); + QtTestResult *intermediate = new QtTestResult(qtOther->executable(), qtOther->m_projectFile, qtOther->name()); intermediate->m_function = qtOther->m_function; intermediate->m_dataTag = qtOther->m_dataTag; // intermediates will be needed only for data tags intermediate->setDescription("Data tag: " + qtOther->m_dataTag); + const auto correspondingItem = intermediate->findTestTreeItem(); + if (correspondingItem && correspondingItem->line()) { + intermediate->setFileName(correspondingItem->filePath()); + intermediate->setLine(static_cast(correspondingItem->line())); + } return intermediate; } +const TestTreeItem *QtTestResult::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 QtTestResult::matches(const TestTreeItem *item) const +{ + QTC_ASSERT(item, return false); + TestTreeItem *parentItem = item->parentItem(); + QTC_ASSERT(parentItem, return false); + + TestTreeItem::Type type = item->type(); + switch (type) { + case TestTreeItem::TestCase: + if (!isTestCase()) + return false; + if (item->proFile() != m_projectFile) + return false; + return matchesTestCase(item); + case TestTreeItem::TestFunctionOrSet: + case TestTreeItem::TestSpecialFunction: + if (!isTestFunction()) + return false; + if (parentItem->proFile() != m_projectFile) + return false; + return matchesTestFunction(item); + case TestTreeItem::TestDataTag: { + if (!isDataTag()) + return false; + TestTreeItem *grandParentItem = parentItem->parentItem(); + QTC_ASSERT(grandParentItem, return false); + if (grandParentItem->proFile() != m_projectFile) + return false; + return matchesTestFunction(item); + } + default: + Q_FALLTHROUGH(); + } + + return false; +} + +bool QtTestResult::matchesTestCase(const TestTreeItem *item) const +{ + // FIXME this will never work for Quick Tests + if (item->name() == name()) + return true; + return false; +} + +bool QtTestResult::matchesTestFunction(const TestTreeItem *item) const +{ + TestTreeItem *parentItem = item->parentItem(); + TestTreeItem::Type type = item->type(); + if (m_function.contains("::")) { // Quick tests have slightly different layout // BAD/WRONG! + const QStringList tmp = m_function.split("::"); + QTC_ASSERT(tmp.size() == 2, return false); + return item->name() == tmp.last() && parentItem->name() == tmp.first(); + } + if (type == TestTreeItem::TestDataTag) { + TestTreeItem *grandParentItem = parentItem->parentItem(); + return parentItem->name() == m_function && grandParentItem->name() == name(); + } + return item->name() == m_function && parentItem->name() == name(); +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/qtest/qttestresult.h b/src/plugins/autotest/qtest/qttestresult.h index c7c9e8ae2a9..f26de9a6211 100644 --- a/src/plugins/autotest/qtest/qttestresult.h +++ b/src/plugins/autotest/qtest/qttestresult.h @@ -33,8 +33,8 @@ namespace Internal { class QtTestResult : public TestResult { public: - explicit QtTestResult(const QString &className = QString()); - QtTestResult(const QString &executable, const QString &className); + explicit QtTestResult(const QString &projectFile, const QString &className = QString()); + QtTestResult(const QString &executable, const QString &projectFile, const QString &className); const QString outputString(bool selected) const override; void setFunctionName(const QString &functionName) { m_function = functionName; } @@ -43,13 +43,19 @@ public: bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const override; bool isIntermediateFor(const TestResult *other) const override; TestResult *createIntermediateResultFor(const TestResult *other) override; + const TestTreeItem *findTestTreeItem() const override; private: bool isTestCase() const { return m_function.isEmpty() && m_dataTag.isEmpty(); } bool isTestFunction() const { return !m_function.isEmpty() && m_dataTag.isEmpty(); } bool isDataTag() const { return !m_function.isEmpty() && !m_dataTag.isEmpty(); } + bool matches(const TestTreeItem *item) const; + bool matchesTestCase(const TestTreeItem *item) const; + bool matchesTestFunction(const TestTreeItem *item) const; + QString m_function; QString m_dataTag; + QString m_projectFile; }; } // namespace Internal diff --git a/src/plugins/autotest/quick/quicktestconfiguration.cpp b/src/plugins/autotest/quick/quicktestconfiguration.cpp index 88920e0352f..827b350cfe6 100644 --- a/src/plugins/autotest/quick/quicktestconfiguration.cpp +++ b/src/plugins/autotest/quick/quicktestconfiguration.cpp @@ -45,9 +45,9 @@ TestOutputReader *QuickTestConfiguration::outputReader(const QFutureInterfaceuseXMLOutput) - return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::XML); + return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), QtTestOutputReader::XML); else - return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::PlainText); + return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), QtTestOutputReader::PlainText); } QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) const