diff --git a/src/plugins/autotest/gtest/gtestresult.cpp b/src/plugins/autotest/gtest/gtestresult.cpp index 1e3a6f7d7b8..e9cfe0f89cf 100644 --- a/src/plugins/autotest/gtest/gtestresult.cpp +++ b/src/plugins/autotest/gtest/gtestresult.cpp @@ -52,5 +52,14 @@ const QString GTestResult::outputString(bool selected) const return output; } +bool GTestResult::isDirectParentOf(const TestResult *other, bool *needsIntermediate) const +{ + if (!TestResult::isDirectParentOf(other, needsIntermediate)) + return false; + + const GTestResult *gtOther = static_cast(other); + return isTest() && gtOther->isTestSet(); +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/gtest/gtestresult.h b/src/plugins/autotest/gtest/gtestresult.h index 03083bf5359..0c69ed4c408 100644 --- a/src/plugins/autotest/gtest/gtestresult.h +++ b/src/plugins/autotest/gtest/gtestresult.h @@ -37,8 +37,10 @@ public: const QString outputString(bool selected) const override; void setTestSetName(const QString &testSetName) { m_testSetName = testSetName; } - + bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const override; private: + bool isTest() const { return m_testSetName.isEmpty(); } + bool isTestSet() const { return !m_testSetName.isEmpty(); } QString m_testSetName; }; diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp index c0f468d073d..3ee409cda1e 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.cpp +++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp @@ -162,14 +162,18 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine) if (currentTag == QStringLiteral("TestCase")) { m_className = m_xmlReader.attributes().value(QStringLiteral("name")).toString(); QTC_ASSERT(!m_className.isEmpty(), continue); - TestResultPtr testResult = TestResultPtr(new QtTestResult(m_className)); + TestResultPtr testResult = TestResultPtr(createDefaultResult()); testResult->setResult(Result::MessageTestCaseStart); testResult->setDescription(tr("Executing test case %1").arg(m_className)); m_futureInterface.reportResult(testResult); } else if (currentTag == QStringLiteral("TestFunction")) { m_testCase = m_xmlReader.attributes().value(QStringLiteral("name")).toString(); QTC_ASSERT(!m_testCase.isEmpty(), continue); - TestResultPtr testResult = TestResultPtr(new QtTestResult()); + TestResultPtr testResult = TestResultPtr(createDefaultResult()); + testResult->setResult(Result::MessageTestCaseStart); + testResult->setDescription(tr("Executing test function %1").arg(m_testCase)); + m_futureInterface.reportResult(testResult); + testResult = TestResultPtr(new QtTestResult); testResult->setResult(Result::MessageCurrentTest); testResult->setDescription(tr("Entering test function %1::%2").arg(m_className, m_testCase)); @@ -179,7 +183,6 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine) QTC_ASSERT(!m_duration.isEmpty(), continue); } else if (currentTag == QStringLiteral("Message") || currentTag == QStringLiteral("Incident")) { - m_dataTag.clear(); m_description.clear(); m_duration.clear(); m_file.clear(); @@ -189,15 +192,15 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine) m_result = TestResult::resultFromString( attributes.value(QStringLiteral("type")).toString()); m_file = decode(attributes.value(QStringLiteral("file")).toString()); - if (!m_file.isEmpty()) { + if (!m_file.isEmpty()) m_file = constructSourceFilePath(m_buildDir, m_file); - } m_lineNumber = attributes.value(QStringLiteral("line")).toInt(); } else if (currentTag == QStringLiteral("BenchmarkResult")) { const QXmlStreamAttributes &attributes = m_xmlReader.attributes(); const QString metric = attributes.value(QStringLiteral("metric")).toString(); const double value = attributes.value(QStringLiteral("value")).toDouble(); const int iterations = attributes.value(QStringLiteral("iterations")).toInt(); + m_dataTag = attributes.value(QStringLiteral("tag")).toString(); m_description = constructBenchmarkInformation(metric, value, iterations); m_result = Result::Benchmark; } else if (currentTag == QStringLiteral("DataTag")) { @@ -250,30 +253,31 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine) m_cdataMode = None; const QStringRef currentTag = m_xmlReader.name(); if (currentTag == QStringLiteral("TestFunction")) { - if (!m_duration.isEmpty()) { - QtTestResult *testResult = new QtTestResult(m_className); - testResult->setFunctionName(m_testCase); - testResult->setResult(Result::MessageInternal); - testResult->setDescription(tr("Execution took %1 ms.").arg(m_duration)); - m_futureInterface.reportResult(TestResultPtr(testResult)); - } + QtTestResult *testResult = createDefaultResult(); + testResult->setResult(Result::MessageTestCaseEnd); + testResult->setDescription( + m_duration.isEmpty() ? tr("Test function finished.") + : tr("Execution took %1 ms.").arg(m_duration)); + m_futureInterface.reportResult(TestResultPtr(testResult)); m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1); + m_dataTag.clear(); + m_testCase.clear(); } else if (currentTag == QStringLiteral("TestCase")) { - QtTestResult *testResult = new QtTestResult(m_className); + QtTestResult *testResult = createDefaultResult(); testResult->setResult(Result::MessageTestCaseEnd); testResult->setDescription( m_duration.isEmpty() ? tr("Test finished.") : tr("Test execution took %1 ms.").arg(m_duration)); m_futureInterface.reportResult(TestResultPtr(testResult)); } else if (validEndTags.contains(currentTag.toString())) { - QtTestResult *testResult = new QtTestResult(m_className); - testResult->setFunctionName(m_testCase); - testResult->setDataTag(m_dataTag); + QtTestResult *testResult = createDefaultResult(); testResult->setResult(m_result); testResult->setFileName(m_file); testResult->setLine(m_lineNumber); testResult->setDescription(m_description); m_futureInterface.reportResult(TestResultPtr(testResult)); + if (currentTag == QStringLiteral("Incident")) + m_dataTag.clear(); } break; } @@ -283,5 +287,13 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine) } } +QtTestResult *QtTestOutputReader::createDefaultResult() const +{ + QtTestResult *result = new QtTestResult(m_className); + result->setFunctionName(m_testCase); + result->setDataTag(m_dataTag); + return result; +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/qtest/qttestoutputreader.h b/src/plugins/autotest/qtest/qttestoutputreader.h index a39ec640421..2ae74d60d27 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.h +++ b/src/plugins/autotest/qtest/qttestoutputreader.h @@ -33,6 +33,8 @@ namespace Autotest { namespace Internal { +class QtTestResult; + class QtTestOutputReader : public TestOutputReader { Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::QtTestOutputReader) @@ -45,6 +47,8 @@ protected: void processOutput(const QByteArray &outputLine) override; private: + QtTestResult *createDefaultResult() const; + enum CDATAMode { None, diff --git a/src/plugins/autotest/qtest/qttestresult.cpp b/src/plugins/autotest/qtest/qttestresult.cpp index 199009f9433..f4c108ec2e3 100644 --- a/src/plugins/autotest/qtest/qttestresult.cpp +++ b/src/plugins/autotest/qtest/qttestresult.cpp @@ -25,6 +25,8 @@ #include "qttestresult.h" +#include + namespace Autotest { namespace Internal { @@ -71,5 +73,47 @@ const QString QtTestResult::outputString(bool selected) const return output; } +bool QtTestResult::isDirectParentOf(const TestResult *other, bool *needsIntermediate) const +{ + if (!TestResult::isDirectParentOf(other, needsIntermediate)) + return false; + const QtTestResult *qtOther = static_cast(other); + if (result() == Result::MessageTestCaseStart || result() == Result::MessageIntermediate) { + if (qtOther->isDataTag()) { + if (qtOther->m_function == m_function) { + if (m_dataTag.isEmpty()) { + // avoid adding function's TestCaseEnd to the data tag + *needsIntermediate = qtOther->result() != Result::MessageTestCaseEnd; + return true; + } + return qtOther->m_dataTag == m_dataTag; + } + } else if (qtOther->isTestFunction()) { + return isTestCase() || m_function == qtOther->m_function; + } + } + return false; +} + +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(); +} + +TestResult *QtTestResult::createIntermediateResultFor(const TestResult *other) +{ + QTC_ASSERT(other, return 0); + const QtTestResult *qtOther = static_cast(other); + QtTestResult *intermediate = new QtTestResult(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); + return intermediate; +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/qtest/qttestresult.h b/src/plugins/autotest/qtest/qttestresult.h index 54d1d1055a3..216b6ef3327 100644 --- a/src/plugins/autotest/qtest/qttestresult.h +++ b/src/plugins/autotest/qtest/qttestresult.h @@ -39,7 +39,14 @@ public: void setFunctionName(const QString &functionName) { m_function = functionName; } void setDataTag(const QString &dataTag) { m_dataTag = dataTag; } + bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const override; + bool isIntermediateFor(const TestResult *other) const override; + TestResult *createIntermediateResultFor(const TestResult *other) 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(); } + QString m_function; QString m_dataTag; }; diff --git a/src/plugins/autotest/testresult.cpp b/src/plugins/autotest/testresult.cpp index ef5b8a61f83..93ad46354aa 100644 --- a/src/plugins/autotest/testresult.cpp +++ b/src/plugins/autotest/testresult.cpp @@ -25,6 +25,7 @@ #include "testresult.h" +#include #include namespace Autotest { @@ -158,5 +159,24 @@ QColor TestResult::colorForType(const Result::Type type) } } +bool TestResult::isDirectParentOf(const TestResult *other, bool * /*needsIntermediate*/) const +{ + QTC_ASSERT(other, return false); + return m_name == other->m_name; +} + +bool TestResult::isIntermediateFor(const TestResult *other) const +{ + QTC_ASSERT(other, return false); + return m_name == other->m_name; +} + +TestResult *TestResult::createIntermediateResultFor(const TestResult *other) +{ + QTC_ASSERT(other, return 0); + TestResult *intermediate = new TestResult(other->m_name); + return intermediate; +} + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/testresult.h b/src/plugins/autotest/testresult.h index 4256aaedf8a..034adf51883 100644 --- a/src/plugins/autotest/testresult.h +++ b/src/plugins/autotest/testresult.h @@ -59,6 +59,7 @@ enum Type { MessageTestCaseFail, MessageTestCaseEnd, MessageTestCaseRepetition, + MessageIntermediate, MessageCurrentTest, INTERNAL_MESSAGES_END = MessageCurrentTest, Invalid, @@ -91,6 +92,10 @@ public: static QString resultToString(const Result::Type type); static QColor colorForType(const Result::Type type); + virtual bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const; + virtual bool isIntermediateFor(const TestResult *other) const; + virtual TestResult *createIntermediateResultFor(const TestResult *other); + private: QString m_name; Result::Type m_result = Result::Invalid; diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp index b39f232fb5c..4baa15ba2ae 100644 --- a/src/plugins/autotest/testresultdelegate.cpp +++ b/src/plugins/autotest/testresultdelegate.cpp @@ -31,7 +31,6 @@ #include #include -#include #include #include @@ -49,15 +48,13 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); - // make sure we paint the complete delegate instead of keeping an offset - opt.rect.adjust(-opt.rect.x(), 0, 0, 0); painter->save(); QFontMetrics fm(opt.font); QColor foreground; const QAbstractItemView *view = qobject_cast(opt.widget); - const bool selected = view->selectionModel()->currentIndex() == index; + const bool selected = opt.state & QStyle::State_Selected; if (selected) { painter->setBrush(opt.palette.highlight().color()); @@ -75,11 +72,6 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op const TestResult *testResult = resultFilterModel->testResult(index); QTC_ASSERT(testResult, painter->restore();return); - // draw the indicator by ourself as we paint across it with the delegate - QStyleOptionViewItem indicatorOpt = option; - indicatorOpt.rect = QRect(0, opt.rect.y(), positions.indentation(), opt.rect.height()); - opt.widget->style()->drawPrimitive(QStyle::PE_IndicatorBranch, &indicatorOpt, painter); - QIcon icon = index.data(Qt::DecorationRole).value(); if (!icon.isNull()) painter->drawPixmap(positions.left(), positions.top(), @@ -140,7 +132,6 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo QStyleOptionViewItem opt = option; // make sure opt.rect is initialized correctly - otherwise we might get a width of 0 opt.initFrom(opt.widget); - initStyleOption(&opt, index); const QAbstractItemView *view = qobject_cast(opt.widget); const bool selected = view->selectionModel()->currentIndex() == index; diff --git a/src/plugins/autotest/testresultdelegate.h b/src/plugins/autotest/testresultdelegate.h index 4527a8bdc3f..7d94c76c18d 100644 --- a/src/plugins/autotest/testresultdelegate.h +++ b/src/plugins/autotest/testresultdelegate.h @@ -67,7 +67,14 @@ private: m_typeAreaWidth = QFontMetrics(options.font).width("XXXXXXXX"); m_indentation = options.widget ? options.widget->style()->pixelMetric( QStyle::PM_TreeViewIndentation, &options) : 0; - m_level = filterModel->mapToSource(options.index).parent() == srcModel->rootItem()->index() ? 1 : 2; + + const QModelIndex &rootIndex = srcModel->rootItem()->index(); + QModelIndex parentIndex = filterModel->mapToSource(options.index).parent(); + m_level = 1; + while (rootIndex != parentIndex) { + ++m_level; + parentIndex = parentIndex.parent(); + } int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING; if (m_maxFileLength > flexibleArea / 2) m_realFileLength = flexibleArea / 2; @@ -84,12 +91,10 @@ private: int fontHeight() const { return m_fontHeight; } int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; } int typeAreaWidth() const { return m_typeAreaWidth; } - int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING - + (1 - m_level) * m_indentation; } + int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING; } int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); } int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; } int lineAreaLeft() const { return right() - m_maxLineLength; } - int indentation() const { return m_indentation; } QRect typeArea() const { return QRect(typeAreaLeft(), top(), typeAreaWidth(), m_fontHeight); } diff --git a/src/plugins/autotest/testresultmodel.cpp b/src/plugins/autotest/testresultmodel.cpp index 09865b10605..bb9c13a0e31 100644 --- a/src/plugins/autotest/testresultmodel.cpp +++ b/src/plugins/autotest/testresultmodel.cpp @@ -99,7 +99,8 @@ void TestResultItem::updateDescription(const QString &description) void TestResultItem::updateResult() { - if (m_testResult->result() != Result::MessageTestCaseStart) + if (m_testResult->result() != Result::MessageTestCaseStart + && m_testResult->result() != Result::MessageIntermediate) return; Result::Type newResult = Result::MessageTestCaseSuccess; @@ -110,6 +111,7 @@ void TestResultItem::updateResult() case Result::Fail: case Result::MessageFatal: case Result::UnexpectedPass: + case Result::MessageTestCaseFail: m_testResult->setResult(Result::MessageTestCaseFail); return; case Result::ExpectedFail: @@ -117,6 +119,7 @@ void TestResultItem::updateResult() case Result::Skip: case Result::BlacklistedFail: case Result::BlacklistedPass: + case Result::MessageTestCaseWarn: newResult = Result::MessageTestCaseWarn; break; default: {} @@ -126,6 +129,40 @@ void TestResultItem::updateResult() m_testResult->setResult(newResult); } +void TestResultItem::updateIntermediateChildren() +{ + for (Utils::TreeItem *child : children()) { + TestResultItem *childItem = static_cast(child); + if (childItem->testResult()->result() == Result::MessageIntermediate) + childItem->updateResult(); + } +} + +TestResultItem *TestResultItem::intermediateFor(const TestResultItem *item) const +{ + QTC_ASSERT(item, return nullptr); + const TestResult *otherResult = item->testResult(); + for (int row = childCount() - 1; row >= 0; --row) { + TestResultItem *child = static_cast(childAt(row)); + const TestResult *testResult = child->testResult(); + if (testResult->result() != Result::MessageIntermediate) + continue; + if (testResult->isIntermediateFor(otherResult)) + return child; + } + return 0; +} + +TestResultItem *TestResultItem::createAndAddIntermediateFor(const TestResultItem *child) +{ + TestResultPtr result(m_testResult->createIntermediateResultFor(child->testResult())); + QTC_ASSERT(!result.isNull(), return 0); + result->setResult(Result::MessageIntermediate); + TestResultItem *intermediate = new TestResultItem(result); + appendChild(intermediate); + return intermediate; +} + /********************************* TestResultModel *****************************************/ TestResultModel::TestResultModel(QObject *parent) @@ -135,12 +172,11 @@ TestResultModel::TestResultModel(QObject *parent) void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoExpand) { - const QVector &topLevelItems = rootItem()->children(); - const int lastRow = topLevelItems.size() - 1; + const int lastRow = rootItem()->childCount() - 1; if (testResult->result() == Result::MessageCurrentTest) { // MessageCurrentTest should always be the last top level item if (lastRow >= 0) { - TestResultItem *current = static_cast(topLevelItems.at(lastRow)); + TestResultItem *current = static_cast(rootItem()->childAt(lastRow)); const TestResult *result = current->testResult(); if (result && result->result() == Result::MessageCurrentTest) { current->updateDescription(testResult->description()); @@ -158,34 +194,29 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx m_testResultCount[testResult->result()]++; TestResultItem *newItem = new TestResultItem(testResult); - // FIXME this might be totally wrong... we need some more unique information! - if (!testResult->name().isEmpty()) { - for (int row = lastRow; row >= 0; --row) { - TestResultItem *current = static_cast(topLevelItems.at(row)); + TestResultItem *parentItem = findParentItemFor(newItem); + addFileName(testResult->fileName()); // ensure we calculate the results pane correctly + if (parentItem) { + parentItem->appendChild(newItem); + if (autoExpand) + parentItem->expand(); + if (testResult->result() == Result::MessageTestCaseEnd) { + parentItem->updateIntermediateChildren(); + parentItem->updateResult(); + emit dataChanged(parentItem->index(), parentItem->index()); + } + } else { + if (lastRow >= 0) { + TestResultItem *current = static_cast(rootItem()->childAt(lastRow)); const TestResult *result = current->testResult(); - if (result && result->name() == testResult->name()) { - current->appendChild(newItem); - if (autoExpand) - current->expand(); - if (testResult->result() == Result::MessageTestCaseEnd) { - current->updateResult(); - emit dataChanged(current->index(), current->index()); - } + if (result && result->result() == Result::MessageCurrentTest) { + rootItem()->insertChild(current->index().row(), newItem); return; } } + // there is no MessageCurrentTest at the last row, but we have a toplevel item - just add it + rootItem()->appendChild(newItem); } - // if we have a MessageCurrentTest present, add the new top level item before it - if (lastRow >= 0) { - TestResultItem *current = static_cast(topLevelItems.at(lastRow)); - const TestResult *result = current->testResult(); - if (result && result->result() == Result::MessageCurrentTest) { - rootItem()->insertChild(current->index().row(), newItem); - return; - } - } - // there is no MessageCurrentTest at the last row, but we have a toplevel item - just add it - rootItem()->appendChild(newItem); } void TestResultModel::removeCurrentTestMessage() @@ -205,7 +236,7 @@ void TestResultModel::clearTestResults() clear(); m_testResultCount.clear(); m_disabled = 0; - m_processedIndices.clear(); + m_fileNames.clear(); m_maxWidthOfFileName = 0; m_widthOfLineNumber = 0; } @@ -218,38 +249,28 @@ const TestResult *TestResultModel::testResult(const QModelIndex &idx) return 0; } +void TestResultModel::recalculateMaxWidthOfFileName(const QFont &font) +{ + const QFontMetrics fm(font); + m_maxWidthOfFileName = 0; + for (const QString &fileName : m_fileNames) { + int pos = fileName.lastIndexOf('/'); + m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(fileName.mid(pos + 1))); + } +} + +void TestResultModel::addFileName(const QString &fileName) +{ + const QFontMetrics fm(m_measurementFont); + int pos = fileName.lastIndexOf('/'); + m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(fileName.mid(pos + 1))); + m_fileNames.insert(fileName); +} + int TestResultModel::maxWidthOfFileName(const QFont &font) { - if (font != m_measurementFont) { - m_processedIndices.clear(); - m_maxWidthOfFileName = 0; - m_measurementFont = font; - } - - const QFontMetrics fm(font); - const QVector &topLevelItems = rootItem()->children(); - const int count = topLevelItems.size(); - for (int row = 0; row < count; ++row) { - int processed = row < m_processedIndices.size() ? m_processedIndices.at(row) : 0; - const QVector &children = topLevelItems.at(row)->children(); - const int itemCount = children.size(); - if (processed < itemCount) { - for (int childRow = processed; childRow < itemCount; ++childRow) { - const TestResultItem *item = static_cast(children.at(childRow)); - if (const TestResult *result = item->testResult()) { - QString fileName = result->fileName(); - const int pos = fileName.lastIndexOf('/'); - if (pos != -1) - fileName = fileName.mid(pos + 1); - m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(fileName)); - } - } - if (row < m_processedIndices.size()) - m_processedIndices.replace(row, itemCount); - else - m_processedIndices.insert(row, itemCount); - } - } + if (font != m_measurementFont) + recalculateMaxWidthOfFileName(font); return m_maxWidthOfFileName; } @@ -263,6 +284,44 @@ int TestResultModel::maxWidthOfLineNumber(const QFont &font) return m_widthOfLineNumber; } +TestResultItem *TestResultModel::findParentItemFor(const TestResultItem *item, + const TestResultItem *startItem) const +{ + QTC_ASSERT(item, return nullptr); + TestResultItem *root = startItem ? const_cast(startItem) : nullptr; + const TestResult *result = item->testResult(); + const QString &name = result->name(); + + if (root == nullptr) { + for (int row = rootItem()->childCount() - 1; row >= 0; --row) { + TestResultItem *tmp = static_cast(rootItem()->childAt(row)); + if (tmp->testResult()->name() == name) { + root = tmp; + break; + } + } + } + if (root == nullptr) + return root; + + bool needsIntermediate = false; + auto predicate = [result, &needsIntermediate](Utils::TreeItem *it) { + TestResultItem *currentItem = static_cast(it); + return currentItem->testResult()->isDirectParentOf(result, &needsIntermediate); + }; + TestResultItem *parent = static_cast(root->findAnyChild(predicate)); + if (parent) { + if (needsIntermediate) { + // check if the intermediate is present already + if (TestResultItem *intermediate = parent->intermediateFor(item)) + return intermediate; + return parent->createAndAddIntermediateFor(item); + } + return parent; + } + return root; +} + /********************************** Filter Model **********************************/ TestResultFilterModel::TestResultFilterModel(TestResultModel *sourceModel, QObject *parent) @@ -279,7 +338,7 @@ void TestResultFilterModel::enableAllResultTypes() << Result::UnexpectedPass << Result::Skip << Result::MessageDebug << Result::MessageWarn << Result::MessageInternal << Result::MessageFatal << Result::Invalid << Result::BlacklistedPass - << Result::BlacklistedFail << Result::Benchmark + << Result::BlacklistedFail << Result::Benchmark << Result::MessageIntermediate << Result::MessageCurrentTest << Result::MessageTestCaseStart << Result::MessageTestCaseSuccess << Result::MessageTestCaseWarn << Result::MessageTestCaseFail << Result::MessageTestCaseEnd @@ -337,15 +396,19 @@ bool TestResultFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s } } -bool TestResultFilterModel::acceptTestCaseResult(const QModelIndex &index) const +bool TestResultFilterModel::acceptTestCaseResult(const QModelIndex &srcIndex) const { - Utils::TreeItem *item = m_sourceModel->itemForIndex(index); - QTC_ASSERT(item, return false); - - for (const Utils::TreeItem *child : item->children()) { - const TestResultItem *resultItem = static_cast(child); - if (m_enabled.contains(resultItem->testResult()->result())) + for (int row = 0, count = m_sourceModel->rowCount(srcIndex); row < count; ++row) { + const QModelIndex &child = srcIndex.child(row, 0); + Result::Type type = m_sourceModel->testResult(child)->result(); + if (type == Result::MessageTestCaseSuccess) + type = Result::Pass; + if (type == Result::MessageTestCaseFail || type == Result::MessageTestCaseWarn) { + if (acceptTestCaseResult(child)) + return true; + } else if (m_enabled.contains(type)) { return true; + } } return false; } diff --git a/src/plugins/autotest/testresultmodel.h b/src/plugins/autotest/testresultmodel.h index 31e7187d0b8..56643c8fd99 100644 --- a/src/plugins/autotest/testresultmodel.h +++ b/src/plugins/autotest/testresultmodel.h @@ -46,6 +46,10 @@ public: const TestResult *testResult() const { return m_testResult.data(); } void updateDescription(const QString &description); void updateResult(); + void updateIntermediateChildren(); + + TestResultItem *intermediateFor(const TestResultItem *item) const; + TestResultItem *createAndAddIntermediateFor(const TestResultItem *child); private: TestResultPtr m_testResult; @@ -69,11 +73,15 @@ public: int disabledTests() const { return m_disabled; } private: + void recalculateMaxWidthOfFileName(const QFont &font); + void addFileName(const QString &fileName); + TestResultItem *findParentItemFor(const TestResultItem *item, + const TestResultItem *startItem = 0) const; QMap m_testResultCount; int m_widthOfLineNumber = 0; int m_maxWidthOfFileName = 0; int m_disabled = 0; - QList m_processedIndices; + QSet m_fileNames; // TODO: check whether this caching is necessary at all QFont m_measurementFont; }; @@ -93,7 +101,7 @@ protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; private: - bool acceptTestCaseResult(const QModelIndex &index) const; + bool acceptTestCaseResult(const QModelIndex &srcIndex) const; TestResultModel *m_sourceModel; QSet m_enabled; };