forked from qt-creator/qt-creator
AutoTest: Make results tree a real tree
Provide a way to control how the results tree will be generated for the respective test framework and use this information to construct a real tree. Basically this changes the layout of Qt test results, but keeps the former layout of Google test results. Task-number: QTCREATORBUG-17104 Change-Id: I7fca4d8e365bfebcca4cf7855cf6a882e5379143 Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -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<const GTestResult *>(other);
|
||||
return isTest() && gtOther->isTestSet();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "qttestresult.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
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<const QtTestResult *>(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<const QtTestResult *>(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<const QtTestResult *>(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
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "testresult.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/theme/theme.h>
|
||||
|
||||
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
|
||||
|
@@ -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;
|
||||
|
@@ -31,7 +31,6 @@
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QTextLayout>
|
||||
|
||||
@@ -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<const QAbstractItemView *>(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<QIcon>();
|
||||
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<const QAbstractItemView *>(opt.widget);
|
||||
const bool selected = view->selectionModel()->currentIndex() == index;
|
||||
|
@@ -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); }
|
||||
|
@@ -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<TestResultItem *>(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<TestResultItem *>(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<Utils::TreeItem *> &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<TestResultItem *>(topLevelItems.at(lastRow));
|
||||
TestResultItem *current = static_cast<TestResultItem *>(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<TestResultItem *>(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<TestResultItem *>(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<TestResultItem *>(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<Utils::TreeItem *> &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<Utils::TreeItem *> &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<TestResultItem *>(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<TestResultItem *>(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<TestResultItem *>(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<TestResultItem *>(it);
|
||||
return currentItem->testResult()->isDirectParentOf(result, &needsIntermediate);
|
||||
};
|
||||
TestResultItem *parent = static_cast<TestResultItem *>(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<const TestResultItem *>(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;
|
||||
}
|
||||
|
@@ -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<Result::Type, int> m_testResultCount;
|
||||
int m_widthOfLineNumber = 0;
|
||||
int m_maxWidthOfFileName = 0;
|
||||
int m_disabled = 0;
|
||||
QList<int> m_processedIndices;
|
||||
QSet<QString> 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<Result::Type> m_enabled;
|
||||
};
|
||||
|
Reference in New Issue
Block a user